Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12f0b20e5f |
@@ -32,6 +32,12 @@ title-bar controls). The rules that matter most:
|
||||
and bump the module's `version` on every change.
|
||||
- Browsers never call upstream services directly; the module's server routes
|
||||
proxy everything (no CORS, secrets stay server-side).
|
||||
- **Keep the interface clean** (guide rules 5-11): no `help` and no label
|
||||
over three words in a tile's gear; server-side settings in `configSchema`
|
||||
only; a tile setting changes that tile alone; hide-toggles ungrouped when
|
||||
there are four or fewer, in a folded **Show** group when there are five or
|
||||
more; title-bar controls are icons with tooltips, never labels, and never
|
||||
repeat something the gear already offers.
|
||||
- Style with the shell's CSS variables only, scoped under
|
||||
`[data-module="<id>"]`; survive upstream loss (reconnect + `setStatus`).
|
||||
- Verify against the guide's testing section and resilience checklist. Mocks
|
||||
|
||||
+62
-16
@@ -34,6 +34,46 @@ people running the booth depend on them.
|
||||
The admin page refuses to install a module whose requirement the running
|
||||
shell doesn't meet, and it offers updates by comparing versions.
|
||||
|
||||
### Keeping the interface clean
|
||||
|
||||
A dashboard is read across a booth, at a glance, by someone doing three
|
||||
other things. These rules are what keep every tile looking like one product
|
||||
instead of six. They are as binding as the four above.
|
||||
|
||||
5. **No explanatory text in a tile's gear.** No `help` on an
|
||||
`instanceSchema` field or an `instanceGroups` entry. Labels are **three
|
||||
words at most, one or two preferred** — "Slide count", "Item tags",
|
||||
"Running clock". A control that cannot be understood in three words is
|
||||
not a display choice; it belongs in `configSchema`, where `help` is
|
||||
welcome and expected. *One exception:* a `template` field may keep a
|
||||
single `help` line naming its placeholders, which nobody can guess.
|
||||
6. **Server-side settings are admin-only.** Anything the module's server
|
||||
acts on — a host, a credential, what to fetch, how often to poll — is
|
||||
`configSchema`. `instanceSchema` is display choices and nothing else:
|
||||
what this tile shows, and how big.
|
||||
7. **A tile setting changes that tile and nothing else.** Never another
|
||||
tile, never the module as a whole, never the server. If two tiles must
|
||||
agree on something, it is either admin config or page state the shell
|
||||
owns — not a setting each tile keeps its own copy of.
|
||||
8. **Offer toggles for the elements you draw.** Every tile should let the
|
||||
operator switch off the parts it does not need. **One to four toggles:**
|
||||
give them no `group` at all, so they sit unlabelled at the top of the
|
||||
gear. **Five or more:** one group named **Show**, `collapsed: true`,
|
||||
`columns: 2`. Nothing else goes in that group — colours, formats and
|
||||
text belong in groups of their own.
|
||||
9. **Title-bar controls are icons.** No `label` on `addButton` or
|
||||
`addMenu`; the tooltip carries the words, and `docs` above covers drawing
|
||||
the icon. A small state indicator may ride alongside an icon — ProdCom's
|
||||
channel button shows a count while a filter is on — but a control is
|
||||
never named in the bar.
|
||||
10. **If a control needs words to explain it, it is not a title-bar
|
||||
control.** Put it in the gear. The bar is for actions whose icon and
|
||||
tooltip say everything: refresh, clear, jump to latest, bigger text.
|
||||
11. **One control per action.** Whatever has a dedicated title-bar button is
|
||||
not offered again in the gear, and the reverse. Text size is A− / A+ up
|
||||
there, never a "Text size" field; a timestamps button in the bar means
|
||||
no timestamps switch below it.
|
||||
|
||||
## The shape of a module
|
||||
|
||||
A module is one folder dropped into `modules/`:
|
||||
@@ -177,14 +217,18 @@ the group name:
|
||||
|
||||
`help` on a field puts one line of guidance under its control — allowed
|
||||
placeholders, where a value comes from. Keep labels short and put the
|
||||
explanation there.
|
||||
explanation there. **This is for `configSchema` only**: a tile's gear
|
||||
carries no explanations at all (rule 5), so a setting that needs one is
|
||||
telling you it belongs in admin.
|
||||
|
||||
`group` and `help` work on `instanceSchema` fields too, and a manifest
|
||||
`group` works on `instanceSchema` fields too, and a manifest
|
||||
`instanceGroups` map plays the `configGroups` role for the tile's gear
|
||||
popover (`collapsed: true | false`, `columns`, `help`, `toggle`). That is how
|
||||
a module with a long show/hide checklist keeps the popover usable — see
|
||||
`pco-plan`. Display choices belong there, per tile; admin config is for
|
||||
connections. The popover itself is a small window beside the tile — sized to
|
||||
popover (`collapsed: true | false`, `columns`, `toggle` — but not `help`,
|
||||
per rule 5). That is how a module with a long show/hide checklist keeps the
|
||||
popover usable: five or more hide-toggles go in one **Show** group, folded
|
||||
by default, and fewer than five go in no group at all — see `pco-plan` for
|
||||
the long form and `slack` for the short one. Display choices belong there,
|
||||
per tile; admin config is for connections. The popover itself is a small window beside the tile — sized to
|
||||
its content, moved by the slim bar on top, closed by clicking anywhere else
|
||||
or pressing Escape — so a tile never has to be big enough to hold its own
|
||||
settings, and a change can be watched as it lands. Under everything you
|
||||
@@ -354,17 +398,19 @@ menu.setLabel('Channels (2/4) ▾'); // update the button text any time
|
||||
```
|
||||
|
||||
Everything you add is removed automatically when your instance stops, so a
|
||||
remount never duplicates controls. Keep them compact (icon buttons, short
|
||||
labels): the title bar is 32 px tall and shared with the tile's name. Draw
|
||||
icons as inline SVG rather than characters — a glyph such as ✕ or + takes
|
||||
its size from whatever font each browser picks; text-size buttons are a
|
||||
drawn − and +.
|
||||
remount never duplicates controls. **Icons only, no labels** (rule 9): the
|
||||
title bar is 32 px tall and shared with the tile's name, and the tooltip
|
||||
says what the icon means. Draw icons as inline SVG rather than characters —
|
||||
a glyph such as ✕ or + takes its size from whatever font each browser
|
||||
picks; text-size buttons are a drawn − and +. A small state indicator may
|
||||
ride alongside an icon (ProdCom's channel button carries a count while a
|
||||
filter is on); a name may not.
|
||||
|
||||
**One control per action.** Whatever has a dedicated header button is not
|
||||
offered again in the gear popover: text size is A− / A+ up here (persisted
|
||||
through `saveInstanceSettings({ textSize })`, read back with a sane
|
||||
fallback), never a "Text size" field in `instanceSchema`; a timestamps or
|
||||
newest-first toggle in the header means no such switch in the gear. The
|
||||
**One control per action** (rule 11). Whatever has a dedicated header button
|
||||
is not offered again in the gear popover: text size is − / + up here
|
||||
(persisted through `saveInstanceSettings({ textSize })`, read back with a
|
||||
sane fallback), never a "Text size" field in `instanceSchema`; a timestamps
|
||||
or newest-first toggle in the header means no such switch in the gear. The
|
||||
gear is for display choices that have no button of their own.
|
||||
|
||||
One more thing to know: the whole title bar folds away when the dashboard
|
||||
|
||||
+245
-49
@@ -1,15 +1,23 @@
|
||||
{
|
||||
"id": "pco-plan",
|
||||
"name": "PCO Plan",
|
||||
"version": "1.4.0",
|
||||
"version": "1.5.0",
|
||||
"proddash": ">=1.4.0",
|
||||
"description": "Order of service from Planning Center Services",
|
||||
"provides": ["timers"],
|
||||
"provides": [
|
||||
"timers"
|
||||
],
|
||||
"client": "client.js",
|
||||
"server": "server.js",
|
||||
"style": "style.css",
|
||||
"minSize": { "w": 3, "h": 3 },
|
||||
"defaultSize": { "w": 5, "h": 6 },
|
||||
"minSize": {
|
||||
"w": 3,
|
||||
"h": 3
|
||||
},
|
||||
"defaultSize": {
|
||||
"w": 5,
|
||||
"h": 6
|
||||
},
|
||||
"configGroups": {
|
||||
"Planning Center credentials": {
|
||||
"collapsed": "whenSet",
|
||||
@@ -48,7 +56,12 @@
|
||||
"type": "select",
|
||||
"label": "Plan",
|
||||
"default": "",
|
||||
"options": [{ "value": "", "label": "Automatic — today's plan, else the next upcoming" }],
|
||||
"options": [
|
||||
{
|
||||
"value": "",
|
||||
"label": "Automatic — today's plan, else the next upcoming"
|
||||
}
|
||||
],
|
||||
"optionsRoute": "/plans",
|
||||
"optionsDependsOn": "serviceTypeId"
|
||||
},
|
||||
@@ -82,8 +95,14 @@
|
||||
"group": "Service timing",
|
||||
"showWhen": "useDocumentTriggers",
|
||||
"options": [
|
||||
{ "value": "schedule", "label": "Scheduled service time" },
|
||||
{ "value": "document", "label": "ProPresenter document" }
|
||||
{
|
||||
"value": "schedule",
|
||||
"label": "Scheduled service time"
|
||||
},
|
||||
{
|
||||
"value": "document",
|
||||
"label": "ProPresenter document"
|
||||
}
|
||||
]
|
||||
},
|
||||
"serviceStartDocument": {
|
||||
@@ -124,37 +143,94 @@
|
||||
"Item notes": {
|
||||
"toggle": "showNotes"
|
||||
},
|
||||
"Show / hide": {
|
||||
"Show": {
|
||||
"collapsed": true,
|
||||
"columns": 3,
|
||||
"help": "Switch off anything this tile should not show."
|
||||
"columns": 2
|
||||
}
|
||||
},
|
||||
"instanceSchema": {
|
||||
"autoScroll": { "type": "boolean", "label": "Keep the current item in view", "default": true },
|
||||
"autoScroll": {
|
||||
"type": "boolean",
|
||||
"label": "Auto-scroll",
|
||||
"default": true
|
||||
},
|
||||
"headerTemplate": {
|
||||
"type": "template",
|
||||
"label": "Top bar text",
|
||||
"default": "{series} • {part} | {serviceType} • {date}",
|
||||
"group": "Top bar",
|
||||
"variables": [
|
||||
{ "value": "{series}", "label": "Series" },
|
||||
{ "value": "{part}", "label": "Part / plan title" },
|
||||
{ "value": "{serviceType}", "label": "Service type" },
|
||||
{ "value": "{date}", "label": "Date" },
|
||||
{ "value": "{shortDate}", "label": "Short date" },
|
||||
{ "value": "{time}", "label": "Service time" },
|
||||
{ "value": "{length}", "label": "Planned length" },
|
||||
{ "value": "{items}", "label": "Item count" }
|
||||
{
|
||||
"value": "{series}",
|
||||
"label": "Series"
|
||||
},
|
||||
{
|
||||
"value": "{part}",
|
||||
"label": "Part / plan title"
|
||||
},
|
||||
{
|
||||
"value": "{serviceType}",
|
||||
"label": "Service type"
|
||||
},
|
||||
{
|
||||
"value": "{date}",
|
||||
"label": "Date"
|
||||
},
|
||||
{
|
||||
"value": "{shortDate}",
|
||||
"label": "Short date"
|
||||
},
|
||||
{
|
||||
"value": "{time}",
|
||||
"label": "Service time"
|
||||
},
|
||||
{
|
||||
"value": "{length}",
|
||||
"label": "Planned length"
|
||||
},
|
||||
{
|
||||
"value": "{items}",
|
||||
"label": "Item count"
|
||||
}
|
||||
],
|
||||
"help": "Use • between items and | to start a second line."
|
||||
},
|
||||
"keyColor": { "type": "color", "label": "Song key", "default": "#4ea1ff", "group": "Colors" },
|
||||
"headerColor": { "type": "color", "label": "Section headers", "default": "#2ee59a", "group": "Colors" },
|
||||
"currentColor": { "type": "color", "label": "Current item", "default": "#2ee59a", "group": "Colors" },
|
||||
"headerGradient": { "type": "boolean", "label": "Fade header color to the right", "default": true, "group": "Colors" },
|
||||
"tintByType": { "type": "boolean", "label": "Tint items by type (song / media / other)", "default": false, "group": "Colors" },
|
||||
"showNotes": { "type": "switch", "label": "Item notes", "default": true, "group": "Item notes" },
|
||||
"keyColor": {
|
||||
"type": "color",
|
||||
"label": "Song key",
|
||||
"default": "#4ea1ff",
|
||||
"group": "Colors"
|
||||
},
|
||||
"headerColor": {
|
||||
"type": "color",
|
||||
"label": "Section headers",
|
||||
"default": "#2ee59a",
|
||||
"group": "Colors"
|
||||
},
|
||||
"currentColor": {
|
||||
"type": "color",
|
||||
"label": "Current item",
|
||||
"default": "#2ee59a",
|
||||
"group": "Colors"
|
||||
},
|
||||
"headerGradient": {
|
||||
"type": "boolean",
|
||||
"label": "Header fade",
|
||||
"default": true,
|
||||
"group": "Colors"
|
||||
},
|
||||
"tintByType": {
|
||||
"type": "boolean",
|
||||
"label": "Tint by type",
|
||||
"default": false,
|
||||
"group": "Colors"
|
||||
},
|
||||
"showNotes": {
|
||||
"type": "switch",
|
||||
"label": "Item notes",
|
||||
"default": true,
|
||||
"group": "Item notes"
|
||||
},
|
||||
"noteCategories": {
|
||||
"type": "multiselect",
|
||||
"label": "Categories",
|
||||
@@ -163,29 +239,149 @@
|
||||
"columns": 3,
|
||||
"allByDefault": true
|
||||
},
|
||||
"showTopBar": { "type": "boolean", "label": "Top bar", "default": true, "group": "Show / hide" },
|
||||
"showSeries": { "type": "boolean", "label": "Series name", "default": true, "group": "Show / hide" },
|
||||
"showPart": { "type": "boolean", "label": "Part # / plan title", "default": true, "group": "Show / hide" },
|
||||
"showServiceType": { "type": "boolean", "label": "Service type", "default": true, "group": "Show / hide" },
|
||||
"showDate": { "type": "boolean", "label": "Plan date", "default": true, "group": "Show / hide" },
|
||||
"showServiceTime": { "type": "boolean", "label": "Service time", "default": true, "group": "Show / hide" },
|
||||
"showTotalLength": { "type": "boolean", "label": "Planned total length", "default": true, "group": "Show / hide" },
|
||||
"showRunningClock": { "type": "boolean", "label": "Running clock (ahead / behind)", "default": true, "group": "Show / hide" },
|
||||
"showCountdown": { "type": "boolean", "label": "Countdown to service start", "default": true, "group": "Show / hide" },
|
||||
"showHeaders": { "type": "boolean", "label": "Section headers", "default": true, "group": "Show / hide" },
|
||||
"showItemNumbers": { "type": "boolean", "label": "Item numbers", "default": false, "group": "Show / hide" },
|
||||
"showStartTimes": { "type": "boolean", "label": "Scheduled start times", "default": true, "group": "Show / hide" },
|
||||
"showLength": { "type": "boolean", "label": "Item length", "default": true, "group": "Show / hide" },
|
||||
"showItemType": { "type": "boolean", "label": "Item type badge", "default": true, "group": "Show / hide" },
|
||||
"showKey": { "type": "boolean", "label": "Song key", "default": true, "group": "Show / hide" },
|
||||
"showArrangement": { "type": "boolean", "label": "Song arrangement", "default": false, "group": "Show / hide" },
|
||||
"showSongAuthor": { "type": "boolean", "label": "Song author / CCLI", "default": false, "group": "Show / hide" },
|
||||
"showDescription": { "type": "boolean", "label": "Item description", "default": true, "group": "Show / hide" },
|
||||
"showPreService": { "type": "boolean", "label": "Pre-service items", "default": true, "group": "Show / hide" },
|
||||
"showPostService": { "type": "boolean", "label": "Post-service items", "default": true, "group": "Show / hide" },
|
||||
"showNowIndicator": { "type": "boolean", "label": "Now indicator", "default": true, "group": "Show / hide" },
|
||||
"showItemRuntime": { "type": "boolean", "label": "Item runtime (elapsed vs planned)", "default": true, "group": "Show / hide" },
|
||||
"dimCompleted": { "type": "boolean", "label": "Dim completed items", "default": true, "group": "Show / hide" },
|
||||
"showLiveFooter": { "type": "boolean", "label": "ProPresenter match line", "default": true, "group": "Show / hide" }
|
||||
"showTopBar": {
|
||||
"type": "boolean",
|
||||
"label": "Top bar",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showSeries": {
|
||||
"type": "boolean",
|
||||
"label": "Series name",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showPart": {
|
||||
"type": "boolean",
|
||||
"label": "Part / title",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showServiceType": {
|
||||
"type": "boolean",
|
||||
"label": "Service type",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showDate": {
|
||||
"type": "boolean",
|
||||
"label": "Plan date",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showServiceTime": {
|
||||
"type": "boolean",
|
||||
"label": "Service time",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showTotalLength": {
|
||||
"type": "boolean",
|
||||
"label": "Total length",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showRunningClock": {
|
||||
"type": "boolean",
|
||||
"label": "Running clock",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showCountdown": {
|
||||
"type": "boolean",
|
||||
"label": "Countdown",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showHeaders": {
|
||||
"type": "boolean",
|
||||
"label": "Section headers",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showItemNumbers": {
|
||||
"type": "boolean",
|
||||
"label": "Item numbers",
|
||||
"default": false,
|
||||
"group": "Show"
|
||||
},
|
||||
"showStartTimes": {
|
||||
"type": "boolean",
|
||||
"label": "Start times",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showLength": {
|
||||
"type": "boolean",
|
||||
"label": "Item length",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showItemType": {
|
||||
"type": "boolean",
|
||||
"label": "Type badge",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showKey": {
|
||||
"type": "boolean",
|
||||
"label": "Song key",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showArrangement": {
|
||||
"type": "boolean",
|
||||
"label": "Arrangement",
|
||||
"default": false,
|
||||
"group": "Show"
|
||||
},
|
||||
"showSongAuthor": {
|
||||
"type": "boolean",
|
||||
"label": "Author / CCLI",
|
||||
"default": false,
|
||||
"group": "Show"
|
||||
},
|
||||
"showDescription": {
|
||||
"type": "boolean",
|
||||
"label": "Item description",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showPreService": {
|
||||
"type": "boolean",
|
||||
"label": "Pre-service",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showPostService": {
|
||||
"type": "boolean",
|
||||
"label": "Post-service",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showNowIndicator": {
|
||||
"type": "boolean",
|
||||
"label": "Now indicator",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showItemRuntime": {
|
||||
"type": "boolean",
|
||||
"label": "Item runtime",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"dimCompleted": {
|
||||
"type": "boolean",
|
||||
"label": "Dim completed",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showLiveFooter": {
|
||||
"type": "boolean",
|
||||
"label": "Match line",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "prodcom-transcript",
|
||||
"name": "ProdCom",
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.2",
|
||||
"proddash": ">=1.1.0",
|
||||
"description": "Live ProdCom transcript",
|
||||
"permissions": [
|
||||
@@ -40,7 +40,7 @@
|
||||
"instanceSchema": {
|
||||
"showAvatars": {
|
||||
"type": "boolean",
|
||||
"label": "Show channel icons",
|
||||
"label": "Channel icons",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,34 @@
|
||||
{
|
||||
"id": "propresenter-now-next",
|
||||
"name": "ProPresenter",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"proddash": ">=1.5.0",
|
||||
"description": "What is live in ProPresenter and what comes next",
|
||||
"permissions": [
|
||||
{ "kind": "local-network", "reason": "Reaching ProPresenter on this network" }
|
||||
{
|
||||
"kind": "local-network",
|
||||
"reason": "Reaching ProPresenter on this network"
|
||||
}
|
||||
],
|
||||
"client": "client.js",
|
||||
"server": "server.js",
|
||||
"style": "style.css",
|
||||
"minSize": { "w": 3, "h": 3 },
|
||||
"defaultSize": { "w": 4, "h": 5 },
|
||||
"minSize": {
|
||||
"w": 3,
|
||||
"h": 3
|
||||
},
|
||||
"defaultSize": {
|
||||
"w": 4,
|
||||
"h": 5
|
||||
},
|
||||
"configSchema": {
|
||||
"propresenter": {
|
||||
"type": "endpoint",
|
||||
"label": "ProPresenter",
|
||||
"default": { "host": "", "port": 1025 }
|
||||
"default": {
|
||||
"host": "",
|
||||
"port": 1025
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"type": "password",
|
||||
@@ -25,17 +37,64 @@
|
||||
}
|
||||
},
|
||||
"instanceSchema": {
|
||||
"cleanTitles": { "type": "switch", "label": "Tidy titles", "default": true },
|
||||
"showPlaylist": { "type": "switch", "label": "Playlist", "default": true, "group": "Show" },
|
||||
"showLive": { "type": "switch", "label": "Live status", "default": true, "group": "Show" },
|
||||
"showNowLabel": { "type": "switch", "label": "Now label", "default": true, "group": "Show" },
|
||||
"showSlideCount": { "type": "switch", "label": "Slide count", "default": true, "group": "Show" },
|
||||
"showProgress": { "type": "switch", "label": "Progress bar", "default": true, "group": "Show" },
|
||||
"showNext": { "type": "switch", "label": "Next list", "default": true, "group": "Show" },
|
||||
"showNextLabel": { "type": "switch", "label": "Next label", "default": true, "group": "Show" },
|
||||
"showTags": { "type": "switch", "label": "Item tags", "default": true, "group": "Show" }
|
||||
"cleanTitles": {
|
||||
"type": "switch",
|
||||
"label": "Tidy titles",
|
||||
"default": true
|
||||
},
|
||||
"showPlaylist": {
|
||||
"type": "switch",
|
||||
"label": "Playlist",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showLive": {
|
||||
"type": "switch",
|
||||
"label": "Live status",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showNowLabel": {
|
||||
"type": "switch",
|
||||
"label": "Now label",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showSlideCount": {
|
||||
"type": "switch",
|
||||
"label": "Slide count",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showProgress": {
|
||||
"type": "switch",
|
||||
"label": "Progress bar",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showNext": {
|
||||
"type": "switch",
|
||||
"label": "Next list",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showNextLabel": {
|
||||
"type": "switch",
|
||||
"label": "Next label",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
},
|
||||
"showTags": {
|
||||
"type": "switch",
|
||||
"label": "Item tags",
|
||||
"default": true,
|
||||
"group": "Show"
|
||||
}
|
||||
},
|
||||
"instanceGroups": {
|
||||
"Show": { "collapsed": false, "columns": 2 }
|
||||
"Show": {
|
||||
"collapsed": true,
|
||||
"columns": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ module.exports = {
|
||||
showSlideCount: { type: 'switch', label: 'Slide count', default: true, group: 'Show' },
|
||||
showProgress: { type: 'switch', label: 'Progress bar', default: true, group: 'Show' },
|
||||
},
|
||||
instanceGroups: { Show: { collapsed: false, columns: 2 } },
|
||||
instanceGroups: { Show: { collapsed: true, columns: 2 } },
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
Per-tile choices: the gear popover's static switches (whole sources,
|
||||
headings, status lines, the clock's format, the state colours) and the
|
||||
"Timers ▾" header menu, which hides any single timer — keyed `source:id`
|
||||
timers header menu, which hides any single timer — keyed `source:id`
|
||||
and saved with the layout via saveInstanceSettings. The header can be
|
||||
hidden by the user, so the static switches stay the primary controls.
|
||||
A solo tile's gear carries only the settings its one card can use (the
|
||||
@@ -196,6 +196,10 @@ function defaultStatus(timer, value, tone) {
|
||||
|
||||
/* ── the tile ─────────────────────────────────────────────────────────── */
|
||||
|
||||
/* The header menu's icon: a stopwatch, for the list of timers behind it.
|
||||
Title-bar controls carry no words (module guide, rule 9). */
|
||||
const TIMERS_SVG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 2h4"/><path d="M12 14V9"/><circle cx="12" cy="14" r="8"/></svg>';
|
||||
|
||||
export default function create({ root, moduleApi }) {
|
||||
let state = null;
|
||||
let feedOffline = false;
|
||||
@@ -227,7 +231,7 @@ export default function create({ root, moduleApi }) {
|
||||
}
|
||||
|
||||
/* Which picker entry this tile was added as (the module's tiles() list):
|
||||
'' or 'all' = every timer, selectable via the Timers ▾ menu;
|
||||
'' or 'all' = every timer, selectable via the timers menu;
|
||||
'solo:<source>:<id>' = exactly one card, no menu, no headings. The
|
||||
earlier entries 'timer:<uuid>' and 'ltc' map onto the new keys. */
|
||||
const variant = String(moduleApi.variant || '');
|
||||
@@ -283,8 +287,8 @@ export default function create({ root, moduleApi }) {
|
||||
|
||||
let menuEl = null; // the open menu's element (filled on each open)
|
||||
const itemsMenu = solo ? null : moduleApi.header.addMenu({
|
||||
label: 'Timers ▾',
|
||||
title: 'Choose timers',
|
||||
icon: TIMERS_SVG,
|
||||
title: 'Timers',
|
||||
build(menu) {
|
||||
// the shell hands over an emptied element on every open
|
||||
menu.classList.add('tm-menu');
|
||||
@@ -292,6 +296,12 @@ export default function create({ root, moduleApi }) {
|
||||
rebuildMenu(true);
|
||||
},
|
||||
});
|
||||
// The icon carries the name; a count rides alongside it while some timers
|
||||
// are hidden, so the bar says "filtered" without saying it in words.
|
||||
const itemsCountEl = document.createElement('span');
|
||||
itemsCountEl.className = 'tm-count';
|
||||
itemsCountEl.hidden = true;
|
||||
if (itemsMenu) itemsMenu.button.appendChild(itemsCountEl);
|
||||
|
||||
function menuRows() {
|
||||
const rows = [];
|
||||
@@ -319,7 +329,9 @@ export default function create({ root, moduleApi }) {
|
||||
if (!itemsMenu) return;
|
||||
const rows = menuRows();
|
||||
const visible = rows.filter((r) => !hidden.has(r.key)).length;
|
||||
itemsMenu.setLabel(visible === rows.length ? 'Timers ▾' : `Timers (${visible}/${rows.length}) ▾`);
|
||||
const filtering = visible !== rows.length;
|
||||
itemsCountEl.textContent = filtering ? `${visible}/${rows.length}` : '';
|
||||
itemsCountEl.hidden = !filtering;
|
||||
}
|
||||
|
||||
let menuSignature = '';
|
||||
@@ -779,7 +791,7 @@ export default function create({ root, moduleApi }) {
|
||||
let hint = '';
|
||||
const groups = visibleGroups();
|
||||
if (state && !state.sources.length) hint = 'Every source is switched off — turn one on in Admin.';
|
||||
else if (state && !groups.length) hint = 'Nothing selected — switch a source on in this tile’s settings, or pick timers in the Timers ▾ menu.';
|
||||
else if (state && !groups.length) hint = 'Nothing selected — switch a source on in this tile’s settings, or pick timers in the title bar’s timers menu.';
|
||||
hintEl.hidden = !hint;
|
||||
hintEl.textContent = hint;
|
||||
groupsEl.hidden = Boolean(hint);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "propresenter-timers",
|
||||
"name": "Timers",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"proddash": ">=1.4.0",
|
||||
"description": "Timers, Clocks, and LTC From configured Modules",
|
||||
"provides": [
|
||||
@@ -112,7 +112,7 @@
|
||||
},
|
||||
"instanceGroups": {
|
||||
"Show": {
|
||||
"collapsed": false,
|
||||
"collapsed": true,
|
||||
"columns": 2
|
||||
},
|
||||
"Clock": {
|
||||
@@ -121,8 +121,7 @@
|
||||
},
|
||||
"Colours": {
|
||||
"collapsed": true,
|
||||
"columns": 2,
|
||||
"help": "The time's colour per state. Left at the default, a colour follows the dashboard theme; one you change here wins over it."
|
||||
"columns": 2
|
||||
}
|
||||
},
|
||||
"instanceSchema": {
|
||||
|
||||
@@ -321,7 +321,7 @@
|
||||
|
||||
@keyframes tm-nosignal-blink { 50% { opacity: 0.35; } }
|
||||
|
||||
/* ── the "Timers ▾" menu (container styled/positioned by the shell) ── */
|
||||
/* ── the timers menu (container styled/positioned by the shell) ── */
|
||||
|
||||
[data-module="propresenter-timers"] .tm-menu-item {
|
||||
display: flex;
|
||||
@@ -383,3 +383,11 @@
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* the count beside the title-bar timers icon, while some are hidden */
|
||||
[data-module="propresenter-timers"] .tm-count {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
[data-module="propresenter-timers"] .tm-count[hidden] { display: none; }
|
||||
|
||||
Reference in New Issue
Block a user