@things-factory/apptool-ui 10.1.27 → 10.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/index.d.ts +17 -2
- package/dist-client/index.js +31 -17
- package/dist-client/index.js.map +1 -1
- package/dist-client/layout/app-header-band.d.ts +54 -0
- package/dist-client/layout/app-header-band.js +287 -0
- package/dist-client/layout/app-header-band.js.map +1 -0
- package/dist-client/layout/header-band-model.d.ts +86 -0
- package/dist-client/layout/header-band-model.js +93 -0
- package/dist-client/layout/header-band-model.js.map +1 -0
- package/dist-client/layout/header-scope.d.ts +12 -0
- package/dist-client/layout/header-scope.js +33 -0
- package/dist-client/layout/header-scope.js.map +1 -0
- package/dist-client/mdibar-setting-let.d.ts +3 -1
- package/dist-client/mdibar-setting-let.js +17 -3
- package/dist-client/mdibar-setting-let.js.map +1 -1
- package/dist-client/mdibar-setting-state.d.ts +29 -0
- package/dist-client/mdibar-setting-state.js +35 -0
- package/dist-client/mdibar-setting-state.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/tests/header-band.test.ts +182 -0
- package/tests/mdibar-setting-state.test.ts +78 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the shared header band decides, kept apart from how it draws.
|
|
3
|
+
*
|
|
4
|
+
* ADR-0060 decision 4 and its 2026-09-18 addendum. plant, twin and figure each wrote this row for
|
|
5
|
+
* themselves - 386, 603 and 185 lines - and the three drifted: one drew a menu button and another
|
|
6
|
+
* did not, one put the unread count inside the pill and another hung it off the corner, each
|
|
7
|
+
* reached for a different set of colour tokens. The band is one copy of the row; this file is the
|
|
8
|
+
* part of it a test in this repository can reach, since the rendering needs a DOM.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The five places, in the order they are drawn, left to right.
|
|
12
|
+
*
|
|
13
|
+
* These are `TOOL_POSITION`'s own values, written out rather than imported: `@operato/layout` is an
|
|
14
|
+
* ES module and this repository's test runner does not transform node_modules, so importing the
|
|
15
|
+
* enum here would take the deciding out of reach of every test. `tests/header-band.test.ts` reads
|
|
16
|
+
* the installed declaration and checks that these five still match it.
|
|
17
|
+
*/
|
|
18
|
+
export const SLOT_ORDER = ['FRONT_END', 'FRONT', 'CENTER', 'REAR', 'REAR_END'];
|
|
19
|
+
/**
|
|
20
|
+
* Split the one registry into the five slots.
|
|
21
|
+
*
|
|
22
|
+
* The band and `app-toolbar` read the same `store.getState().apptool.tools`, so lite-menu's
|
|
23
|
+
* hamburger and more-ui's morenda button land in the band without either package knowing the band
|
|
24
|
+
* exists. A second registry would mean every contributor choosing a side.
|
|
25
|
+
*/
|
|
26
|
+
export function toolsBySlot(tools = []) {
|
|
27
|
+
const bySlot = {};
|
|
28
|
+
for (const slot of SLOT_ORDER) {
|
|
29
|
+
bySlot[slot] = (tools || []).filter(tool => tool?.position === slot);
|
|
30
|
+
}
|
|
31
|
+
return bySlot;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether the band draws its own way into the menu.
|
|
35
|
+
*
|
|
36
|
+
* lite-menu hides the rail on a narrow viewport and registers its hamburger in the **app toolbar**
|
|
37
|
+
* (`TOOL_POSITION.FRONT_END`), which an app running the band has turned off. twin turned it off and
|
|
38
|
+
* drew no button, so on a narrow viewport there was no way to leave the screen you were on
|
|
39
|
+
* (2026-09-18). With the rail on screen a second way in is noise, so the button comes and goes with
|
|
40
|
+
* it.
|
|
41
|
+
*
|
|
42
|
+
* `undefined` means the menu part has not registered yet. That reads as shown, so the button does
|
|
43
|
+
* not flash on during boot.
|
|
44
|
+
*/
|
|
45
|
+
export function menuButtonShown(menuPartShow, frontEndTools = []) {
|
|
46
|
+
/* Something already registered a way in - lite-menu's own hamburger, or the app's. Don't draw a second. */
|
|
47
|
+
if (frontEndTools.length > 0)
|
|
48
|
+
return false;
|
|
49
|
+
return menuPartShow === undefined ? false : !menuPartShow;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* How much of a scope control there is to draw.
|
|
53
|
+
*
|
|
54
|
+
* A single factory draws its name and no picker: an install with one of something pays nothing for
|
|
55
|
+
* the ability to switch between several. plant already worked this way and the band keeps it.
|
|
56
|
+
*/
|
|
57
|
+
export function scopeShape(scope) {
|
|
58
|
+
if (!scope)
|
|
59
|
+
return 'none';
|
|
60
|
+
const items = scope.items || [];
|
|
61
|
+
if (items.length > 1)
|
|
62
|
+
return 'picker';
|
|
63
|
+
if (items.length === 1 || scope.selectedId)
|
|
64
|
+
return 'name';
|
|
65
|
+
return 'none';
|
|
66
|
+
}
|
|
67
|
+
/** The name to draw for a scope that has no picker. */
|
|
68
|
+
export function scopeName(scope) {
|
|
69
|
+
if (!scope)
|
|
70
|
+
return '';
|
|
71
|
+
const items = scope.items || [];
|
|
72
|
+
const selected = items.find(item => item.id === scope.selectedId) || items[0];
|
|
73
|
+
return selected?.label || '';
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The product name, split where it is drawn in two weights: `operato` **plant**.
|
|
77
|
+
*
|
|
78
|
+
* The band owns this shape, and the app gives it one word (ruling ① of the addendum). plant and
|
|
79
|
+
* figure had written the same two-part wordmark by hand and twin had none at all.
|
|
80
|
+
*/
|
|
81
|
+
export function wordmark(product) {
|
|
82
|
+
const name = (product || '').trim();
|
|
83
|
+
if (!name)
|
|
84
|
+
return undefined;
|
|
85
|
+
return { lead: 'operato', name };
|
|
86
|
+
}
|
|
87
|
+
/** The count as the bell shows it. Above 99 the exact number stops being worth the width. */
|
|
88
|
+
export function unreadLabel(unread) {
|
|
89
|
+
if (!unread || unread < 1)
|
|
90
|
+
return undefined;
|
|
91
|
+
return unread > 99 ? '99+' : String(unread);
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=header-band-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header-band-model.js","sourceRoot":"","sources":["../../client/layout/header-band-model.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAU,CAAA;AAqBvF;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,QAA4B,EAAE;IACxD,MAAM,MAAM,GAAG,EAA6B,CAAA;IAE5C,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAA;IACtE,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,YAAiC,EAAE,gBAAoC,EAAE;IACvG,2GAA2G;IAC3G,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAA;IAE1C,OAAO,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAA;AAC3D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAmB;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAA;IAEzB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAA;IAC/B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAA;IACrC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU;QAAE,OAAO,MAAM,CAAA;IAEzD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,SAAS,CAAC,KAAmB;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IAErB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAA;IAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAA;IAE7E,OAAO,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAA;AAC9B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAgB;IACvC,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IACnC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAA;IAE3B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;AAClC,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,WAAW,CAAC,MAA0B;IACpD,IAAI,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAA;IAE3C,OAAO,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;AAC7C,CAAC","sourcesContent":["/**\n * What the shared header band decides, kept apart from how it draws.\n *\n * ADR-0060 decision 4 and its 2026-09-18 addendum. plant, twin and figure each wrote this row for\n * themselves - 386, 603 and 185 lines - and the three drifted: one drew a menu button and another\n * did not, one put the unread count inside the pill and another hung it off the corner, each\n * reached for a different set of colour tokens. The band is one copy of the row; this file is the\n * part of it a test in this repository can reach, since the rendering needs a DOM.\n */\n\n/** A tool as the registry keeps it. `template` is a lit template; this file never looks inside it. */\nexport type AppTool = { name?: string; position?: string; template?: unknown }\n\n/**\n * The five places, in the order they are drawn, left to right.\n *\n * These are `TOOL_POSITION`'s own values, written out rather than imported: `@operato/layout` is an\n * ES module and this repository's test runner does not transform node_modules, so importing the\n * enum here would take the deciding out of reach of every test. `tests/header-band.test.ts` reads\n * the installed declaration and checks that these five still match it.\n */\nexport const SLOT_ORDER = ['FRONT_END', 'FRONT', 'CENTER', 'REAR', 'REAR_END'] as const\n\nexport type Slot = (typeof SLOT_ORDER)[number]\n\n/**\n * The scope an app hands over: what it is showing, and how to change it.\n *\n * The band draws this and knows nothing else about it (ADR-0048 · 0058). plant passes factories,\n * twin passes sites, warehouse passes warehouses, and none of those words reach this package.\n */\nexport type HeaderScope = {\n /** What this kind of thing is called, for the control's accessible name. */\n label: string\n /** What there is to choose from. One or none means there is nothing to choose. */\n items?: { id: string; label: string }[]\n /** Which one is being shown. */\n selectedId?: string\n /** Called with the chosen id. Entering it is the app's business, not the band's. */\n onSelect?: (id: string) => void\n}\n\n/**\n * Split the one registry into the five slots.\n *\n * The band and `app-toolbar` read the same `store.getState().apptool.tools`, so lite-menu's\n * hamburger and more-ui's morenda button land in the band without either package knowing the band\n * exists. A second registry would mean every contributor choosing a side.\n */\nexport function toolsBySlot(tools: readonly AppTool[] = []): Record<Slot, AppTool[]> {\n const bySlot = {} as Record<Slot, AppTool[]>\n\n for (const slot of SLOT_ORDER) {\n bySlot[slot] = (tools || []).filter(tool => tool?.position === slot)\n }\n\n return bySlot\n}\n\n/**\n * Whether the band draws its own way into the menu.\n *\n * lite-menu hides the rail on a narrow viewport and registers its hamburger in the **app toolbar**\n * (`TOOL_POSITION.FRONT_END`), which an app running the band has turned off. twin turned it off and\n * drew no button, so on a narrow viewport there was no way to leave the screen you were on\n * (2026-09-18). With the rail on screen a second way in is noise, so the button comes and goes with\n * it.\n *\n * `undefined` means the menu part has not registered yet. That reads as shown, so the button does\n * not flash on during boot.\n */\nexport function menuButtonShown(menuPartShow: boolean | undefined, frontEndTools: readonly AppTool[] = []): boolean {\n /* Something already registered a way in - lite-menu's own hamburger, or the app's. Don't draw a second. */\n if (frontEndTools.length > 0) return false\n\n return menuPartShow === undefined ? false : !menuPartShow\n}\n\n/**\n * How much of a scope control there is to draw.\n *\n * A single factory draws its name and no picker: an install with one of something pays nothing for\n * the ability to switch between several. plant already worked this way and the band keeps it.\n */\nexport function scopeShape(scope?: HeaderScope): 'none' | 'name' | 'picker' {\n if (!scope) return 'none'\n\n const items = scope.items || []\n if (items.length > 1) return 'picker'\n if (items.length === 1 || scope.selectedId) return 'name'\n\n return 'none'\n}\n\n/** The name to draw for a scope that has no picker. */\nexport function scopeName(scope?: HeaderScope): string {\n if (!scope) return ''\n\n const items = scope.items || []\n const selected = items.find(item => item.id === scope.selectedId) || items[0]\n\n return selected?.label || ''\n}\n\n/**\n * The product name, split where it is drawn in two weights: `operato` **plant**.\n *\n * The band owns this shape, and the app gives it one word (ruling ① of the addendum). plant and\n * figure had written the same two-part wordmark by hand and twin had none at all.\n */\nexport function wordmark(product?: string): { lead: string; name: string } | undefined {\n const name = (product || '').trim()\n if (!name) return undefined\n\n return { lead: 'operato', name }\n}\n\n/** The count as the bell shows it. Above 99 the exact number stops being worth the width. */\nexport function unreadLabel(unread: number | undefined): string | undefined {\n if (!unread || unread < 1) return undefined\n\n return unread > 99 ? '99+' : String(unread)\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HeaderScope } from './header-band-model';
|
|
2
|
+
/** What the band draws now. */
|
|
3
|
+
export declare function getHeaderScope(): HeaderScope | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* Hand the band a scope, or `undefined` to take it away.
|
|
6
|
+
*
|
|
7
|
+
* Call it again whenever the list or the chosen one changes — the band redraws from what it is
|
|
8
|
+
* given and keeps nothing of its own.
|
|
9
|
+
*/
|
|
10
|
+
export declare function setHeaderScope(scope?: HeaderScope): void;
|
|
11
|
+
/** Watch it. Returns the unsubscribe, which the band calls when it leaves the document. */
|
|
12
|
+
export declare function subscribeHeaderScope(listener: () => void): () => void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an app hands the band its scope — what it is showing, and how to change it.
|
|
3
|
+
*
|
|
4
|
+
* ADR-0060 decision 4 addendum, ruling ②: the band draws the control and the app owns the words and
|
|
5
|
+
* the move. plant passes its factories, twin its sites, warehouse its warehouses, and none of those
|
|
6
|
+
* words are in this package.
|
|
7
|
+
*
|
|
8
|
+
* A module-level value rather than a redux slice: it is one object, the band is its only reader,
|
|
9
|
+
* and a slice would put the app's vocabulary in the shared store where anything could read it.
|
|
10
|
+
*/
|
|
11
|
+
let current;
|
|
12
|
+
const listeners = new Set();
|
|
13
|
+
/** What the band draws now. */
|
|
14
|
+
export function getHeaderScope() {
|
|
15
|
+
return current;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Hand the band a scope, or `undefined` to take it away.
|
|
19
|
+
*
|
|
20
|
+
* Call it again whenever the list or the chosen one changes — the band redraws from what it is
|
|
21
|
+
* given and keeps nothing of its own.
|
|
22
|
+
*/
|
|
23
|
+
export function setHeaderScope(scope) {
|
|
24
|
+
current = scope;
|
|
25
|
+
for (const listener of [...listeners])
|
|
26
|
+
listener();
|
|
27
|
+
}
|
|
28
|
+
/** Watch it. Returns the unsubscribe, which the band calls when it leaves the document. */
|
|
29
|
+
export function subscribeHeaderScope(listener) {
|
|
30
|
+
listeners.add(listener);
|
|
31
|
+
return () => listeners.delete(listener);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=header-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header-scope.js","sourceRoot":"","sources":["../../client/layout/header-scope.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,IAAI,OAAgC,CAAA;AACpC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAA;AAEvC,+BAA+B;AAC/B,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAmB;IAChD,OAAO,GAAG,KAAK,CAAA;IACf,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,SAAS,CAAC;QAAE,QAAQ,EAAE,CAAA;AACnD,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,oBAAoB,CAAC,QAAoB;IACvD,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACvB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;AACzC,CAAC","sourcesContent":["import type { HeaderScope } from './header-band-model'\n\n/**\n * Where an app hands the band its scope — what it is showing, and how to change it.\n *\n * ADR-0060 decision 4 addendum, ruling ②: the band draws the control and the app owns the words and\n * the move. plant passes its factories, twin its sites, warehouse its warehouses, and none of those\n * words are in this package.\n *\n * A module-level value rather than a redux slice: it is one object, the band is its only reader,\n * and a slice would put the app's vocabulary in the shared store where anything could read it.\n */\n\nlet current: HeaderScope | undefined\nconst listeners = new Set<() => void>()\n\n/** What the band draws now. */\nexport function getHeaderScope(): HeaderScope | undefined {\n return current\n}\n\n/**\n * Hand the band a scope, or `undefined` to take it away.\n *\n * Call it again whenever the list or the chosen one changes — the band redraws from what it is\n * given and keeps nothing of its own.\n */\nexport function setHeaderScope(scope?: HeaderScope): void {\n current = scope\n for (const listener of [...listeners]) listener()\n}\n\n/** Watch it. Returns the unsubscribe, which the band calls when it leaves the document. */\nexport function subscribeHeaderScope(listener: () => void): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n"]}
|
|
@@ -5,8 +5,10 @@ declare const MDIBarSettingLet_base: (new (...args: any[]) => LitElement) & type
|
|
|
5
5
|
export declare class MDIBarSettingLet extends MDIBarSettingLet_base {
|
|
6
6
|
static styles: import("lit").CSSResult[];
|
|
7
7
|
show: boolean;
|
|
8
|
+
private _unsubscribe?;
|
|
8
9
|
render(): import("lit-html").TemplateResult<1>;
|
|
9
|
-
|
|
10
|
+
connectedCallback(): void;
|
|
11
|
+
disconnectedCallback(): void;
|
|
10
12
|
onChangeShowMDIBar(e: any): Promise<void>;
|
|
11
13
|
}
|
|
12
14
|
export {};
|
|
@@ -4,8 +4,9 @@ import '@material/web/checkbox/checkbox.js';
|
|
|
4
4
|
import { css, html, LitElement } from 'lit';
|
|
5
5
|
import { customElement, property } from 'lit/decorators.js';
|
|
6
6
|
import { i18next, localize } from '@operato/i18n';
|
|
7
|
-
import { clientSettingStore } from '@operato/shell';
|
|
7
|
+
import { clientSettingStore, store } from '@operato/shell';
|
|
8
8
|
import { updateViewpart } from '@operato/layout';
|
|
9
|
+
import { mdibarShownNow } from './mdibar-setting-state';
|
|
9
10
|
let MDIBarSettingLet = class MDIBarSettingLet extends localize(i18next)(LitElement) {
|
|
10
11
|
constructor() {
|
|
11
12
|
super(...arguments);
|
|
@@ -39,8 +40,21 @@ let MDIBarSettingLet = class MDIBarSettingLet extends localize(i18next)(LitEleme
|
|
|
39
40
|
</setting-let>
|
|
40
41
|
`;
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
/*
|
|
44
|
+
* Follow the bar, not the stored setting: until the user picks, an app that starts the bar on has
|
|
45
|
+
* nothing stored, and this row drew "off" over a bar that was on (see `mdibar-setting-state`).
|
|
46
|
+
*
|
|
47
|
+
* Reading once is not enough — the card outlives a `updateViewpart('appmdibar', …)` from anywhere
|
|
48
|
+
* else, and would then draw the old answer.
|
|
49
|
+
*/
|
|
50
|
+
connectedCallback() {
|
|
51
|
+
super.connectedCallback();
|
|
52
|
+
this.show = mdibarShownNow(store.getState());
|
|
53
|
+
this._unsubscribe = store.subscribe(() => (this.show = mdibarShownNow(store.getState())));
|
|
54
|
+
}
|
|
55
|
+
disconnectedCallback() {
|
|
56
|
+
super.disconnectedCallback();
|
|
57
|
+
this._unsubscribe?.();
|
|
44
58
|
}
|
|
45
59
|
async onChangeShowMDIBar(e) {
|
|
46
60
|
const show = e.currentTarget.checked;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mdibar-setting-let.js","sourceRoot":"","sources":["../client/mdibar-setting-let.ts"],"names":[],"mappings":";AAAA,OAAO,0BAA0B,CAAA;AACjC,OAAO,oCAAoC,CAAA;AAE3C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAS,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"mdibar-setting-let.js","sourceRoot":"","sources":["../client/mdibar-setting-let.ts"],"names":[],"mappings":";AAAA,OAAO,0BAA0B,CAAA;AACjC,OAAO,oCAAoC,CAAA;AAE3C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAS,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAGhD,IAAM,gBAAgB,GAAtB,MAAM,gBAAiB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAA5D;;QAewB,SAAI,GAAY,KAAK,CAAA;IA2DpD,CAAC;aAzEQ,WAAM,GAAG;QACd,GAAG,CAAA;;;;;;;;;;KAUF;KACF,AAZY,CAYZ;IAMD,MAAM;QACJ,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAA;QAElC,OAAO,IAAI,CAAA;;;;;;mCAMoB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,aAAa,OAAO;cACxE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC;;;;KAIhC,CAAA;IACH,CAAC;IAED;;;;;;OAMG;IACH,iBAAiB;QACf,KAAK,CAAC,iBAAiB,EAAE,CAAA;QAEzB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC5C,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAA;IAC3F,CAAC;IAED,oBAAoB;QAClB,KAAK,CAAC,oBAAoB,EAAE,CAAA;QAE5B,IAAI,CAAC,YAAY,EAAE,EAAE,CAAA;IACvB,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,aAAa,CAAC,OAAO,CAAA;QAEpC,IAAI,CAAC;YACH,MAAM,kBAAkB,CAAC,GAAG,CAAC;gBAC3B,GAAG,EAAE,QAAQ;gBACb,KAAK,EAAE;oBACL,IAAI;iBACL;aACF,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAClB,CAAC;QAED,cAAc,CAAC,WAAW,EAAE;YAC1B,IAAI;SACL,CAAC,CAAA;IACJ,CAAC;;AA1D4B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;8CAAsB;AAfvC,gBAAgB;IAD5B,aAAa,CAAC,oBAAoB,CAAC;GACvB,gBAAgB,CA0E5B","sourcesContent":["import '@operato/i18n/ox-i18n.js'\nimport '@material/web/checkbox/checkbox.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { clientSettingStore, store } from '@operato/shell'\nimport { updateViewpart } from '@operato/layout'\n\nimport { mdibarShownNow } from './mdibar-setting-state'\n\n@customElement('mdibar-setting-let')\nexport class MDIBarSettingLet extends localize(i18next)(LitElement) {\n static styles = [\n css`\n label {\n display: flex;\n gap: 10px;\n align-items: center;\n\n font: var(--label-font);\n color: var(--md-sys-color-on-primary-container);\n text-transform: var(--label-text-transform);\n }\n `\n ]\n\n @property({ type: Boolean }) show: boolean = false\n\n private _unsubscribe?: () => void\n\n render() {\n const checked = this.show === true\n\n return html`\n <setting-let>\n <ox-i18n slot=\"title\" msgid=\"title.mdibar setting\"></ox-i18n>\n\n <div slot=\"content\">\n <label>\n <md-checkbox @change=${e => this.onChangeShowMDIBar(e)} ?checked=${checked}></md-checkbox>\n ${i18next.t('label.show')}\n </label>\n </div>\n </setting-let>\n `\n }\n\n /*\n * Follow the bar, not the stored setting: until the user picks, an app that starts the bar on has\n * nothing stored, and this row drew \"off\" over a bar that was on (see `mdibar-setting-state`).\n *\n * Reading once is not enough — the card outlives a `updateViewpart('appmdibar', …)` from anywhere\n * else, and would then draw the old answer.\n */\n connectedCallback() {\n super.connectedCallback()\n\n this.show = mdibarShownNow(store.getState())\n this._unsubscribe = store.subscribe(() => (this.show = mdibarShownNow(store.getState())))\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n\n this._unsubscribe?.()\n }\n\n async onChangeShowMDIBar(e) {\n const show = e.currentTarget.checked\n\n try {\n await clientSettingStore.put({\n key: 'mdibar',\n value: {\n show\n }\n })\n } catch (e) {\n console.error(e)\n }\n\n updateViewpart('appmdibar', {\n show\n })\n }\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the MDI setting row shows: **the state the tab bar is actually in.**
|
|
3
|
+
*
|
|
4
|
+
* The row used to read the stored setting alone (`clientSettingStore 'mdibar'`). An app that starts
|
|
5
|
+
* the bar on (`setupAppToolPart({ mdibar: { default: true } })`) leaves that value unset until the
|
|
6
|
+
* user picks something, so the row drew an empty checkbox while the bar stood on the screen — the
|
|
7
|
+
* setting said off and the product said on. Someone arriving for the first time ticks it to turn the
|
|
8
|
+
* bar on and nothing happens, because it was on already.
|
|
9
|
+
*
|
|
10
|
+
* `setupAppToolPart` has already folded the stored value and the app's default into the viewpart, so
|
|
11
|
+
* the viewpart is the one place that knows the answer. Writing the default into the store instead
|
|
12
|
+
* would freeze it: the app could never change its own default afterwards.
|
|
13
|
+
*
|
|
14
|
+
* No imports, so the test runner can hold it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function mdibarShownNow(state: any): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The bar's state when the app starts: the user's stored choice, or the app's own default until they
|
|
19
|
+
* make one.
|
|
20
|
+
*
|
|
21
|
+
* `setupAppToolPart` and the setting row must not each carry their own version of this — that split
|
|
22
|
+
* is what let the row say "off" over a bar that was on.
|
|
23
|
+
*
|
|
24
|
+
* `mdibar: true` keeps the old behaviour (off until the user turns it on); `{ default: true }` starts
|
|
25
|
+
* it on.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveMdibarShow(stored: boolean | undefined, option: boolean | {
|
|
28
|
+
default: boolean;
|
|
29
|
+
}): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the MDI setting row shows: **the state the tab bar is actually in.**
|
|
3
|
+
*
|
|
4
|
+
* The row used to read the stored setting alone (`clientSettingStore 'mdibar'`). An app that starts
|
|
5
|
+
* the bar on (`setupAppToolPart({ mdibar: { default: true } })`) leaves that value unset until the
|
|
6
|
+
* user picks something, so the row drew an empty checkbox while the bar stood on the screen — the
|
|
7
|
+
* setting said off and the product said on. Someone arriving for the first time ticks it to turn the
|
|
8
|
+
* bar on and nothing happens, because it was on already.
|
|
9
|
+
*
|
|
10
|
+
* `setupAppToolPart` has already folded the stored value and the app's default into the viewpart, so
|
|
11
|
+
* the viewpart is the one place that knows the answer. Writing the default into the store instead
|
|
12
|
+
* would freeze it: the app could never change its own default afterwards.
|
|
13
|
+
*
|
|
14
|
+
* No imports, so the test runner can hold it.
|
|
15
|
+
*/
|
|
16
|
+
export function mdibarShownNow(state) {
|
|
17
|
+
return state?.layout?.viewparts?.appmdibar?.show === true;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The bar's state when the app starts: the user's stored choice, or the app's own default until they
|
|
21
|
+
* make one.
|
|
22
|
+
*
|
|
23
|
+
* `setupAppToolPart` and the setting row must not each carry their own version of this — that split
|
|
24
|
+
* is what let the row say "off" over a bar that was on.
|
|
25
|
+
*
|
|
26
|
+
* `mdibar: true` keeps the old behaviour (off until the user turns it on); `{ default: true }` starts
|
|
27
|
+
* it on.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveMdibarShow(stored, option) {
|
|
30
|
+
if (stored !== undefined) {
|
|
31
|
+
return stored === true;
|
|
32
|
+
}
|
|
33
|
+
return typeof option === 'object' && option !== null ? option.default === true : false;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=mdibar-setting-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdibar-setting-state.js","sourceRoot":"","sources":["../client/mdibar-setting-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,KAAU;IACvC,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,KAAK,IAAI,CAAA;AAC3D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAA2B,EAAE,MAAsC;IACnG,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,MAAM,KAAK,IAAI,CAAA;IACxB,CAAC;IAED,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAA;AACxF,CAAC","sourcesContent":["/**\n * What the MDI setting row shows: **the state the tab bar is actually in.**\n *\n * The row used to read the stored setting alone (`clientSettingStore 'mdibar'`). An app that starts\n * the bar on (`setupAppToolPart({ mdibar: { default: true } })`) leaves that value unset until the\n * user picks something, so the row drew an empty checkbox while the bar stood on the screen — the\n * setting said off and the product said on. Someone arriving for the first time ticks it to turn the\n * bar on and nothing happens, because it was on already.\n *\n * `setupAppToolPart` has already folded the stored value and the app's default into the viewpart, so\n * the viewpart is the one place that knows the answer. Writing the default into the store instead\n * would freeze it: the app could never change its own default afterwards.\n *\n * No imports, so the test runner can hold it.\n */\nexport function mdibarShownNow(state: any): boolean {\n return state?.layout?.viewparts?.appmdibar?.show === true\n}\n\n/**\n * The bar's state when the app starts: the user's stored choice, or the app's own default until they\n * make one.\n *\n * `setupAppToolPart` and the setting row must not each carry their own version of this — that split\n * is what let the row say \"off\" over a bar that was on.\n *\n * `mdibar: true` keeps the old behaviour (off until the user turns it on); `{ default: true }` starts\n * it on.\n */\nexport function resolveMdibarShow(stored: boolean | undefined, option: boolean | { default: boolean }): boolean {\n if (stored !== undefined) {\n return stored === true\n }\n\n return typeof option === 'object' && option !== null ? option.default === true : false\n}\n"]}
|