@operato/layout 10.29.39 → 10.29.43
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/CHANGELOG.md +17 -0
- package/dist/src/actions/entity360-name.js +1 -1
- package/dist/src/actions/entity360-name.js.map +1 -1
- package/dist/src/actions/entity360-registry.d.ts +4 -2
- package/dist/src/actions/entity360-registry.js +6 -4
- package/dist/src/actions/entity360-registry.js.map +1 -1
- package/dist/src/actions/entity360.d.ts +5 -2
- package/dist/src/actions/entity360.js +6 -3
- package/dist/src/actions/entity360.js.map +1 -1
- package/dist/src/actions/layout.d.ts +2 -0
- package/dist/src/actions/layout.js +15 -2
- package/dist/src/actions/layout.js.map +1 -1
- package/dist/src/components/ox-url-tabs.js +2 -2
- package/dist/src/components/ox-url-tabs.js.map +1 -1
- package/dist/test/overlay-close-after-write.test.d.ts +1 -0
- package/dist/test/overlay-close-after-write.test.js +29 -0
- package/dist/test/overlay-close-after-write.test.js.map +1 -0
- package/dist/test/popup-navigation.test.d.ts +1 -0
- package/dist/test/popup-navigation.test.js +60 -0
- package/dist/test/popup-navigation.test.js.map +1 -0
- package/dist/test/popup-result.test.js +4 -2
- package/dist/test/popup-result.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/test/overlay-close-after-write.test.ts +31 -0
- package/test/popup-navigation.test.ts +67 -0
- package/test/popup-result.test.ts +5 -2
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -1136
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov.info +0 -62848
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
### [10.29.43](https://github.com/hatiolab/operato/compare/v10.29.42...v10.29.43) (2026-10-18)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### :bug: Bug Fix
|
|
10
|
+
|
|
11
|
+
* **address:** the address layer works in every browser and keeps what users had ([33fba6b](https://github.com/hatiolab/operato/commit/33fba6bff742c5c79b6849d9ed6cb5f7915dd2ec))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### [10.29.40](https://github.com/hatiolab/operato/compare/v10.29.39...v10.29.40) (2026-10-17)
|
|
16
|
+
|
|
17
|
+
**Note:** Version bump only for package @operato/layout
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
6
23
|
### [10.29.39](https://github.com/hatiolab/operato/compare/v10.29.38...v10.29.39) (2026-10-17)
|
|
7
24
|
|
|
8
25
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity360-name.js","sourceRoot":"","sources":["../../../src/actions/entity360-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAA;AAE/C,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAErG;;;;;;;;;;;GAWG;AACH,MAAM,SAAS,GAAG,mDAAmD,CAAA;AACrE,MAAM,WAAW,GAAG,mDAAmD,CAAA;AAEvE,SAAS,gBAAgB,CAAC,KAAY,EAAE,OAAgB;IACtD,MAAM,IAAI,GAAG,KAAK,CAAC,aAA4B,CAAA;IAC/C,IAAI,CAAC,KAAK,CAAC,mBAAmB,GAAG,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAA;AAC9G,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAA6C,EAAE,IAAa;;IACxF,MAAM,KAAK,GAAG,MAAA,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,mCAAI,EAAE,CAAA;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAEvD,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,KAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,EAA6B,EAC7B,IAAa;AACb,yGAAyG;AACzG,IAA0B;;IAE1B,MAAM,KAAK,GAAG,MAAA,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,EAAE,mCAAI,EAAE,CAAA;IAC9B,IAAI,CAAC,EAAE,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAEvD,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;AACxD,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,KAA0B,EAAE,KAAa;IACnE,OAAO,IAAI,CAAA;;;6JAGgJ,SAAS;oBAClJ,CAAC,KAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC;oBAC/C,CAAC,KAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;aACvD,CAAC,KAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;YACjD,CAAC,KAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;mBACzC,CAAC,KAAY,EAAE,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE;eAC7C,CAAC,KAAoB,EAAE,EAAE;QAClC,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO;YAAE,KAAK,CAAC,eAAe,EAAE,CAAA;IACpD,CAAC;aACQ,CAAC,KAAY,EAAE,EAAE;QACxB,KAAK,CAAC,cAAc,EAAE,CAAA;QACtB,KAAK,CAAC,eAAe,EAAE,CAAA;QACvB,MAAM,MAAM,GAAG,KAAK,CAAC,aAA4B,CAAA;QACjD,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,MAAM,CAAC,WAAW;YAAE,MAAM,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"entity360-name.js","sourceRoot":"","sources":["../../../src/actions/entity360-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAA;AAE/C,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAErG;;;;;;;;;;;GAWG;AACH,MAAM,SAAS,GAAG,mDAAmD,CAAA;AACrE,MAAM,WAAW,GAAG,mDAAmD,CAAA;AAEvE,SAAS,gBAAgB,CAAC,KAAY,EAAE,OAAgB;IACtD,MAAM,IAAI,GAAG,KAAK,CAAC,aAA4B,CAAA;IAC/C,IAAI,CAAC,KAAK,CAAC,mBAAmB,GAAG,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAA;AAC9G,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAA6C,EAAE,IAAa;;IACxF,MAAM,KAAK,GAAG,MAAA,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,mCAAI,EAAE,CAAA;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAEvD,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,KAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,EAA6B,EAC7B,IAAa;AACb,yGAAyG;AACzG,IAA0B;;IAE1B,MAAM,KAAK,GAAG,MAAA,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,EAAE,mCAAI,EAAE,CAAA;IAC9B,IAAI,CAAC,EAAE,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAEvD,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;AACxD,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,KAA0B,EAAE,KAAa;IACnE,OAAO,IAAI,CAAA;;;6JAGgJ,SAAS;oBAClJ,CAAC,KAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC;oBAC/C,CAAC,KAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;aACvD,CAAC,KAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;YACjD,CAAC,KAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;mBACzC,CAAC,KAAY,EAAE,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE;eAC7C,CAAC,KAAoB,EAAE,EAAE;QAClC,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO;YAAE,KAAK,CAAC,eAAe,EAAE,CAAA;IACpD,CAAC;aACQ,CAAC,KAAY,EAAE,EAAE;QACxB,KAAK,CAAC,cAAc,EAAE,CAAA;QACtB,KAAK,CAAC,eAAe,EAAE,CAAA;QACvB,MAAM,MAAM,GAAG,KAAK,CAAC,aAA4B,CAAA;QACjD,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,MAAM,CAAC,WAAW;YAAE,MAAM,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;IAC1F,CAAC;OACE,KAAK;IACR,CAAA;AACJ,CAAC","sourcesContent":["import { html, type TemplateResult } from 'lit'\n\nimport { entity360KindOf, entity360KindRegistered, openEntity360Kind } from './entity360-registry.js'\n\n/**\n * An entity's name as the screen draws it: a link to its 360 when its kind is registered, the plain\n * text otherwise (ADR-0067 decision 1 - an entity's name opens its 360; ADR-0071 - no dead links).\n *\n * One markup for every place a name is drawn - a grid cell, a ribbon's 「담당자」, a detail's\n * 「올린 사람」 - with the same interaction everywhere.\n *\n * The link keeps the body colour and carries a thin, faint underline set off from the text, so a\n * column of names stays calm; the line firms up under the pointer or keyboard focus. Keep this in\n * the helper as it also renders inside grid and section shadow roots. A click does not travel on:\n * inside a grid row it would start editing or select the row.\n */\nconst LINE_REST = 'color-mix(in srgb, currentColor 30%, transparent)'\nconst LINE_ACTIVE = 'color-mix(in srgb, currentColor 65%, transparent)'\n\nfunction updateDecoration(event: Event, hovered: boolean) {\n const link = event.currentTarget as HTMLElement\n link.style.textDecorationColor = hovered || link.matches(':hover, :focus-visible') ? LINE_ACTIVE : LINE_REST\n}\n\nexport function entity360Name(value: Record<string, any> | null | undefined, text?: string): TemplateResult | string {\n const label = text ?? value?.name ?? ''\n const kind = value ? entity360KindOf(value) : undefined\n\n return kind ? link(kind, value!, label) : label\n}\n\n/**\n * An identifier as the screen draws it when the screen knows its kind but holds no typed value - an\n * id inside a row (a location in a stock line), or a relation inside another 360. The same link as\n * entity360Name; plain text for a kind nobody registered, or an empty id.\n */\nexport function entity360Link(\n kind: string,\n id: string | null | undefined,\n text?: string,\n /** More the kind's sections read - which twin the item belongs to, say. Given to the subject's value. */\n more?: Record<string, any>\n): TemplateResult | string {\n const label = text ?? id ?? ''\n if (!id || !entity360KindRegistered(kind)) return label\n\n return link(kind, { ...more, id, name: label }, label)\n}\n\nfunction link(kind: string, value: Record<string, any>, label: string): TemplateResult {\n return html`<a\n href=\"#\"\n data-entity360\n style=\"color: inherit; cursor: pointer; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; text-decoration-color: ${LINE_REST}; transition: text-decoration-color 0.12s\"\n @pointerenter=${(event: Event) => updateDecoration(event, true)}\n @pointerleave=${(event: Event) => updateDecoration(event, false)}\n @focus=${(event: Event) => updateDecoration(event, false)}\n @blur=${(event: Event) => updateDecoration(event, false)}\n @pointerdown=${(event: Event) => event.stopPropagation()}\n @keydown=${(event: KeyboardEvent) => {\n if (event.key === 'Enter') event.stopPropagation()\n }}\n @click=${(event: Event) => {\n event.preventDefault()\n event.stopPropagation()\n const opener = event.currentTarget as HTMLElement\n openEntity360Kind(kind, value, () => { if (opener.isConnected) opener.focus() }, opener)\n }}\n >${label}</a\n >`\n}\n"]}
|
|
@@ -75,7 +75,7 @@ export declare function entity360Rejections(): Entity360Rejection[];
|
|
|
75
75
|
* A kind with sections shows them in order. A kind with none - or a value that opens no kind -
|
|
76
76
|
* shows one line saying there is no 360 for it yet. The dock is never blank.
|
|
77
77
|
*/
|
|
78
|
-
export declare function openEntity360For(value: Record<string, any>, returnFocus?: () => void): void;
|
|
78
|
+
export declare function openEntity360For(value: Record<string, any>, returnFocus?: () => void, source?: Element): void;
|
|
79
79
|
/**
|
|
80
80
|
* Open the 360 of a kind directly - for a value known by its kind and id rather than by a GraphQL
|
|
81
81
|
* typename: an identifier inside a row, or a relation inside another 360.
|
|
@@ -83,7 +83,9 @@ export declare function openEntity360For(value: Record<string, any>, returnFocus
|
|
|
83
83
|
* Opening is a step in the address (`f=kind:id`, the same step twin's focus takes), so the dock's back
|
|
84
84
|
* button and the browser's go to the 360 before, and a reload or a shared link opens the same one.
|
|
85
85
|
*/
|
|
86
|
-
export declare function openEntity360Kind(kind: string, value: Record<string, any>, returnFocus?: () => void
|
|
86
|
+
export declare function openEntity360Kind(kind: string, value: Record<string, any>, returnFocus?: () => void,
|
|
87
|
+
/** The element the request comes from; a link inside a popup names that popup as its origin. */
|
|
88
|
+
source?: Element): void;
|
|
87
89
|
/** Close the registry's 360 and take its step out of the address - for an app whose own state let go of it. */
|
|
88
90
|
export declare function closeEntity360(): void;
|
|
89
91
|
/**
|
|
@@ -88,9 +88,9 @@ function subjectInAddress() {
|
|
|
88
88
|
* A kind with sections shows them in order. A kind with none - or a value that opens no kind -
|
|
89
89
|
* shows one line saying there is no 360 for it yet. The dock is never blank.
|
|
90
90
|
*/
|
|
91
|
-
export function openEntity360For(value, returnFocus) {
|
|
91
|
+
export function openEntity360For(value, returnFocus, source) {
|
|
92
92
|
var _a;
|
|
93
|
-
openEntity360Kind((_a = entity360KindOf(value)) !== null && _a !== void 0 ? _a : '', value, returnFocus);
|
|
93
|
+
openEntity360Kind((_a = entity360KindOf(value)) !== null && _a !== void 0 ? _a : '', value, returnFocus, source);
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
96
|
* Open the 360 of a kind directly - for a value known by its kind and id rather than by a GraphQL
|
|
@@ -99,7 +99,9 @@ export function openEntity360For(value, returnFocus) {
|
|
|
99
99
|
* Opening is a step in the address (`f=kind:id`, the same step twin's focus takes), so the dock's back
|
|
100
100
|
* button and the browser's go to the 360 before, and a reload or a shared link opens the same one.
|
|
101
101
|
*/
|
|
102
|
-
export function openEntity360Kind(kind, value, returnFocus
|
|
102
|
+
export function openEntity360Kind(kind, value, returnFocus,
|
|
103
|
+
/** The element the request comes from; a link inside a popup names that popup as its origin. */
|
|
104
|
+
source) {
|
|
103
105
|
var _a;
|
|
104
106
|
const subject = { kind, id: String((_a = value === null || value === void 0 ? void 0 : value.id) !== null && _a !== void 0 ? _a : ''), value: value !== null && value !== void 0 ? value : {} };
|
|
105
107
|
const address = addressOf(kind, subject.id);
|
|
@@ -110,7 +112,7 @@ export function openEntity360Kind(kind, value, returnFocus) {
|
|
|
110
112
|
const url = new URL(location.href);
|
|
111
113
|
if (url.searchParams.get(FOCUS) !== address) {
|
|
112
114
|
url.searchParams.set(FOCUS, address);
|
|
113
|
-
void navigateEntity360(url.pathname + url.search + url.hash);
|
|
115
|
+
void navigateEntity360(url.pathname + url.search + url.hash, source);
|
|
114
116
|
return;
|
|
115
117
|
}
|
|
116
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity360-registry.js","sourceRoot":"","sources":["../../../src/actions/entity360-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, type TemplateResult } from 'lit'\nimport { i18next } from '@operato/i18n'\nimport { addressController, writeAddress } from '@operato/utils/address.js'\n\nimport { store, UPDATE_CONTEXT } from '@operato/shell'\n\nimport { showEntity360, hideEntity360, canGoBackEntity360, isEntity360Shown, navigateEntity360 } from './entity360.js'\n\nconst returnFocusByAddress = new Map<string, () => void>()\n\n/**\n * Which kinds of thing open a 360, and what each module puts in it (ADR-0067, the person 360 ruling).\n *\n * An entity's name is a link to its 360 (ADR-0067 decision 1), and a grid shows names by the dozen -\n * `updater` alone is a person column on 65 framework screens. Writing an opener into every column\n * would never finish. Instead a kind is declared once with the GraphQL `__typename`s it claims, and\n * whatever draws a value can ask `entity360KindOf(value)`: registered, it is a link; not, it stays\n * text, exactly as before.\n *\n * This file knows no product words and imports no product module. A kind brings its own title; each\n * module hangs its own section with its own read door (`registerEntity360Section`), the way activity\n * panels hang on the activity detail. Opening a kind with no section says so in one line - never a\n * blank dock.\n */\n\n/** The thing a 360 is about: its kind, its id, and the value it was opened from. */\nexport interface Entity360Subject {\n kind: string\n id: string\n value: Record<string, any>\n}\n\nexport interface Entity360KindDeclaration {\n /** The name sections register under - 'person', 'role', … */\n kind: string\n /** The GraphQL `__typename`s whose values open this kind. A typename belongs to one kind. */\n typenames: string[]\n /** The dock's title for a subject - what a person recognises it by. */\n title(subject: Entity360Subject): string\n /** A line under the title, if the kind has one. */\n context?(subject: Entity360Subject): string\n /**\n * The open 360 is what the page talks about: while it is open the page context's `subject` is\n * `{ type: kind, id }`, so the AI dock follows it (ai-assistant registerSubjectSurface). Closing\n * gives the page its own subject back. Off by default - a kind the assistant has no surface for\n * would only take the page's subject away.\n */\n assistantSubject?: boolean\n}\n\nexport interface Entity360Section {\n /** Unique within its kind. */\n name: string\n /** Lower comes first; equal orders keep the order they were registered in. */\n order?: number\n /** The section's content - usually the module's own element, which reads its own door. */\n render(subject: Entity360Subject): TemplateResult\n}\n\nexport type Entity360Rejection = { kind: string; name?: string; reason: string }\n\nconst kinds = new Map<string, Entity360KindDeclaration>()\nconst kindOfTypename = new Map<string, string>()\nconst sections = new Map<string, (Entity360Section & { at: number })[]>()\nconst rejected: Entity360Rejection[] = []\nlet registered = 0\n\n/** The dock has one owner for everything opened through this registry. */\nconst OWNER = {}\n\n/**\n * Declare a kind. A kind named twice, or a typename another kind already claims, is refused and\n * kept in `entity360Rejections()` - silently taking the second would reroute links nobody asked to move.\n */\nexport function registerEntity360Kind(declaration: Entity360KindDeclaration): void {\n const kind = String(declaration?.kind ?? '').trim()\n\n if (!kind) return void rejected.push({ kind: '(no kind)', reason: 'no-kind' })\n if (kinds.has(kind)) return void rejected.push({ kind, reason: 'duplicate-kind' })\n if (typeof declaration.title !== 'function') return void rejected.push({ kind, reason: 'no-title' })\n\n const taken = (declaration.typenames || []).find(typename => kindOfTypename.has(typename))\n if (taken) return void rejected.push({ kind, name: taken, reason: 'typename-claimed' })\n\n kinds.set(kind, { ...declaration, kind })\n for (const typename of declaration.typenames || []) kindOfTypename.set(typename, kind)\n}\n\n/** Hang a section on a kind. A name already on that kind is refused. */\nexport function registerEntity360Section(kind: string, section: Entity360Section): void {\n const name = String(section?.name ?? '').trim()\n\n if (!name) return void rejected.push({ kind, reason: 'no-name' })\n if (typeof section.render !== 'function') return void rejected.push({ kind, name, reason: 'no-render' })\n\n const list = sections.get(kind) ?? []\n if (list.some(one => one.name === name)) return void rejected.push({ kind, name, reason: 'duplicate-name' })\n\n list.push({ ...section, name, at: registered++ })\n list.sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.at - b.at)\n sections.set(kind, list)\n}\n\n/**\n * The kind a value opens, or `undefined`.\n *\n * `undefined` for anything not registered and for a value without `__typename` (a call that turned\n * the cache off) - the caller draws it as it always has.\n */\nexport function entity360KindOf(value: unknown): string | undefined {\n const typename = (value as any)?.__typename\n return typeof typename === 'string' ? kindOfTypename.get(typename) : undefined\n}\n\n/** Whether a kind was declared - an identifier of an undeclared kind stays text. */\nexport function entity360KindRegistered(kind: string): boolean {\n return kinds.has(kind)\n}\n\n/** The sections hung on a kind, in order. */\nexport function entity360Sections(kind: string): Entity360Section[] {\n return [...(sections.get(kind) ?? [])]\n}\n\n/** What was refused, and why - for a test or a boot check to read. */\nexport function entity360Rejections(): Entity360Rejection[] {\n return [...rejected]\n}\n\nlet shown: Entity360Subject | undefined\n/** Values opened in this page, by address - going back to one gives its section the value it was opened with. */\nconst opened = new Map<string, Record<string, any>>()\n/** The page's own subject while a 360 stands in for it, and the page it belongs to. */\nlet pageSubject: { path: string; subject: unknown; ours?: unknown } | undefined\n\n/** The address of a subject: `f=kind:id`. The id may hold colons (an EPC URN); the kind does not. */\nconst FOCUS = 'f'\nconst addressOf = (kind: string, id: string) => `${kind}:${id}`\n\nfunction subjectInAddress(): { kind: string; id: string } | undefined {\n const value = new URLSearchParams(location.search).get(FOCUS)\n const at = value ? value.indexOf(':') : -1\n return at > 0 ? { kind: value!.slice(0, at), id: value!.slice(at + 1) } : undefined\n}\n\n/**\n * Open the 360 of a value.\n *\n * A kind with sections shows them in order. A kind with none - or a value that opens no kind -\n * shows one line saying there is no 360 for it yet. The dock is never blank.\n */\nexport function openEntity360For(value: Record<string, any>, returnFocus?: () => void): void {\n openEntity360Kind(entity360KindOf(value) ?? '', value, returnFocus)\n}\n\n/**\n * Open the 360 of a kind directly - for a value known by its kind and id rather than by a GraphQL\n * typename: an identifier inside a row, or a relation inside another 360.\n *\n * Opening is a step in the address (`f=kind:id`, the same step twin's focus takes), so the dock's back\n * button and the browser's go to the 360 before, and a reload or a shared link opens the same one.\n */\nexport function openEntity360Kind(kind: string, value: Record<string, any>, returnFocus?: () => void): void {\n const subject: Entity360Subject = { kind, id: String(value?.id ?? ''), value: value ?? {} }\n const address = addressOf(kind, subject.id)\n opened.set(address, subject.value)\n if (returnFocus) returnFocusByAddress.set(address, returnFocus)\n\n if (kind && subject.id) {\n const url = new URL(location.href)\n if (url.searchParams.get(FOCUS) !== address) {\n url.searchParams.set(FOCUS, address)\n void navigateEntity360(url.pathname + url.search + url.hash)\n return\n }\n }\n\n draw(subject, returnFocus)\n}\n\n/* Back and forward walk the address; the dock follows it and adds no step of its own. */\naddressController().subscribe(() => {\n const inAddress = subjectInAddress()\n\n if (inAddress && kinds.has(inAddress.kind)) {\n if (shown?.kind === inAddress.kind && shown.id === inAddress.id && isEntity360Shown(OWNER)) return\n const value = opened.get(addressOf(inAddress.kind, inAddress.id)) ?? { id: inAddress.id }\n draw({ ...inAddress, value }, returnFocusByAddress.get(addressOf(inAddress.kind, inAddress.id)))\n } else if (isEntity360Shown(OWNER)) {\n close(false)\n }\n}, false)\n\n/** Close the registry's 360 and take its step out of the address - for an app whose own state let go of it. */\nexport function closeEntity360(): void {\n if (isEntity360Shown(OWNER)) close(true)\n}\n\n/**\n * Draw the shown 360 again - its title and context are read anew. For a kind whose title comes from\n * what its section has just read.\n */\nexport function redrawEntity360(): void {\n if (shown && isEntity360Shown(OWNER)) draw(shown)\n}\n\ntype Entity360Listener = (subject: Entity360Subject | undefined) => void\nconst listeners = new Set<Entity360Listener>()\n\n/**\n * Hear which subject the registry's 360 shows - on open, on back or forward, and `undefined` on close.\n * An app with its own selection (twin's focus) follows the dock through this. Returns the unsubscribe.\n */\nexport function onEntity360Change(listener: Entity360Listener): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\nfunction tell(subject: Entity360Subject | undefined): void {\n listeners.forEach(listener => listener(subject))\n}\n\nfunction close(clearAddress: boolean): void {\n if (clearAddress && subjectInAddress()) {\n const url = new URL(location.href)\n url.searchParams.delete(FOCUS)\n void writeAddress(url, { replace: true, state: history.state })\n return\n }\n hideEntity360(OWNER)\n shown = undefined\n giveSubjectBack()\n tell(undefined)\n}\n\nfunction draw(subject: Entity360Subject, returnFocus?: () => void): void {\n const { kind, value } = subject\n const declaration = kinds.get(kind)\n const hung = entity360Sections(kind)\n shown = subject\n\n const content = hung.length\n ? html`${hung.map(section => section.render(subject))}`\n : html`<p data-not-yet>${i18next.t('text.entity360-not-yet', { defaultValue: 'there is no 360 for this kind yet.' })}</p>`\n\n showEntity360(\n OWNER,\n {\n key: addressOf(kind, subject.id),\n title: declaration ? declaration.title(subject) : String(value?.name ?? value?.id ?? ''),\n context: declaration?.context ? declaration.context(subject) : '',\n content,\n labels: {\n close: i18next.t('button.close', { defaultValue: 'close' }),\n expand: i18next.t('button.expand', { defaultValue: 'expand' }),\n collapse: i18next.t('button.collapse', { defaultValue: 'collapse' }),\n resize: i18next.t('label.resize', { defaultValue: 'resize' }),\n back: i18next.t('button.back', { defaultValue: 'back' })\n },\n onClose: () => close(true),\n onBack: canGoBackEntity360() ? () => history.back() : undefined\n },\n returnFocus\n )\n\n if (declaration?.assistantSubject) takeSubject({ type: kind, id: subject.id })\n else giveSubjectBack()\n\n tell(subject)\n}\n\nfunction routeContext(): any {\n return (store.getState() as any)?.route?.context ?? {}\n}\n\nfunction takeSubject(ours: { type: string; id: string }): void {\n const context = routeContext()\n if (!pageSubject || pageSubject.path !== location.pathname) pageSubject = { path: location.pathname, subject: context.subject }\n pageSubject.ours = ours\n store.dispatch({ type: UPDATE_CONTEXT, context: { ...context, subject: ours } })\n}\n\n/** Only on the same page, and only while the subject is still the one the 360 put there. */\nfunction giveSubjectBack(): void {\n const taken = pageSubject\n pageSubject = undefined\n if (!taken || taken.path !== location.pathname) return\n\n const context = routeContext()\n if (context.subject === taken.ours) store.dispatch({ type: UPDATE_CONTEXT, context: { ...context, subject: taken.subject } })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"entity360-registry.js","sourceRoot":"","sources":["../../../src/actions/entity360-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAE3E,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEtD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEtH,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAsB,CAAA;AAqD1D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoC,CAAA;AACzD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAA;AAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAiD,CAAA;AACzE,MAAM,QAAQ,GAAyB,EAAE,CAAA;AACzC,IAAI,UAAU,GAAG,CAAC,CAAA;AAElB,0EAA0E;AAC1E,MAAM,KAAK,GAAG,EAAE,CAAA;AAEhB;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAqC;;IACzE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,IAAI,mCAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAEnD,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;IAC9E,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAA;IAClF,IAAI,OAAO,WAAW,CAAC,KAAK,KAAK,UAAU;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;IAEpG,MAAM,KAAK,GAAG,CAAC,WAAW,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC1F,IAAI,KAAK;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAA;IAEvF,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;IACzC,KAAK,MAAM,QAAQ,IAAI,WAAW,CAAC,SAAS,IAAI,EAAE;QAAE,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;AACxF,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,wBAAwB,CAAC,IAAY,EAAE,OAAyB;;IAC9E,MAAM,IAAI,GAAG,MAAM,CAAC,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,IAAI,mCAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAE/C,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;IACjE,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAA;IAExG,MAAM,IAAI,GAAG,MAAA,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,mCAAI,EAAE,CAAA;IACrC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAA;IAE5G,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,CAAA;IACjD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,eAAC,OAAA,CAAC,MAAA,CAAC,CAAC,KAAK,mCAAI,CAAC,CAAC,GAAG,CAAC,MAAA,CAAC,CAAC,KAAK,mCAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAA,EAAA,CAAC,CAAA;IACnE,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,MAAM,QAAQ,GAAI,KAAa,aAAb,KAAK,uBAAL,KAAK,CAAU,UAAU,CAAA;IAC3C,OAAO,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAChF,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AACxB,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,iBAAiB,CAAC,IAAY;;IAC5C,OAAO,CAAC,GAAG,CAAC,MAAA,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,mCAAI,EAAE,CAAC,CAAC,CAAA;AACxC,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,mBAAmB;IACjC,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAA;AACtB,CAAC;AAED,IAAI,KAAmC,CAAA;AACvC,iHAAiH;AACjH,MAAM,MAAM,GAAG,IAAI,GAAG,EAA+B,CAAA;AACrD,uFAAuF;AACvF,IAAI,WAA2E,CAAA;AAE/E,qGAAqG;AACrG,MAAM,KAAK,GAAG,GAAG,CAAA;AACjB,MAAM,SAAS,GAAG,CAAC,IAAY,EAAE,EAAU,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,CAAA;AAE/D,SAAS,gBAAgB;IACvB,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC7D,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1C,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAM,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;AACrF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA0B,EAAE,WAAwB,EAAE,MAAgB;;IACrG,iBAAiB,CAAC,MAAA,eAAe,CAAC,KAAK,CAAC,mCAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,CAAA;AAC7E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,KAA0B,EAC1B,WAAwB;AACxB,gGAAgG;AAChG,MAAgB;;IAEhB,MAAM,OAAO,GAAqB,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,EAAE,mCAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,EAAE,EAAE,CAAA;IAC3F,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;IAC3C,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IAClC,IAAI,WAAW;QAAE,oBAAoB,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;IAE/D,IAAI,IAAI,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAClC,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,OAAO,EAAE,CAAC;YAC5C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;YACpC,KAAK,iBAAiB,CAAC,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;YACpE,OAAM;QACR,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;AAC5B,CAAC;AAED,yFAAyF;AACzF,iBAAiB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE;;IACjC,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAA;IAEpC,IAAI,SAAS,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,IAAI,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,MAAK,SAAS,CAAC,IAAI,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,IAAI,gBAAgB,CAAC,KAAK,CAAC;YAAE,OAAM;QAClG,MAAM,KAAK,GAAG,MAAA,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,mCAAI,EAAE,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,CAAA;QACzF,IAAI,CAAC,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,EAAE,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IAClG,CAAC;SAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QACnC,KAAK,CAAC,KAAK,CAAC,CAAA;IACd,CAAC;AACH,CAAC,EAAE,KAAK,CAAC,CAAA;AAET,+GAA+G;AAC/G,MAAM,UAAU,cAAc;IAC5B,IAAI,gBAAgB,CAAC,KAAK,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,CAAA;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,KAAK,IAAI,gBAAgB,CAAC,KAAK,CAAC;QAAE,IAAI,CAAC,KAAK,CAAC,CAAA;AACnD,CAAC;AAGD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAqB,CAAA;AAE9C;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAA2B;IAC3D,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACvB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;AACzC,CAAC;AAED,SAAS,IAAI,CAAC,OAAqC;IACjD,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAA;AAClD,CAAC;AAED,SAAS,KAAK,CAAC,YAAqB;IAClC,IAAI,YAAY,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAClC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC9B,KAAK,YAAY,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAA;QAC/D,OAAM;IACR,CAAC;IACD,aAAa,CAAC,KAAK,CAAC,CAAA;IACpB,KAAK,GAAG,SAAS,CAAA;IACjB,eAAe,EAAE,CAAA;IACjB,IAAI,CAAC,SAAS,CAAC,CAAA;AACjB,CAAC;AAED,SAAS,IAAI,CAAC,OAAyB,EAAE,WAAwB;;IAC/D,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IAC/B,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACnC,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAA;IACpC,KAAK,GAAG,OAAO,CAAA;IAEf,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM;QACzB,CAAC,CAAC,IAAI,CAAA,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE;QACvD,CAAC,CAAC,IAAI,CAAA,mBAAmB,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,EAAE,YAAY,EAAE,oCAAoC,EAAE,CAAC,MAAM,CAAA;IAE5H,aAAa,CACX,KAAK,EACL;QACE,GAAG,EAAE,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;QAChC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAA,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,mCAAI,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,EAAE,mCAAI,EAAE,CAAC;QACxF,OAAO,EAAE,CAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,OAAO,EAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;QACjE,OAAO;QACP,MAAM,EAAE;YACN,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;YAC3D,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;YAC9D,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,iBAAiB,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;YACpE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;YAC7D,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;SACzD;QACD,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;QAC1B,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS;KAChE,EACD,WAAW,CACZ,CAAA;IAED,IAAI,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,gBAAgB;QAAE,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;;QACzE,eAAe,EAAE,CAAA;IAEtB,IAAI,CAAC,OAAO,CAAC,CAAA;AACf,CAAC;AAED,SAAS,YAAY;;IACnB,OAAO,MAAA,MAAA,MAAC,KAAK,CAAC,QAAQ,EAAU,0CAAE,KAAK,0CAAE,OAAO,mCAAI,EAAE,CAAA;AACxD,CAAC;AAED,SAAS,WAAW,CAAC,IAAkC;IACrD,MAAM,OAAO,GAAG,YAAY,EAAE,CAAA;IAC9B,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,IAAI,KAAK,QAAQ,CAAC,QAAQ;QAAE,WAAW,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAA;IAC/H,WAAW,CAAC,IAAI,GAAG,IAAI,CAAA;IACvB,KAAK,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;AAClF,CAAC;AAED,4FAA4F;AAC5F,SAAS,eAAe;IACtB,MAAM,KAAK,GAAG,WAAW,CAAA;IACzB,WAAW,GAAG,SAAS,CAAA;IACvB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,QAAQ;QAAE,OAAM;IAEtD,MAAM,OAAO,GAAG,YAAY,EAAE,CAAA;IAC9B,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK,CAAC,IAAI;QAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;AAC/H,CAAC","sourcesContent":["import { html, type TemplateResult } from 'lit'\nimport { i18next } from '@operato/i18n'\nimport { addressController, writeAddress } from '@operato/utils/address.js'\n\nimport { store, UPDATE_CONTEXT } from '@operato/shell'\n\nimport { showEntity360, hideEntity360, canGoBackEntity360, isEntity360Shown, navigateEntity360 } from './entity360.js'\n\nconst returnFocusByAddress = new Map<string, () => void>()\n\n/**\n * Which kinds of thing open a 360, and what each module puts in it (ADR-0067, the person 360 ruling).\n *\n * An entity's name is a link to its 360 (ADR-0067 decision 1), and a grid shows names by the dozen -\n * `updater` alone is a person column on 65 framework screens. Writing an opener into every column\n * would never finish. Instead a kind is declared once with the GraphQL `__typename`s it claims, and\n * whatever draws a value can ask `entity360KindOf(value)`: registered, it is a link; not, it stays\n * text, exactly as before.\n *\n * This file knows no product words and imports no product module. A kind brings its own title; each\n * module hangs its own section with its own read door (`registerEntity360Section`), the way activity\n * panels hang on the activity detail. Opening a kind with no section says so in one line - never a\n * blank dock.\n */\n\n/** The thing a 360 is about: its kind, its id, and the value it was opened from. */\nexport interface Entity360Subject {\n kind: string\n id: string\n value: Record<string, any>\n}\n\nexport interface Entity360KindDeclaration {\n /** The name sections register under - 'person', 'role', … */\n kind: string\n /** The GraphQL `__typename`s whose values open this kind. A typename belongs to one kind. */\n typenames: string[]\n /** The dock's title for a subject - what a person recognises it by. */\n title(subject: Entity360Subject): string\n /** A line under the title, if the kind has one. */\n context?(subject: Entity360Subject): string\n /**\n * The open 360 is what the page talks about: while it is open the page context's `subject` is\n * `{ type: kind, id }`, so the AI dock follows it (ai-assistant registerSubjectSurface). Closing\n * gives the page its own subject back. Off by default - a kind the assistant has no surface for\n * would only take the page's subject away.\n */\n assistantSubject?: boolean\n}\n\nexport interface Entity360Section {\n /** Unique within its kind. */\n name: string\n /** Lower comes first; equal orders keep the order they were registered in. */\n order?: number\n /** The section's content - usually the module's own element, which reads its own door. */\n render(subject: Entity360Subject): TemplateResult\n}\n\nexport type Entity360Rejection = { kind: string; name?: string; reason: string }\n\nconst kinds = new Map<string, Entity360KindDeclaration>()\nconst kindOfTypename = new Map<string, string>()\nconst sections = new Map<string, (Entity360Section & { at: number })[]>()\nconst rejected: Entity360Rejection[] = []\nlet registered = 0\n\n/** The dock has one owner for everything opened through this registry. */\nconst OWNER = {}\n\n/**\n * Declare a kind. A kind named twice, or a typename another kind already claims, is refused and\n * kept in `entity360Rejections()` - silently taking the second would reroute links nobody asked to move.\n */\nexport function registerEntity360Kind(declaration: Entity360KindDeclaration): void {\n const kind = String(declaration?.kind ?? '').trim()\n\n if (!kind) return void rejected.push({ kind: '(no kind)', reason: 'no-kind' })\n if (kinds.has(kind)) return void rejected.push({ kind, reason: 'duplicate-kind' })\n if (typeof declaration.title !== 'function') return void rejected.push({ kind, reason: 'no-title' })\n\n const taken = (declaration.typenames || []).find(typename => kindOfTypename.has(typename))\n if (taken) return void rejected.push({ kind, name: taken, reason: 'typename-claimed' })\n\n kinds.set(kind, { ...declaration, kind })\n for (const typename of declaration.typenames || []) kindOfTypename.set(typename, kind)\n}\n\n/** Hang a section on a kind. A name already on that kind is refused. */\nexport function registerEntity360Section(kind: string, section: Entity360Section): void {\n const name = String(section?.name ?? '').trim()\n\n if (!name) return void rejected.push({ kind, reason: 'no-name' })\n if (typeof section.render !== 'function') return void rejected.push({ kind, name, reason: 'no-render' })\n\n const list = sections.get(kind) ?? []\n if (list.some(one => one.name === name)) return void rejected.push({ kind, name, reason: 'duplicate-name' })\n\n list.push({ ...section, name, at: registered++ })\n list.sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.at - b.at)\n sections.set(kind, list)\n}\n\n/**\n * The kind a value opens, or `undefined`.\n *\n * `undefined` for anything not registered and for a value without `__typename` (a call that turned\n * the cache off) - the caller draws it as it always has.\n */\nexport function entity360KindOf(value: unknown): string | undefined {\n const typename = (value as any)?.__typename\n return typeof typename === 'string' ? kindOfTypename.get(typename) : undefined\n}\n\n/** Whether a kind was declared - an identifier of an undeclared kind stays text. */\nexport function entity360KindRegistered(kind: string): boolean {\n return kinds.has(kind)\n}\n\n/** The sections hung on a kind, in order. */\nexport function entity360Sections(kind: string): Entity360Section[] {\n return [...(sections.get(kind) ?? [])]\n}\n\n/** What was refused, and why - for a test or a boot check to read. */\nexport function entity360Rejections(): Entity360Rejection[] {\n return [...rejected]\n}\n\nlet shown: Entity360Subject | undefined\n/** Values opened in this page, by address - going back to one gives its section the value it was opened with. */\nconst opened = new Map<string, Record<string, any>>()\n/** The page's own subject while a 360 stands in for it, and the page it belongs to. */\nlet pageSubject: { path: string; subject: unknown; ours?: unknown } | undefined\n\n/** The address of a subject: `f=kind:id`. The id may hold colons (an EPC URN); the kind does not. */\nconst FOCUS = 'f'\nconst addressOf = (kind: string, id: string) => `${kind}:${id}`\n\nfunction subjectInAddress(): { kind: string; id: string } | undefined {\n const value = new URLSearchParams(location.search).get(FOCUS)\n const at = value ? value.indexOf(':') : -1\n return at > 0 ? { kind: value!.slice(0, at), id: value!.slice(at + 1) } : undefined\n}\n\n/**\n * Open the 360 of a value.\n *\n * A kind with sections shows them in order. A kind with none - or a value that opens no kind -\n * shows one line saying there is no 360 for it yet. The dock is never blank.\n */\nexport function openEntity360For(value: Record<string, any>, returnFocus?: () => void, source?: Element): void {\n openEntity360Kind(entity360KindOf(value) ?? '', value, returnFocus, source)\n}\n\n/**\n * Open the 360 of a kind directly - for a value known by its kind and id rather than by a GraphQL\n * typename: an identifier inside a row, or a relation inside another 360.\n *\n * Opening is a step in the address (`f=kind:id`, the same step twin's focus takes), so the dock's back\n * button and the browser's go to the 360 before, and a reload or a shared link opens the same one.\n */\nexport function openEntity360Kind(\n kind: string,\n value: Record<string, any>,\n returnFocus?: () => void,\n /** The element the request comes from; a link inside a popup names that popup as its origin. */\n source?: Element\n): void {\n const subject: Entity360Subject = { kind, id: String(value?.id ?? ''), value: value ?? {} }\n const address = addressOf(kind, subject.id)\n opened.set(address, subject.value)\n if (returnFocus) returnFocusByAddress.set(address, returnFocus)\n\n if (kind && subject.id) {\n const url = new URL(location.href)\n if (url.searchParams.get(FOCUS) !== address) {\n url.searchParams.set(FOCUS, address)\n void navigateEntity360(url.pathname + url.search + url.hash, source)\n return\n }\n }\n\n draw(subject, returnFocus)\n}\n\n/* Back and forward walk the address; the dock follows it and adds no step of its own. */\naddressController().subscribe(() => {\n const inAddress = subjectInAddress()\n\n if (inAddress && kinds.has(inAddress.kind)) {\n if (shown?.kind === inAddress.kind && shown.id === inAddress.id && isEntity360Shown(OWNER)) return\n const value = opened.get(addressOf(inAddress.kind, inAddress.id)) ?? { id: inAddress.id }\n draw({ ...inAddress, value }, returnFocusByAddress.get(addressOf(inAddress.kind, inAddress.id)))\n } else if (isEntity360Shown(OWNER)) {\n close(false)\n }\n}, false)\n\n/** Close the registry's 360 and take its step out of the address - for an app whose own state let go of it. */\nexport function closeEntity360(): void {\n if (isEntity360Shown(OWNER)) close(true)\n}\n\n/**\n * Draw the shown 360 again - its title and context are read anew. For a kind whose title comes from\n * what its section has just read.\n */\nexport function redrawEntity360(): void {\n if (shown && isEntity360Shown(OWNER)) draw(shown)\n}\n\ntype Entity360Listener = (subject: Entity360Subject | undefined) => void\nconst listeners = new Set<Entity360Listener>()\n\n/**\n * Hear which subject the registry's 360 shows - on open, on back or forward, and `undefined` on close.\n * An app with its own selection (twin's focus) follows the dock through this. Returns the unsubscribe.\n */\nexport function onEntity360Change(listener: Entity360Listener): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\nfunction tell(subject: Entity360Subject | undefined): void {\n listeners.forEach(listener => listener(subject))\n}\n\nfunction close(clearAddress: boolean): void {\n if (clearAddress && subjectInAddress()) {\n const url = new URL(location.href)\n url.searchParams.delete(FOCUS)\n void writeAddress(url, { replace: true, state: history.state })\n return\n }\n hideEntity360(OWNER)\n shown = undefined\n giveSubjectBack()\n tell(undefined)\n}\n\nfunction draw(subject: Entity360Subject, returnFocus?: () => void): void {\n const { kind, value } = subject\n const declaration = kinds.get(kind)\n const hung = entity360Sections(kind)\n shown = subject\n\n const content = hung.length\n ? html`${hung.map(section => section.render(subject))}`\n : html`<p data-not-yet>${i18next.t('text.entity360-not-yet', { defaultValue: 'there is no 360 for this kind yet.' })}</p>`\n\n showEntity360(\n OWNER,\n {\n key: addressOf(kind, subject.id),\n title: declaration ? declaration.title(subject) : String(value?.name ?? value?.id ?? ''),\n context: declaration?.context ? declaration.context(subject) : '',\n content,\n labels: {\n close: i18next.t('button.close', { defaultValue: 'close' }),\n expand: i18next.t('button.expand', { defaultValue: 'expand' }),\n collapse: i18next.t('button.collapse', { defaultValue: 'collapse' }),\n resize: i18next.t('label.resize', { defaultValue: 'resize' }),\n back: i18next.t('button.back', { defaultValue: 'back' })\n },\n onClose: () => close(true),\n onBack: canGoBackEntity360() ? () => history.back() : undefined\n },\n returnFocus\n )\n\n if (declaration?.assistantSubject) takeSubject({ type: kind, id: subject.id })\n else giveSubjectBack()\n\n tell(subject)\n}\n\nfunction routeContext(): any {\n return (store.getState() as any)?.route?.context ?? {}\n}\n\nfunction takeSubject(ours: { type: string; id: string }): void {\n const context = routeContext()\n if (!pageSubject || pageSubject.path !== location.pathname) pageSubject = { path: location.pathname, subject: context.subject }\n pageSubject.ours = ours\n store.dispatch({ type: UPDATE_CONTEXT, context: { ...context, subject: ours } })\n}\n\n/** Only on the same page, and only while the subject is still the one the 360 put there. */\nfunction giveSubjectBack(): void {\n const taken = pageSubject\n pageSubject = undefined\n if (!taken || taken.path !== location.pathname) return\n\n const context = routeContext()\n if (context.subject === taken.ours) store.dispatch({ type: UPDATE_CONTEXT, context: { ...context, subject: taken.subject } })\n}\n"]}
|
|
@@ -4,6 +4,9 @@ export declare function showEntity360(owner: object, view: Entity360View, return
|
|
|
4
4
|
/** Whether the dock is open for this owner - closed by its button, by another owner or by navigation, it is not. */
|
|
5
5
|
export declare function isEntity360Shown(owner: object): boolean;
|
|
6
6
|
export declare function hideEntity360(owner: object, restoreFocus?: boolean): void;
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Providers validate the canonical axis and read scope before calling this function. `source` is the element the
|
|
9
|
+
* request comes from: a link inside a popup names that popup as its origin.
|
|
10
|
+
*/
|
|
11
|
+
export declare function navigateEntity360(address: string, source?: Element): Promise<boolean>;
|
|
9
12
|
export declare function canGoBackEntity360(): boolean;
|
|
@@ -39,14 +39,17 @@ export function hideEntity360(owner, restoreFocus = true) {
|
|
|
39
39
|
if (restoreFocus)
|
|
40
40
|
queueMicrotask(() => { var _a; return (_a = previous.returnFocus) === null || _a === void 0 ? void 0 : _a.call(previous); });
|
|
41
41
|
}
|
|
42
|
-
/**
|
|
43
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Providers validate the canonical axis and read scope before calling this function. `source` is the element the
|
|
44
|
+
* request comes from: a link inside a popup names that popup as its origin.
|
|
45
|
+
*/
|
|
46
|
+
export function navigateEntity360(address, source) {
|
|
44
47
|
const url = new URL(address, location.href);
|
|
45
48
|
if (url.origin !== location.origin)
|
|
46
49
|
throw new Error('Entity 360 must stay within the application');
|
|
47
50
|
if (url.href === location.href)
|
|
48
51
|
return Promise.resolve(true);
|
|
49
|
-
return writeAddress(url, { state: { ...history.state, entity360Previous: location.href } });
|
|
52
|
+
return writeAddress(url, { state: { ...history.state, entity360Previous: location.href }, source });
|
|
50
53
|
}
|
|
51
54
|
export function canGoBackEntity360() {
|
|
52
55
|
var _a;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity360.js","sourceRoot":"","sources":["../../../src/actions/entity360.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC1B,OAAO,EAAE,KAAK,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAE/E,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAExD,MAAM,IAAI,GAAG,WAAW,CAAA;AACxB,IAAI,OAAqG,CAAA;AAEzG,mGAAmG;AACnG,KAAK,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,4BAA4B,EAAE,QAAQ,EAAE,GAAG,EAAE;QAClE,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,QAAQ;YAAE,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IACxF,CAAC,EAAE,CAAC,CAAA;AAEJ,+FAA+F;AAC/F,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,IAAmB,EAAE,WAAwB;IACxF,IAAI,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK,MAAK,KAAK,EAAE,CAAC;QAC7B,IAAI,OAAO;YAAE,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAChD,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,mBAAmB,CAAoB,CAAA;QAC3E,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAA;QAC/D,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,iBAAiB,CAAC,QAAQ;YAC/D,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAA,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;IAC1E,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QACxB,IAAI,WAAW;YAAE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAA;IACpD,CAAC;AACH,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK,MAAK,KAAK,CAAA;AACjC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,YAAY,GAAG,IAAI;IAC9D,IAAI,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK,MAAK,KAAK;QAAE,OAAM;IACpC,MAAM,QAAQ,GAAG,OAAO,CAAA;IACxB,OAAO,GAAG,SAAS,CAAA;IACnB,cAAc,CAAC,IAAI,CAAC,CAAA;IACpB,IAAI,YAAY;QAAE,cAAc,CAAC,GAAG,EAAE,WAAC,OAAA,MAAA,QAAQ,CAAC,WAAW,wDAAI,CAAA,EAAA,CAAC,CAAA;AAClE,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"entity360.js","sourceRoot":"","sources":["../../../src/actions/entity360.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit'\nimport { store, REGISTER_NAVIGATION_CALLBACK } from '@operato/shell'\nimport { appendViewpart, removeViewpart, VIEWPART_POSITION } from './layout.js'\nimport { OxEntity360Dock, type Entity360View } from '../components/ox-entity360-dock.js'\nimport { writeAddress } from '@operato/utils/address.js'\n\nconst NAME = 'entity360'\nlet current: { owner: object; dock: OxEntity360Dock; path: string; returnFocus?: () => void } | undefined\n\n// Cached pages may stay connected after navigation; their details must not leak into another task.\nstore.dispatch({ type: REGISTER_NAVIGATION_CALLBACK, callback: () => {\n if (current && current.path !== location.pathname) hideEntity360(current.owner, false)\n} })\n\n/** One dock per application, independent of product controllers and their render lifetimes. */\nexport function showEntity360(owner: object, view: Entity360View, returnFocus?: () => void): void {\n if (current?.owner !== owner) {\n if (current) hideEntity360(current.owner, false)\n const dock = document.createElement('ox-entity360-dock') as OxEntity360Dock\n current = { owner, dock, path: location.pathname, returnFocus }\n dock.view = view\n appendViewpart({ name: NAME, position: VIEWPART_POSITION.ASIDEBAR,\n viewpart: { show: true, persistent: true, template: html`${dock}` } })\n } else {\n current.dock.view = view\n if (returnFocus) current.returnFocus = returnFocus\n }\n}\n\n/** Whether the dock is open for this owner - closed by its button, by another owner or by navigation, it is not. */\nexport function isEntity360Shown(owner: object): boolean {\n return current?.owner === owner\n}\n\nexport function hideEntity360(owner: object, restoreFocus = true): void {\n if (current?.owner !== owner) return\n const previous = current\n current = undefined\n removeViewpart(NAME)\n if (restoreFocus) queueMicrotask(() => previous.returnFocus?.())\n}\n\n/**\n * Providers validate the canonical axis and read scope before calling this function. `source` is the element the\n * request comes from: a link inside a popup names that popup as its origin.\n */\nexport function navigateEntity360(address: string, source?: Element): Promise<boolean> {\n const url = new URL(address, location.href)\n if (url.origin !== location.origin) throw new Error('Entity 360 must stay within the application')\n if (url.href === location.href) return Promise.resolve(true)\n return writeAddress(url, { state: { ...history.state, entity360Previous: location.href }, source })\n}\n\nexport function canGoBackEntity360(): boolean {\n const previous = history.state?.entity360Previous\n if (typeof previous !== 'string') return false\n const url = new URL(previous, location.href)\n return url.pathname === location.pathname && url.searchParams.has('f')\n}\n"]}
|
|
@@ -67,5 +67,7 @@ export declare const UPDATE_VIEWPORT_WIDTH = "UPDATE_VIEWPORT_WIDTH";
|
|
|
67
67
|
export declare const updateLayout: (wide?: boolean) => void;
|
|
68
68
|
export declare const openOverlay: (name: string, options?: any, silent?: boolean) => void;
|
|
69
69
|
export declare const closeOverlay: (name: string) => boolean;
|
|
70
|
+
/** The overlay on top of the ones open now, if any - what a back control in the app's own bar closes. */
|
|
71
|
+
export declare const topOverlay: () => string | undefined;
|
|
70
72
|
export declare const toggleOverlay: (name: string, options: any) => void;
|
|
71
73
|
export declare const openPopup: <R = unknown>(template: TemplateResult, options?: PopupOptions) => import("@operato/popup").PopupHandle<R>;
|
|
@@ -91,7 +91,7 @@ addressController().subscribe((entry, before) => {
|
|
|
91
91
|
});
|
|
92
92
|
if (firstGone >= 0)
|
|
93
93
|
dismissOverlays(firstGone);
|
|
94
|
-
if (entry.url.
|
|
94
|
+
if (entry.url.pathname !== before.url.pathname || entry.intent === 'navigation')
|
|
95
95
|
dismissOverlays(0);
|
|
96
96
|
const overlay = (_a = entry.state) === null || _a === void 0 ? void 0 : _a.overlay;
|
|
97
97
|
if (!overlay || !pendingOverlays.has(overlay.name))
|
|
@@ -101,6 +101,14 @@ addressController().subscribe((entry, before) => {
|
|
|
101
101
|
store.dispatch({ type: UPDATE_VIEWPART, name: overlay.name, override: { show: true } });
|
|
102
102
|
syncModal();
|
|
103
103
|
}, false);
|
|
104
|
+
/* An overlay hidden by any other way (a viewpart update, its removal) is closed: the stack is what is on screen. */
|
|
105
|
+
store.subscribe(() => {
|
|
106
|
+
var _a;
|
|
107
|
+
const viewparts = ((_a = store.getState().layout) === null || _a === void 0 ? void 0 : _a.viewparts) || {};
|
|
108
|
+
const hidden = overlays.find(overlay => { var _a; return !((_a = viewparts[overlay.name]) === null || _a === void 0 ? void 0 : _a.show); });
|
|
109
|
+
if (hidden)
|
|
110
|
+
closeOverlay(hidden.name);
|
|
111
|
+
});
|
|
104
112
|
document.addEventListener('keydown', event => {
|
|
105
113
|
const top = overlays[overlays.length - 1];
|
|
106
114
|
if ((top === null || top === void 0 ? void 0 : top.escapable) && (event.key === 'Escape' || event.key === 'Esc')) {
|
|
@@ -141,9 +149,14 @@ export const closeOverlay = (name) => {
|
|
|
141
149
|
}
|
|
142
150
|
const overlay = overlays[index];
|
|
143
151
|
dismissOverlays(index);
|
|
144
|
-
|
|
152
|
+
// Back to the entry under the overlay only when nothing with another address was written above it. If something
|
|
153
|
+
// was (a filter while the AI dock was open), the overlay is only hidden: going back would undo that write.
|
|
154
|
+
if (addressController().unchangedSince(overlay.key))
|
|
155
|
+
addressController().traverseTo(overlay.belowKey);
|
|
145
156
|
return true;
|
|
146
157
|
};
|
|
158
|
+
/** The overlay on top of the ones open now, if any - what a back control in the app's own bar closes. */
|
|
159
|
+
export const topOverlay = () => { var _a; return (_a = overlays[overlays.length - 1]) === null || _a === void 0 ? void 0 : _a.name; };
|
|
147
160
|
export const toggleOverlay = (name, options) => {
|
|
148
161
|
var _a;
|
|
149
162
|
var beforeOverlayName = (_a = overlays[overlays.length - 1]) === null || _a === void 0 ? void 0 : _a.name;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/actions/layout.ts"],"names":[],"mappings":"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","sourcesContent":["import { TemplateResult } from 'lit'\n\nimport { openPopup as openPopupWithResult, PopupOptions, setOpenPopupImplementation } from '@operato/popup'\nimport { PopupImplementationHandle } from '@operato/popup/implementation.js'\nimport { store } from '@operato/shell'\nimport { addressController } from '@operato/utils/address.js'\n\nexport type Viewpart = {\n hovering?: 'center' | 'edge' | 'next'\n show?: boolean\n size?: string\n title?: string\n help?: string\n level?: VIEWPART_LEVEL\n closable?: boolean\n search?: {\n value?: string\n handler?: (closure: any, value: string) => void\n placeholder?: string\n }\n filter?: {\n handler?: (closure: any) => void\n }\n backdrop?: boolean\n temporary?: boolean /* auto remove */\n persistent?: boolean /* application-wide dock remains available in fullbleed pages */\n resizable?: boolean\n movable?: boolean\n template?: TemplateResult\n position?: VIEWPART_POSITION\n templateProperties?: any\n zIndex?: number\n}\n\nexport type AppendViewpartAction = {\n name: string\n viewpart: Viewpart\n position: VIEWPART_POSITION\n}\n\nexport const appendViewpart = ({ name, viewpart, position }: AppendViewpartAction) => {\n store.dispatch({\n type: APPEND_VIEWPART,\n name,\n viewpart: {\n ...viewpart,\n show: viewpart.hovering && viewpart.show ? false : viewpart.show\n },\n position\n })\n\n if (viewpart.hovering && viewpart.show) {\n openOverlay(name)\n }\n}\n\nexport const removeViewpart = (name: string) => {\n store.dispatch({\n type: REMOVE_VIEWPART,\n name\n })\n}\n\nexport const updateViewpart = (name: string, override: any) => {\n store.dispatch({\n type: UPDATE_VIEWPART,\n name,\n override\n })\n}\n\nexport const APPEND_VIEWPART = 'APPEND_VIEWPART'\nexport const REMOVE_VIEWPART = 'REMOVE_VIEWPART'\nexport const UPDATE_VIEWPART = 'UPDATE_VIEWPART'\n\nexport enum VIEWPART_POSITION {\n HEADERBAR = 'headerbar',\n NAVBAR = 'navbar',\n ASIDEBAR = 'asidebar',\n FOOTERBAR = 'footerbar',\n PAGE_HEADERBAR = 'page-headerbar',\n PAGE_FOOTERBAR = 'page-footerbar'\n}\n\n/**\n * 각 뷰파트 스택의 순서를 정하는 속성이다.\n * 뷰파트의 level 속성에 정의되도록 한다.\n * 맨 위/아래/좌측/우측에 보여져야하는 뷰파트는 TOPMOST 속성을 갖도록 하고,\n * 쌓인 순서대로 보여지는 뷰파트는 NORMAL 속성을 갖도록 한다.\n * default 속성은 NORMAL이다.\n */\nexport enum VIEWPART_LEVEL {\n TOPMOST = 'TOPMOST',\n NORMAL = 'NORMAL'\n}\n\nexport enum TOOL_POSITION {\n FRONT_END = 'FRONT_END',\n FRONT = 'FRONT',\n CENTER = 'CENTER',\n REAR = 'REAR',\n REAR_END = 'REAR_END'\n}\n\nexport const UPDATE_VIEWPORT_WIDTH = 'UPDATE_VIEWPORT_WIDTH'\n\nexport const updateLayout = (wide?: boolean) => {\n store.dispatch({\n type: UPDATE_VIEWPORT_WIDTH,\n width: wide ? 'WIDE' : 'NARROW'\n })\n}\n\n/* overlay navigation */\nvar overlaySequence = 0\nconst pendingOverlays = new Map<string, AbortController>()\nconst overlays: { name: string; key: string; belowKey: string; escapable: boolean }[] = []\n\nfunction syncModal() {\n const modal = [...overlays]\n .reverse()\n .find(overlay => (store.getState() as any).layout?.viewparts?.[overlay.name]?.backdrop)\n addressController().setModal(modal ? { name: modal.name, close: () => dismissOverlays(0) } : undefined)\n}\n\nfunction dismissOverlays(from: number) {\n for (const overlay of overlays.splice(from)) {\n store.dispatch({ type: UPDATE_VIEWPART, name: overlay.name, override: { show: false } })\n }\n syncModal()\n}\n\naddressController().subscribe((entry, before) => {\n const firstGone = overlays.findIndex(overlay => {\n const index = addressController().indexOf(overlay.key)\n return index < 0 || index > entry.index\n })\n if (firstGone >= 0) dismissOverlays(firstGone)\n if (entry.url.href !== before.url.href) dismissOverlays(0)\n const overlay = (entry.state as any)?.overlay\n if (!overlay || !pendingOverlays.has(overlay.name)) return\n pendingOverlays.delete(overlay.name)\n overlays.push({ ...overlay, key: entry.key, belowKey: before.key })\n store.dispatch({ type: UPDATE_VIEWPART, name: overlay.name, override: { show: true } })\n syncModal()\n}, false)\n\ndocument.addEventListener('keydown', event => {\n const top = overlays[overlays.length - 1]\n if (top?.escapable && (event.key === 'Escape' || event.key === 'Esc')) {\n event.preventDefault()\n closeOverlay(top.name)\n }\n})\n\nexport const openOverlay = (name: string, options?: any, silent?: boolean) => {\n /* store의 layout의 내용을 변경한다. */\n if (!silent && options) {\n store.dispatch({\n type: UPDATE_VIEWPART,\n name,\n override: options\n })\n }\n\n const request = new AbortController()\n pendingOverlays.set(name, request)\n void addressController()\n .pushOverlay(\n {\n ...(history.state || {}),\n overlay: { name, sequence: ++overlaySequence, escapable: options?.escapable !== false }\n },\n request.signal\n )\n .then(accepted => {\n if (accepted) return\n pendingOverlays.delete(name)\n store.dispatch({ type: UPDATE_VIEWPART, name, override: { show: false } })\n document.dispatchEvent(new CustomEvent('overlay-closed', { detail: name }))\n })\n}\n\nexport const closeOverlay = (name: string) => {\n const index = overlays.findIndex(overlay => overlay.name === name)\n if (index < 0) {\n const pending = pendingOverlays.get(name)\n pending?.abort()\n return !!pending\n }\n const overlay = overlays[index]\n dismissOverlays(index)\n addressController().traverseTo(overlay.belowKey)\n return true\n}\n\nexport const toggleOverlay = (name: string, options: any) => {\n var beforeOverlayName = overlays[overlays.length - 1]?.name\n\n if (beforeOverlayName == name) {\n closeOverlay(name)\n } else {\n openOverlay(name, options)\n }\n}\n\n/*\n * popup handling\n *\n * popup은 overlay의 특별한 형태이다.\n * popup은 open될 때, viewpart를 append 하며, close 될 때 viewpart를 remove 한다.\n * - name: '$popup-${popupSequence}'\n * - position: VIEWPART_POSITION_HEADERBAR\n * - hovering: 'center' | 'next' | 'edge'\n */\nvar popupSequence = 0\n\n/**\n * open popup in operato application environment\n *\n * @param {*} template html template to be rendered inside the popup\n * @param {PopupOptions} options\n * @returns the implementation handle. Callers get the full handle (with `result`) from `openPopup`.\n */\nconst openPopupInLayout = (template: TemplateResult, options: PopupOptions = {}): PopupImplementationHandle => {\n var name = `$popup-${popupSequence++}`\n\n appendViewpart({\n name,\n viewpart: {\n hovering: 'center',\n closable: true,\n movable: true,\n ...options,\n backdrop: true,\n show: false,\n temporary: true /* auto remove */,\n template\n },\n position: VIEWPART_POSITION.HEADERBAR\n })\n\n openOverlay(name, options, true)\n\n /* Typed, not cast: a cast would let a close() that forgets to answer compile. */\n var popup: PopupImplementationHandle = {\n name,\n close: () => {\n if (overlays[overlays.length - 1]?.name !== name && !pendingOverlays.has(name)) return false\n return closeOverlay(name)\n },\n closed: false\n }\n\n document.addEventListener('overlay-closed', function listener(e) {\n if (name == (e as CustomEvent).detail) {\n popup.closed = true\n popup.onclosed && popup.onclosed()\n document.removeEventListener('overlay-closed', listener)\n }\n })\n\n return popup\n}\n\nsetOpenPopupImplementation(openPopupInLayout)\n\n/*\n * The popup package's openPopup adds `result` and `close(result)` on top of whichever implementation is\n * registered, so the handle is built in one place. Re-exported here because most callers import\n * openPopup from @operato/layout.\n */\nexport const openPopup = openPopupWithResult\n"]}
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/actions/layout.ts"],"names":[],"mappings":"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","sourcesContent":["import { TemplateResult } from 'lit'\n\nimport { openPopup as openPopupWithResult, PopupOptions, setOpenPopupImplementation } from '@operato/popup'\nimport { PopupImplementationHandle } from '@operato/popup/implementation.js'\nimport { store } from '@operato/shell'\nimport { addressController } from '@operato/utils/address.js'\n\nexport type Viewpart = {\n hovering?: 'center' | 'edge' | 'next'\n show?: boolean\n size?: string\n title?: string\n help?: string\n level?: VIEWPART_LEVEL\n closable?: boolean\n search?: {\n value?: string\n handler?: (closure: any, value: string) => void\n placeholder?: string\n }\n filter?: {\n handler?: (closure: any) => void\n }\n backdrop?: boolean\n temporary?: boolean /* auto remove */\n persistent?: boolean /* application-wide dock remains available in fullbleed pages */\n resizable?: boolean\n movable?: boolean\n template?: TemplateResult\n position?: VIEWPART_POSITION\n templateProperties?: any\n zIndex?: number\n}\n\nexport type AppendViewpartAction = {\n name: string\n viewpart: Viewpart\n position: VIEWPART_POSITION\n}\n\nexport const appendViewpart = ({ name, viewpart, position }: AppendViewpartAction) => {\n store.dispatch({\n type: APPEND_VIEWPART,\n name,\n viewpart: {\n ...viewpart,\n show: viewpart.hovering && viewpart.show ? false : viewpart.show\n },\n position\n })\n\n if (viewpart.hovering && viewpart.show) {\n openOverlay(name)\n }\n}\n\nexport const removeViewpart = (name: string) => {\n store.dispatch({\n type: REMOVE_VIEWPART,\n name\n })\n}\n\nexport const updateViewpart = (name: string, override: any) => {\n store.dispatch({\n type: UPDATE_VIEWPART,\n name,\n override\n })\n}\n\nexport const APPEND_VIEWPART = 'APPEND_VIEWPART'\nexport const REMOVE_VIEWPART = 'REMOVE_VIEWPART'\nexport const UPDATE_VIEWPART = 'UPDATE_VIEWPART'\n\nexport enum VIEWPART_POSITION {\n HEADERBAR = 'headerbar',\n NAVBAR = 'navbar',\n ASIDEBAR = 'asidebar',\n FOOTERBAR = 'footerbar',\n PAGE_HEADERBAR = 'page-headerbar',\n PAGE_FOOTERBAR = 'page-footerbar'\n}\n\n/**\n * 각 뷰파트 스택의 순서를 정하는 속성이다.\n * 뷰파트의 level 속성에 정의되도록 한다.\n * 맨 위/아래/좌측/우측에 보여져야하는 뷰파트는 TOPMOST 속성을 갖도록 하고,\n * 쌓인 순서대로 보여지는 뷰파트는 NORMAL 속성을 갖도록 한다.\n * default 속성은 NORMAL이다.\n */\nexport enum VIEWPART_LEVEL {\n TOPMOST = 'TOPMOST',\n NORMAL = 'NORMAL'\n}\n\nexport enum TOOL_POSITION {\n FRONT_END = 'FRONT_END',\n FRONT = 'FRONT',\n CENTER = 'CENTER',\n REAR = 'REAR',\n REAR_END = 'REAR_END'\n}\n\nexport const UPDATE_VIEWPORT_WIDTH = 'UPDATE_VIEWPORT_WIDTH'\n\nexport const updateLayout = (wide?: boolean) => {\n store.dispatch({\n type: UPDATE_VIEWPORT_WIDTH,\n width: wide ? 'WIDE' : 'NARROW'\n })\n}\n\n/* overlay navigation */\nvar overlaySequence = 0\nconst pendingOverlays = new Map<string, AbortController>()\nconst overlays: { name: string; key: string; belowKey: string; escapable: boolean }[] = []\n\nfunction syncModal() {\n const modal = [...overlays]\n .reverse()\n .find(overlay => (store.getState() as any).layout?.viewparts?.[overlay.name]?.backdrop)\n addressController().setModal(modal ? { name: modal.name, close: () => dismissOverlays(0) } : undefined)\n}\n\nfunction dismissOverlays(from: number) {\n for (const overlay of overlays.splice(from)) {\n store.dispatch({ type: UPDATE_VIEWPART, name: overlay.name, override: { show: false } })\n }\n syncModal()\n}\n\naddressController().subscribe((entry, before) => {\n const firstGone = overlays.findIndex(overlay => {\n const index = addressController().indexOf(overlay.key)\n return index < 0 || index > entry.index\n })\n if (firstGone >= 0) dismissOverlays(firstGone)\n if (entry.url.pathname !== before.url.pathname || entry.intent === 'navigation') dismissOverlays(0)\n const overlay = (entry.state as any)?.overlay\n if (!overlay || !pendingOverlays.has(overlay.name)) return\n pendingOverlays.delete(overlay.name)\n overlays.push({ ...overlay, key: entry.key, belowKey: before.key })\n store.dispatch({ type: UPDATE_VIEWPART, name: overlay.name, override: { show: true } })\n syncModal()\n}, false)\n\n/* An overlay hidden by any other way (a viewpart update, its removal) is closed: the stack is what is on screen. */\nstore.subscribe(() => {\n const viewparts = (store.getState() as any).layout?.viewparts || {}\n const hidden = overlays.find(overlay => !viewparts[overlay.name]?.show)\n if (hidden) closeOverlay(hidden.name)\n})\n\ndocument.addEventListener('keydown', event => {\n const top = overlays[overlays.length - 1]\n if (top?.escapable && (event.key === 'Escape' || event.key === 'Esc')) {\n event.preventDefault()\n closeOverlay(top.name)\n }\n})\n\nexport const openOverlay = (name: string, options?: any, silent?: boolean) => {\n /* store의 layout의 내용을 변경한다. */\n if (!silent && options) {\n store.dispatch({\n type: UPDATE_VIEWPART,\n name,\n override: options\n })\n }\n\n const request = new AbortController()\n pendingOverlays.set(name, request)\n void addressController()\n .pushOverlay(\n {\n ...(history.state || {}),\n overlay: { name, sequence: ++overlaySequence, escapable: options?.escapable !== false }\n },\n request.signal\n )\n .then(accepted => {\n if (accepted) return\n pendingOverlays.delete(name)\n store.dispatch({ type: UPDATE_VIEWPART, name, override: { show: false } })\n document.dispatchEvent(new CustomEvent('overlay-closed', { detail: name }))\n })\n}\n\nexport const closeOverlay = (name: string) => {\n const index = overlays.findIndex(overlay => overlay.name === name)\n if (index < 0) {\n const pending = pendingOverlays.get(name)\n pending?.abort()\n return !!pending\n }\n const overlay = overlays[index]\n dismissOverlays(index)\n // Back to the entry under the overlay only when nothing with another address was written above it. If something\n // was (a filter while the AI dock was open), the overlay is only hidden: going back would undo that write.\n if (addressController().unchangedSince(overlay.key)) addressController().traverseTo(overlay.belowKey)\n return true\n}\n\n/** The overlay on top of the ones open now, if any - what a back control in the app's own bar closes. */\nexport const topOverlay = (): string | undefined => overlays[overlays.length - 1]?.name\n\nexport const toggleOverlay = (name: string, options: any) => {\n var beforeOverlayName = overlays[overlays.length - 1]?.name\n\n if (beforeOverlayName == name) {\n closeOverlay(name)\n } else {\n openOverlay(name, options)\n }\n}\n\n/*\n * popup handling\n *\n * popup은 overlay의 특별한 형태이다.\n * popup은 open될 때, viewpart를 append 하며, close 될 때 viewpart를 remove 한다.\n * - name: '$popup-${popupSequence}'\n * - position: VIEWPART_POSITION_HEADERBAR\n * - hovering: 'center' | 'next' | 'edge'\n */\nvar popupSequence = 0\n\n/**\n * open popup in operato application environment\n *\n * @param {*} template html template to be rendered inside the popup\n * @param {PopupOptions} options\n * @returns the implementation handle. Callers get the full handle (with `result`) from `openPopup`.\n */\nconst openPopupInLayout = (template: TemplateResult, options: PopupOptions = {}): PopupImplementationHandle => {\n var name = `$popup-${popupSequence++}`\n\n appendViewpart({\n name,\n viewpart: {\n hovering: 'center',\n closable: true,\n movable: true,\n ...options,\n backdrop: true,\n show: false,\n temporary: true /* auto remove */,\n template\n },\n position: VIEWPART_POSITION.HEADERBAR\n })\n\n openOverlay(name, options, true)\n\n /* Typed, not cast: a cast would let a close() that forgets to answer compile. */\n var popup: PopupImplementationHandle = {\n name,\n close: () => {\n if (overlays[overlays.length - 1]?.name !== name && !pendingOverlays.has(name)) return false\n return closeOverlay(name)\n },\n closed: false\n }\n\n document.addEventListener('overlay-closed', function listener(e) {\n if (name == (e as CustomEvent).detail) {\n popup.closed = true\n popup.onclosed && popup.onclosed()\n document.removeEventListener('overlay-closed', listener)\n }\n })\n\n return popup\n}\n\nsetOpenPopupImplementation(openPopupInLayout)\n\n/*\n * The popup package's openPopup adds `result` and `close(result)` on top of whichever implementation is\n * registered, so the handle is built in one place. Re-exported here because most callers import\n * openPopup from @operato/layout.\n */\nexport const openPopup = openPopupWithResult\n"]}
|
|
@@ -86,7 +86,7 @@ let OxUrlTabs = class OxUrlTabs extends LitElement {
|
|
|
86
86
|
return;
|
|
87
87
|
const first = (_a = this.tabs[0]) === null || _a === void 0 ? void 0 : _a.key;
|
|
88
88
|
if (this.base) {
|
|
89
|
-
await navigate(key === first ? this.base : `${this.base}/${key}
|
|
89
|
+
await navigate(key === first ? this.base : `${this.base}/${key}`, false, this);
|
|
90
90
|
}
|
|
91
91
|
else {
|
|
92
92
|
const url = new URL(location.href);
|
|
@@ -94,7 +94,7 @@ let OxUrlTabs = class OxUrlTabs extends LitElement {
|
|
|
94
94
|
url.searchParams.delete(this.param);
|
|
95
95
|
else
|
|
96
96
|
url.searchParams.set(this.param, key);
|
|
97
|
-
await writeAddress(url, { replace: true, state: history.state });
|
|
97
|
+
await writeAddress(url, { replace: true, state: history.state, source: this, intent: 'state' });
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
announce() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ox-url-tabs.js","sourceRoot":"","sources":["../../../src/components/ox-url-tabs.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { navigate } from '@operato/shell'\nimport { addressController, writeAddress } from '@operato/utils/address.js'\n\nexport interface OxUrlTab {\n key: string\n label: string\n}\n\n/**\n * Tabs whose choice lives in the address (ADR-0060 decision 8 addendum, the third frame).\n *\n * plant's quality and pending lists and its equipment screen keep their tab in the address, each its\n * own way; twin's business screen reads the tab from the address on entry and never writes it back, so\n * a copied link opens on the first tab. Here reading and writing always go together: open the link,\n * press back after opening a row, reload - the same tab.\n *\n * - `?tab=` (the `param` name) is where new screens keep it. The first tab is the default and is not\n * written, so a screen's plain address stays plain. Other query values are kept as they are.\n * The change replaces the address rather than adding a history step - back leaves the screen, it\n * does not walk through the tabs.\n * - `base` puts the key in a path segment instead (`<base>/<key>`), through the shell's navigate, the\n * way plant's three screens do. **Transitional** (architect ruling 2026-09-24): it is here so those\n * screens can take the frame without breaking the links people have; new screens use `?tab=`.\n * - `tab-change` (detail `{ key }`) fires when the tab changes - by a press, or by back / forward.\n * `selected` holds the current key, read from the address when the element connects.\n *\n * The look is the suite's tab strip (SurfaceStyles `.ui-tabs`): an underline under the chosen one.\n */\n@customElement('ox-url-tabs')\nexport class OxUrlTabs extends LitElement {\n static styles = css`\n :host {\n display: flex;\n gap: var(--spacing-large, 16px);\n border-bottom: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n }\n button {\n border: none;\n border-bottom: 2px solid transparent;\n background: none;\n padding: var(--spacing-medium, 8px) 2px calc(var(--spacing-medium, 8px) - 2px);\n font: inherit;\n font-size: 14px;\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n cursor: pointer;\n }\n button:hover:not([aria-selected='true']) {\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n }\n button[aria-selected='true'] {\n font-weight: 600;\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n border-bottom-color: var(--ui-accent, var(--md-sys-color-primary));\n }\n button:focus-visible {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 2px;\n }\n `\n\n @property({ attribute: false }) tabs: OxUrlTab[] = []\n /** The query name the key is kept under. */\n @property() param = 'tab'\n /** Transitional: keep the key in a path segment after this page instead of the query. */\n @property() base?: string\n\n @state() selected = ''\n\n private stopAddress?: () => void\n private stopTab?: () => void\n\n connectedCallback() {\n super.connectedCallback()\n this.stopTab = addressController().registerTab(this.param)\n const path = location.pathname\n this.stopAddress = addressController().subscribe(entry => {\n if (entry.url.pathname === path || this.base) this.read(true)\n }, false)\n this.read(false)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.stopAddress?.()\n this.stopTab?.()\n }\n\n updated(changed: Map<string, unknown>) {\n if (changed.has('param')) {\n this.stopTab?.()\n this.stopTab = addressController().registerTab(this.param)\n }\n if (changed.has('tabs') || changed.has('base') || changed.has('param')) this.read(false)\n }\n\n /** The key the address names, or the first tab when it names none of them. */\n keyInAddress(): string {\n const keys = this.tabs.map(tab => tab.key)\n let named: string | null = null\n\n if (this.base) {\n const segments = location.pathname.split('/').filter(Boolean)\n const at = segments.lastIndexOf(this.base)\n named = at >= 0 ? (segments[at + 1] ?? null) : null\n } else {\n named = new URLSearchParams(location.search).get(this.param)\n }\n\n return named && keys.includes(named) ? named : (keys[0] ?? '')\n }\n\n private read(announce: boolean) {\n const key = this.keyInAddress()\n if (key === this.selected) return\n this.selected = key\n if (announce) this.announce()\n }\n\n /** Choose a tab: the address follows, and the screen hears it. */\n async select(key: string) {\n if (key === this.selected || !this.tabs.some(tab => tab.key === key)) return\n const first = this.tabs[0]?.key\n\n if (this.base) {\n await navigate(key === first ? this.base : `${this.base}/${key}`)\n } else {\n const url = new URL(location.href)\n if (key === first) url.searchParams.delete(this.param)\n else url.searchParams.set(this.param, key)\n await writeAddress(url, { replace: true, state: history.state })\n }\n }\n\n private announce() {\n this.dispatchEvent(new CustomEvent('tab-change', { detail: { key: this.selected }, bubbles: true, composed: true }))\n }\n\n private onKey(event: KeyboardEvent) {\n const keys = this.tabs.map(tab => tab.key)\n const at = keys.indexOf(this.selected)\n const step = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0\n if (!step || at < 0) return\n event.preventDefault()\n const next = keys[(at + step + keys.length) % keys.length]\n this.select(next)\n this.updateComplete.then(() => this.renderRoot.querySelector<HTMLButtonElement>(`[data-key=\"${next}\"]`)?.focus())\n }\n\n render() {\n return html`<div role=\"tablist\" style=\"display: contents\" @keydown=${this.onKey}>\n ${this.tabs.map(\n tab => html`<button\n role=\"tab\"\n data-key=${tab.key}\n aria-selected=${String(tab.key === this.selected)}\n tabindex=${tab.key === this.selected ? 0 : -1}\n @click=${() => this.select(tab.key)}\n >\n ${tab.label}\n </button>`\n )}\n </div>`\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ox-url-tabs': OxUrlTabs\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ox-url-tabs.js","sourceRoot":"","sources":["../../../src/components/ox-url-tabs.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { navigate } from '@operato/shell'\nimport { addressController, writeAddress } from '@operato/utils/address.js'\n\nexport interface OxUrlTab {\n key: string\n label: string\n}\n\n/**\n * Tabs whose choice lives in the address (ADR-0060 decision 8 addendum, the third frame).\n *\n * plant's quality and pending lists and its equipment screen keep their tab in the address, each its\n * own way; twin's business screen reads the tab from the address on entry and never writes it back, so\n * a copied link opens on the first tab. Here reading and writing always go together: open the link,\n * press back after opening a row, reload - the same tab.\n *\n * - `?tab=` (the `param` name) is where new screens keep it. The first tab is the default and is not\n * written, so a screen's plain address stays plain. Other query values are kept as they are.\n * The change replaces the address rather than adding a history step - back leaves the screen, it\n * does not walk through the tabs.\n * - `base` puts the key in a path segment instead (`<base>/<key>`), through the shell's navigate, the\n * way plant's three screens do. **Transitional** (architect ruling 2026-09-24): it is here so those\n * screens can take the frame without breaking the links people have; new screens use `?tab=`.\n * - `tab-change` (detail `{ key }`) fires when the tab changes - by a press, or by back / forward.\n * `selected` holds the current key, read from the address when the element connects.\n *\n * The look is the suite's tab strip (SurfaceStyles `.ui-tabs`): an underline under the chosen one.\n */\n@customElement('ox-url-tabs')\nexport class OxUrlTabs extends LitElement {\n static styles = css`\n :host {\n display: flex;\n gap: var(--spacing-large, 16px);\n border-bottom: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n }\n button {\n border: none;\n border-bottom: 2px solid transparent;\n background: none;\n padding: var(--spacing-medium, 8px) 2px calc(var(--spacing-medium, 8px) - 2px);\n font: inherit;\n font-size: 14px;\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n cursor: pointer;\n }\n button:hover:not([aria-selected='true']) {\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n }\n button[aria-selected='true'] {\n font-weight: 600;\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n border-bottom-color: var(--ui-accent, var(--md-sys-color-primary));\n }\n button:focus-visible {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 2px;\n }\n `\n\n @property({ attribute: false }) tabs: OxUrlTab[] = []\n /** The query name the key is kept under. */\n @property() param = 'tab'\n /** Transitional: keep the key in a path segment after this page instead of the query. */\n @property() base?: string\n\n @state() selected = ''\n\n private stopAddress?: () => void\n private stopTab?: () => void\n\n connectedCallback() {\n super.connectedCallback()\n this.stopTab = addressController().registerTab(this.param)\n const path = location.pathname\n this.stopAddress = addressController().subscribe(entry => {\n if (entry.url.pathname === path || this.base) this.read(true)\n }, false)\n this.read(false)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.stopAddress?.()\n this.stopTab?.()\n }\n\n updated(changed: Map<string, unknown>) {\n if (changed.has('param')) {\n this.stopTab?.()\n this.stopTab = addressController().registerTab(this.param)\n }\n if (changed.has('tabs') || changed.has('base') || changed.has('param')) this.read(false)\n }\n\n /** The key the address names, or the first tab when it names none of them. */\n keyInAddress(): string {\n const keys = this.tabs.map(tab => tab.key)\n let named: string | null = null\n\n if (this.base) {\n const segments = location.pathname.split('/').filter(Boolean)\n const at = segments.lastIndexOf(this.base)\n named = at >= 0 ? (segments[at + 1] ?? null) : null\n } else {\n named = new URLSearchParams(location.search).get(this.param)\n }\n\n return named && keys.includes(named) ? named : (keys[0] ?? '')\n }\n\n private read(announce: boolean) {\n const key = this.keyInAddress()\n if (key === this.selected) return\n this.selected = key\n if (announce) this.announce()\n }\n\n /** Choose a tab: the address follows, and the screen hears it. */\n async select(key: string) {\n if (key === this.selected || !this.tabs.some(tab => tab.key === key)) return\n const first = this.tabs[0]?.key\n\n if (this.base) {\n await navigate(key === first ? this.base : `${this.base}/${key}`, false, this)\n } else {\n const url = new URL(location.href)\n if (key === first) url.searchParams.delete(this.param)\n else url.searchParams.set(this.param, key)\n await writeAddress(url, { replace: true, state: history.state, source: this, intent: 'state' })\n }\n }\n\n private announce() {\n this.dispatchEvent(new CustomEvent('tab-change', { detail: { key: this.selected }, bubbles: true, composed: true }))\n }\n\n private onKey(event: KeyboardEvent) {\n const keys = this.tabs.map(tab => tab.key)\n const at = keys.indexOf(this.selected)\n const step = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0\n if (!step || at < 0) return\n event.preventDefault()\n const next = keys[(at + step + keys.length) % keys.length]\n this.select(next)\n this.updateComplete.then(() => this.renderRoot.querySelector<HTMLButtonElement>(`[data-key=\"${next}\"]`)?.focus())\n }\n\n render() {\n return html`<div role=\"tablist\" style=\"display: contents\" @keydown=${this.onKey}>\n ${this.tabs.map(\n tab => html`<button\n role=\"tab\"\n data-key=${tab.key}\n aria-selected=${String(tab.key === this.selected)}\n tabindex=${tab.key === this.selected ? 0 : -1}\n @click=${() => this.select(tab.key)}\n >\n ${tab.label}\n </button>`\n )}\n </div>`\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ox-url-tabs': OxUrlTabs\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { expect, fixture, waitUntil } from '@open-wc/testing';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { addressController, writeAddress } from '@operato/utils/address.js';
|
|
4
|
+
import { closeOverlay, openOverlay, appendViewpart, VIEWPART_POSITION } from '../src/index.js';
|
|
5
|
+
/*
|
|
6
|
+
* Review probe (2026-10-19): a non-modal overlay (the AI dock) stays open while the list query changes; closing it
|
|
7
|
+
* afterwards must not undo that change.
|
|
8
|
+
*/
|
|
9
|
+
describe('closing a non-modal overlay after a later write', () => {
|
|
10
|
+
it('keeps the write made while it was open', async () => {
|
|
11
|
+
await fixture(html `<ox-header-bar></ox-header-bar>`);
|
|
12
|
+
addressController().registerQuery();
|
|
13
|
+
expect(await writeAddress('?filters=%5B%5D&probe=before')).to.equal(true);
|
|
14
|
+
appendViewpart({
|
|
15
|
+
name: 'probe-dock',
|
|
16
|
+
viewpart: { hovering: 'edge', show: false, backdrop: false, template: html `<div>dock</div>` },
|
|
17
|
+
position: VIEWPART_POSITION.HEADERBAR
|
|
18
|
+
});
|
|
19
|
+
openOverlay('probe-dock', { backdrop: false });
|
|
20
|
+
await waitUntil(() => { var _a, _b; return ((_b = (_a = history.state) === null || _a === void 0 ? void 0 : _a.overlay) === null || _b === void 0 ? void 0 : _b.name) === 'probe-dock'; }, 'dock not open');
|
|
21
|
+
await addressController().whenSettled();
|
|
22
|
+
expect(await writeAddress('?filters=%5B%5D&probe=after')).to.equal(true);
|
|
23
|
+
closeOverlay('probe-dock');
|
|
24
|
+
await addressController().whenSettled();
|
|
25
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
26
|
+
expect(new URLSearchParams(location.search).get('probe')).to.equal('after');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=overlay-close-after-write.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay-close-after-write.test.js","sourceRoot":"","sources":["../../test/overlay-close-after-write.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC1B,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC3E,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAE9F;;;GAGG;AAEH,QAAQ,CAAC,iDAAiD,EAAE,GAAG,EAAE;IAC/D,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,OAAO,CAAC,IAAI,CAAA,iCAAiC,CAAC,CAAA;QACpD,iBAAiB,EAAE,CAAC,aAAa,EAAE,CAAA;QACnC,MAAM,CAAC,MAAM,YAAY,CAAC,8BAA8B,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACzE,cAAc,CAAC;YACb,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAA,iBAAiB,EAAE;YAC7F,QAAQ,EAAE,iBAAiB,CAAC,SAAS;SACtC,CAAC,CAAA;QACF,WAAW,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;QAC9C,MAAM,SAAS,CAAC,GAAG,EAAE,eAAC,OAAA,CAAA,MAAA,MAAC,OAAO,CAAC,KAAa,0CAAE,OAAO,0CAAE,IAAI,MAAK,YAAY,CAAA,EAAA,EAAE,eAAe,CAAC,CAAA;QAC9F,MAAM,iBAAiB,EAAE,CAAC,WAAW,EAAE,CAAA;QAEvC,MAAM,CAAC,MAAM,YAAY,CAAC,6BAA6B,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACxE,YAAY,CAAC,YAAY,CAAC,CAAA;QAC1B,MAAM,iBAAiB,EAAE,CAAC,WAAW,EAAE,CAAA;QACvC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;QACtD,MAAM,CAAC,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC7E,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import { expect, fixture, waitUntil } from '@open-wc/testing'\nimport { html } from 'lit'\nimport { addressController, writeAddress } from '@operato/utils/address.js'\nimport { closeOverlay, openOverlay, appendViewpart, VIEWPART_POSITION } from '../src/index.js'\n\n/*\n * Review probe (2026-10-19): a non-modal overlay (the AI dock) stays open while the list query changes; closing it\n * afterwards must not undo that change.\n */\n\ndescribe('closing a non-modal overlay after a later write', () => {\n it('keeps the write made while it was open', async () => {\n await fixture(html`<ox-header-bar></ox-header-bar>`)\n addressController().registerQuery()\n expect(await writeAddress('?filters=%5B%5D&probe=before')).to.equal(true)\n appendViewpart({\n name: 'probe-dock',\n viewpart: { hovering: 'edge', show: false, backdrop: false, template: html`<div>dock</div>` },\n position: VIEWPART_POSITION.HEADERBAR\n })\n openOverlay('probe-dock', { backdrop: false })\n await waitUntil(() => (history.state as any)?.overlay?.name === 'probe-dock', 'dock not open')\n await addressController().whenSettled()\n\n expect(await writeAddress('?filters=%5B%5D&probe=after')).to.equal(true)\n closeOverlay('probe-dock')\n await addressController().whenSettled()\n await new Promise(resolve => setTimeout(resolve, 100))\n expect(new URLSearchParams(location.search).get('probe')).to.equal('after')\n })\n})\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { expect, fixture, waitUntil } from '@open-wc/testing';
|
|
2
|
+
import { sendMouse } from '@web/test-runner-commands';
|
|
3
|
+
import { html } from 'lit';
|
|
4
|
+
import { addressController, writeAddress } from '@operato/utils/address.js';
|
|
5
|
+
import { store } from '@operato/shell';
|
|
6
|
+
import { closeOverlay, openPopup, UPDATE_VIEWPART } from '../src/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Before 2026-10-17 a button in a popup that navigated went where it said and the popup closed; a hidden
|
|
9
|
+
* overlay held nothing back. These pin that behaviour.
|
|
10
|
+
*/
|
|
11
|
+
const overlays = () => document.querySelector('ox-header-bar').shadowRoot.querySelectorAll('ox-floating-overlay');
|
|
12
|
+
const click = async (element) => {
|
|
13
|
+
const bounds = element.getBoundingClientRect();
|
|
14
|
+
await sendMouse({
|
|
15
|
+
type: 'click',
|
|
16
|
+
position: [Math.round(bounds.x + bounds.width / 2), Math.round(bounds.y + bounds.height / 2)]
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const shown = async (element) => {
|
|
20
|
+
await waitUntil(() => element.isConnected && element.getBoundingClientRect().width > 0, 'popup not shown');
|
|
21
|
+
await addressController().whenSettled();
|
|
22
|
+
};
|
|
23
|
+
describe('requests from inside a popup', () => {
|
|
24
|
+
beforeEach(async () => {
|
|
25
|
+
await fixture(html `<ox-header-bar></ox-header-bar>`);
|
|
26
|
+
});
|
|
27
|
+
afterEach(async () => {
|
|
28
|
+
for (const overlay of Array.from(overlays()).reverse())
|
|
29
|
+
closeOverlay(overlay.name);
|
|
30
|
+
await addressController().whenSettled();
|
|
31
|
+
await waitUntil(() => overlays().length === 0, 'a popup stayed open after the test');
|
|
32
|
+
});
|
|
33
|
+
it('a button in a popup that navigates closes the popup and replaces its entry', async () => {
|
|
34
|
+
expect(await writeAddress('?under=popup')).to.equal(true);
|
|
35
|
+
const under = location.href;
|
|
36
|
+
const button = document.createElement('button');
|
|
37
|
+
button.textContent = 'open elsewhere';
|
|
38
|
+
let request;
|
|
39
|
+
button.addEventListener('click', () => (request = writeAddress('?destination=1', { source: button, intent: 'navigation' })));
|
|
40
|
+
openPopup(html `${button}`, { backdrop: true });
|
|
41
|
+
await shown(button);
|
|
42
|
+
await click(button);
|
|
43
|
+
expect(await request).to.equal(true);
|
|
44
|
+
expect(location.search).to.equal('?destination=1');
|
|
45
|
+
await waitUntil(() => overlays().length === 0, 'popup left open');
|
|
46
|
+
history.back();
|
|
47
|
+
await waitUntil(() => location.href === under, "back did not arrive");
|
|
48
|
+
expect(location.href).to.equal(under);
|
|
49
|
+
});
|
|
50
|
+
it('a popup hidden through the store is closed: it no longer holds requests back', async () => {
|
|
51
|
+
const content = document.createElement('div');
|
|
52
|
+
content.textContent = 'printing';
|
|
53
|
+
const handle = openPopup(html `${content}`, { backdrop: true });
|
|
54
|
+
await shown(content);
|
|
55
|
+
store.dispatch({ type: UPDATE_VIEWPART, name: handle.name, override: { show: false } });
|
|
56
|
+
await addressController().whenSettled();
|
|
57
|
+
expect(await writeAddress('?after=hidden')).to.equal(true);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=popup-navigation.test.js.map
|