@happyvertical/smrt-svelte 0.55.0 → 0.55.2
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/README.md +38 -6
- package/agents/components.md +44 -1
- package/agents/workspace.md +77 -2
- package/dist/components/app/AppShell.svelte +122 -24
- package/dist/components/app/AppShell.svelte.d.ts +25 -2
- package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
- package/dist/components/app/DockToggles.svelte +103 -0
- package/dist/components/app/DockToggles.svelte.d.ts +8 -0
- package/dist/components/app/DockToggles.svelte.d.ts.map +1 -0
- package/dist/components/app/dock-toggle.d.ts +14 -0
- package/dist/components/app/dock-toggle.d.ts.map +1 -0
- package/dist/components/app/dock-toggle.js +1 -0
- package/dist/components/app/index.d.ts +2 -0
- package/dist/components/app/index.d.ts.map +1 -1
- package/dist/components/app/index.js +1 -0
- package/dist/components/board/Board.svelte +92 -394
- package/dist/components/board/Board.svelte.d.ts.map +1 -1
- package/dist/components/forms/RelationInput.svelte +379 -0
- package/dist/components/forms/RelationInput.svelte.d.ts +75 -0
- package/dist/components/forms/RelationInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/index.d.ts +2 -1
- package/dist/components/forms/index.d.ts.map +1 -1
- package/dist/components/forms/index.js +1 -0
- package/dist/components/forms/types.d.ts +9 -0
- package/dist/components/forms/types.d.ts.map +1 -1
- package/dist/components/sortable/Sortable.svelte +359 -0
- package/dist/components/sortable/Sortable.svelte.d.ts +26 -0
- package/dist/components/sortable/Sortable.svelte.d.ts.map +1 -0
- package/dist/components/sortable/controller.svelte.d.ts +152 -0
- package/dist/components/sortable/controller.svelte.d.ts.map +1 -0
- package/dist/components/sortable/controller.svelte.js +453 -0
- package/dist/components/sortable/index.d.ts +5 -0
- package/dist/components/sortable/index.d.ts.map +1 -0
- package/dist/components/sortable/index.js +2 -0
- package/dist/components/sortable/types.d.ts +80 -0
- package/dist/components/sortable/types.d.ts.map +1 -0
- package/dist/components/sortable/types.js +1 -0
- package/dist/components/workspace/admin-shell/AdminShell.svelte +62 -3
- package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts +5 -0
- package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/PhoneTopBar.svelte +2 -1
- package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte +196 -0
- package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts +17 -0
- package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/TenantNav.svelte +69 -3
- package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/dock.d.ts +37 -0
- package/dist/components/workspace/admin-shell/dock.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/dock.js +25 -0
- package/dist/components/workspace/admin-shell/layout-context.d.ts +7 -0
- package/dist/components/workspace/admin-shell/layout-context.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/layout-context.js +17 -0
- package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts +69 -0
- package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/layout-controller.svelte.js +91 -0
- package/dist/components/workspace/admin-shell/layout.d.ts +120 -0
- package/dist/components/workspace/admin-shell/layout.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/layout.js +402 -0
- package/dist/components/workspace/admin-shell/settings.d.ts +6 -4
- package/dist/components/workspace/admin-shell/settings.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/settings.js +14 -4
- package/dist/components/workspace/admin-shell/state.svelte.d.ts +59 -1
- package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/state.svelte.js +146 -2
- package/dist/components/workspace/admin-shell/types.d.ts +52 -0
- package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
- package/dist/components/workspace/index.d.ts +6 -1
- package/dist/components/workspace/index.d.ts.map +1 -1
- package/dist/components/workspace/index.js +5 -0
- package/dist/i18n/server.d.ts +1 -0
- package/dist/i18n/server.d.ts.map +1 -1
- package/dist/i18n/server.js +1 -0
- package/dist/i18n/strings.sortable.d.ts +13 -0
- package/dist/i18n/strings.sortable.d.ts.map +1 -0
- package/dist/i18n/strings.sortable.js +13 -0
- package/dist/i18n/strings.workspace.d.ts +13 -0
- package/dist/i18n/strings.workspace.d.ts.map +1 -1
- package/dist/i18n/strings.workspace.js +13 -0
- package/package.json +26 -15
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-customizable shell layout: a small, versioned, JSON-serializable delta
|
|
3
|
+
* applied over the navigation and panel defaults the host generates.
|
|
4
|
+
*
|
|
5
|
+
* This module is pure (no Svelte, no DOM) so a host can validate, store,
|
|
6
|
+
* export, or apply a layout anywhere, including on the server. It is also
|
|
7
|
+
* published as `@happyvertical/smrt-svelte/workspace/layout`.
|
|
8
|
+
*
|
|
9
|
+
* Ids: sections are identified by `ShellNavGroup.id ?? heading`, items by
|
|
10
|
+
* `ShellNavItem.id ?? href`. Sections and items share one id namespace (the
|
|
11
|
+
* `hidden` list holds both), so keep them unique; later duplicates get a
|
|
12
|
+
* `#2`, `#3`, ... suffix. The flat `nav` items form an implicit first
|
|
13
|
+
* section, {@link SHELL_NAV_ROOT_SECTION_ID}, which always renders first and
|
|
14
|
+
* cannot be reordered or hidden as a whole.
|
|
15
|
+
*/
|
|
16
|
+
import type { PanelEdge, ShellNavGroup, ShellNavItem, ShellPanelDefaults } from './types.js';
|
|
17
|
+
/** The only layout format version this package reads and writes. */
|
|
18
|
+
export declare const SHELL_LAYOUT_VERSION: 1;
|
|
19
|
+
/** Section id of the flat `nav` items rendered above all groups. */
|
|
20
|
+
export declare const SHELL_NAV_ROOT_SECTION_ID = "@root";
|
|
21
|
+
/** A user's choice for one edge panel. */
|
|
22
|
+
export interface ShellLayoutPanel {
|
|
23
|
+
/** `false` hides the edge; absent or `true` keeps it as configured. */
|
|
24
|
+
visible?: boolean;
|
|
25
|
+
/** The state the edge starts in. */
|
|
26
|
+
initial?: 'collapsed' | 'expanded';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A user's customization of the shell, stored as a sparse delta over what the
|
|
30
|
+
* host generates. Every field is optional; unknown ids are ignored.
|
|
31
|
+
*/
|
|
32
|
+
export interface ShellLayout {
|
|
33
|
+
version: 1;
|
|
34
|
+
/** Section ids in display order. Sections not listed keep their slot. */
|
|
35
|
+
sectionOrder?: string[];
|
|
36
|
+
/** Per section id, item ids in display order. */
|
|
37
|
+
itemOrder?: Record<string, string[]>;
|
|
38
|
+
/** Ids of hidden sections and items. */
|
|
39
|
+
hidden?: string[];
|
|
40
|
+
/** Item id to the id of the section it was moved into. */
|
|
41
|
+
moved?: Record<string, string>;
|
|
42
|
+
/** Per edge panel overrides. */
|
|
43
|
+
panels?: Partial<Record<PanelEdge, ShellLayoutPanel>>;
|
|
44
|
+
}
|
|
45
|
+
/** An item as the layout sees it. */
|
|
46
|
+
export interface ShellNavModelItem {
|
|
47
|
+
id: string;
|
|
48
|
+
item: ShellNavItem;
|
|
49
|
+
/** The section the host put the item in. */
|
|
50
|
+
nativeSectionId: string;
|
|
51
|
+
/** The section it is displayed in (differs when moved). */
|
|
52
|
+
sectionId: string;
|
|
53
|
+
/** Hidden by its own id. */
|
|
54
|
+
hidden: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** A section as the layout sees it. */
|
|
57
|
+
export interface ShellNavModelSection {
|
|
58
|
+
id: string;
|
|
59
|
+
/** `null` for the implicit root section of flat `nav` items. */
|
|
60
|
+
group: ShellNavGroup | null;
|
|
61
|
+
heading: string | null;
|
|
62
|
+
hidden: boolean;
|
|
63
|
+
/** Items in display order, including hidden ones. */
|
|
64
|
+
items: ShellNavModelItem[];
|
|
65
|
+
}
|
|
66
|
+
/** Result of {@link applyShellLayout}. */
|
|
67
|
+
export interface AppliedShellLayout {
|
|
68
|
+
nav: ShellNavItem[];
|
|
69
|
+
groups: ShellNavGroup[];
|
|
70
|
+
panels: ShellPanelDefaults;
|
|
71
|
+
}
|
|
72
|
+
export declare function createShellLayout(): ShellLayout;
|
|
73
|
+
/** A layout with no customization (nothing to apply). */
|
|
74
|
+
export declare function isShellLayoutEmpty(layout: ShellLayout | null | undefined): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Read an untrusted value (stored JSON, an imported blueprint) as a layout.
|
|
77
|
+
* Never throws: anything unreadable, including a version this package does not
|
|
78
|
+
* know, yields an empty layout, and unknown or malformed fields are dropped.
|
|
79
|
+
*/
|
|
80
|
+
export declare function normalizeShellLayout(input: unknown): ShellLayout;
|
|
81
|
+
export declare function shellNavGroupId(group: ShellNavGroup): string;
|
|
82
|
+
export declare function shellNavItemId(item: ShellNavItem): string;
|
|
83
|
+
/**
|
|
84
|
+
* The navigation as the layout sees it: every section and item (hidden ones
|
|
85
|
+
* flagged, not removed) in display order. An editor renders this; the shell
|
|
86
|
+
* renders {@link applyShellLayout}'s filtered result.
|
|
87
|
+
*/
|
|
88
|
+
export declare function resolveShellNavModel(nav?: readonly ShellNavItem[], groups?: readonly ShellNavGroup[], layout?: ShellLayout | null): ShellNavModelSection[];
|
|
89
|
+
/** The state an edge starts in once a layout override is applied. */
|
|
90
|
+
export declare function applyShellLayoutPanel(base: ShellPanelDefaults[PanelEdge], panel: ShellLayoutPanel | undefined): ShellPanelDefaults[PanelEdge];
|
|
91
|
+
/**
|
|
92
|
+
* Apply a layout over host-generated navigation and panel defaults.
|
|
93
|
+
*
|
|
94
|
+
* Pure and total: ids the host no longer has are ignored, items and sections
|
|
95
|
+
* the layout does not mention keep their default positions, and an empty or
|
|
96
|
+
* missing layout returns the inputs unchanged. Sections the layout empties
|
|
97
|
+
* (every item hidden or moved away) are dropped from `groups`.
|
|
98
|
+
*/
|
|
99
|
+
export declare function applyShellLayout(nav: ShellNavItem[], groups: ShellNavGroup[], panels: ShellPanelDefaults | undefined, layout: ShellLayout | null | undefined): AppliedShellLayout;
|
|
100
|
+
/**
|
|
101
|
+
* Move a group section to `toIndex` among the groups (the root section is not
|
|
102
|
+
* counted). Returns the layout unchanged when `sectionId` is not a group.
|
|
103
|
+
*/
|
|
104
|
+
export declare function moveShellSection(nav: readonly ShellNavItem[], groups: readonly ShellNavGroup[], layout: ShellLayout | null | undefined, sectionId: string, toIndex: number): ShellLayout;
|
|
105
|
+
/**
|
|
106
|
+
* Move an item into `toSectionId` (a group or the root section) at `toIndex`
|
|
107
|
+
* among that section's items, hidden ones included. `toIndex` defaults to the
|
|
108
|
+
* end. Returns the layout unchanged when either id is unknown.
|
|
109
|
+
*/
|
|
110
|
+
export declare function moveShellItem(nav: readonly ShellNavItem[], groups: readonly ShellNavGroup[], layout: ShellLayout | null | undefined, itemId: string, toSectionId: string, toIndex?: number): ShellLayout;
|
|
111
|
+
/** Hide a section or item. Unknown ids and the root section are ignored. */
|
|
112
|
+
export declare function hideShellEntry(nav: readonly ShellNavItem[], groups: readonly ShellNavGroup[], layout: ShellLayout | null | undefined, id: string): ShellLayout;
|
|
113
|
+
/** Show a hidden section or item again. */
|
|
114
|
+
export declare function showShellEntry(layout: ShellLayout | null | undefined, id: string): ShellLayout;
|
|
115
|
+
/**
|
|
116
|
+
* Set an edge panel's `visible` and/or `initial`. Values equal to the host's
|
|
117
|
+
* defaults (`defaults`, when given) are dropped to keep the layout sparse.
|
|
118
|
+
*/
|
|
119
|
+
export declare function setShellLayoutPanel(layout: ShellLayout | null | undefined, edge: PanelEdge, patch: ShellLayoutPanel, defaults?: ShellPanelDefaults): ShellLayout;
|
|
120
|
+
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/layout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/** The only layout format version this package reads and writes. */
|
|
2
|
+
export const SHELL_LAYOUT_VERSION = 1;
|
|
3
|
+
/** Section id of the flat `nav` items rendered above all groups. */
|
|
4
|
+
export const SHELL_NAV_ROOT_SECTION_ID = '@root';
|
|
5
|
+
const EDGES = ['top', 'left', 'right', 'bottom'];
|
|
6
|
+
export function createShellLayout() {
|
|
7
|
+
return { version: SHELL_LAYOUT_VERSION };
|
|
8
|
+
}
|
|
9
|
+
/** A layout with no customization (nothing to apply). */
|
|
10
|
+
export function isShellLayoutEmpty(layout) {
|
|
11
|
+
if (!layout)
|
|
12
|
+
return true;
|
|
13
|
+
return (!layout.sectionOrder?.length &&
|
|
14
|
+
!hasKeys(layout.itemOrder) &&
|
|
15
|
+
!layout.hidden?.length &&
|
|
16
|
+
!hasKeys(layout.moved) &&
|
|
17
|
+
!hasKeys(layout.panels));
|
|
18
|
+
}
|
|
19
|
+
function hasKeys(value) {
|
|
20
|
+
return (typeof value === 'object' && value !== null && Object.keys(value).length > 0);
|
|
21
|
+
}
|
|
22
|
+
function isRecord(value) {
|
|
23
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
24
|
+
}
|
|
25
|
+
function stringList(value) {
|
|
26
|
+
if (!Array.isArray(value))
|
|
27
|
+
return undefined;
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
for (const entry of value) {
|
|
30
|
+
if (typeof entry === 'string' && entry !== '')
|
|
31
|
+
seen.add(entry);
|
|
32
|
+
}
|
|
33
|
+
return seen.size > 0 ? [...seen] : undefined;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Read an untrusted value (stored JSON, an imported blueprint) as a layout.
|
|
37
|
+
* Never throws: anything unreadable, including a version this package does not
|
|
38
|
+
* know, yields an empty layout, and unknown or malformed fields are dropped.
|
|
39
|
+
*/
|
|
40
|
+
export function normalizeShellLayout(input) {
|
|
41
|
+
const layout = createShellLayout();
|
|
42
|
+
if (!isRecord(input) || input.version !== SHELL_LAYOUT_VERSION)
|
|
43
|
+
return layout;
|
|
44
|
+
const sectionOrder = stringList(input.sectionOrder);
|
|
45
|
+
if (sectionOrder)
|
|
46
|
+
layout.sectionOrder = sectionOrder;
|
|
47
|
+
if (isRecord(input.itemOrder)) {
|
|
48
|
+
const itemOrder = {};
|
|
49
|
+
for (const [section, ids] of Object.entries(input.itemOrder)) {
|
|
50
|
+
const list = stringList(ids);
|
|
51
|
+
if (list)
|
|
52
|
+
itemOrder[section] = list;
|
|
53
|
+
}
|
|
54
|
+
if (hasKeys(itemOrder))
|
|
55
|
+
layout.itemOrder = itemOrder;
|
|
56
|
+
}
|
|
57
|
+
const hidden = stringList(input.hidden);
|
|
58
|
+
if (hidden)
|
|
59
|
+
layout.hidden = hidden;
|
|
60
|
+
if (isRecord(input.moved)) {
|
|
61
|
+
const moved = {};
|
|
62
|
+
for (const [id, section] of Object.entries(input.moved)) {
|
|
63
|
+
if (typeof section === 'string' && section !== '')
|
|
64
|
+
moved[id] = section;
|
|
65
|
+
}
|
|
66
|
+
if (hasKeys(moved))
|
|
67
|
+
layout.moved = moved;
|
|
68
|
+
}
|
|
69
|
+
if (isRecord(input.panels)) {
|
|
70
|
+
const panels = {};
|
|
71
|
+
for (const edge of EDGES) {
|
|
72
|
+
const raw = input.panels[edge];
|
|
73
|
+
if (!isRecord(raw))
|
|
74
|
+
continue;
|
|
75
|
+
const panel = {};
|
|
76
|
+
if (typeof raw.visible === 'boolean')
|
|
77
|
+
panel.visible = raw.visible;
|
|
78
|
+
if (raw.initial === 'collapsed' || raw.initial === 'expanded') {
|
|
79
|
+
panel.initial = raw.initial;
|
|
80
|
+
}
|
|
81
|
+
if (hasKeys(panel))
|
|
82
|
+
panels[edge] = panel;
|
|
83
|
+
}
|
|
84
|
+
if (hasKeys(panels))
|
|
85
|
+
layout.panels = panels;
|
|
86
|
+
}
|
|
87
|
+
return layout;
|
|
88
|
+
}
|
|
89
|
+
export function shellNavGroupId(group) {
|
|
90
|
+
return group.id ?? group.heading;
|
|
91
|
+
}
|
|
92
|
+
export function shellNavItemId(item) {
|
|
93
|
+
return item.id ?? item.href;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Keep every listed id that is present, in the listed order, in the slots the
|
|
97
|
+
* listed ids already occupy. Ids the list does not mention (for example new
|
|
98
|
+
* ones the host added later) stay exactly where the defaults put them.
|
|
99
|
+
*/
|
|
100
|
+
function mergeOrder(defaults, preferred) {
|
|
101
|
+
if (!preferred || preferred.length === 0)
|
|
102
|
+
return [...defaults];
|
|
103
|
+
const present = new Set(defaults);
|
|
104
|
+
const listed = [];
|
|
105
|
+
const listedSet = new Set();
|
|
106
|
+
for (const id of preferred) {
|
|
107
|
+
if (present.has(id) && !listedSet.has(id)) {
|
|
108
|
+
listedSet.add(id);
|
|
109
|
+
listed.push(id);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (listed.length === 0)
|
|
113
|
+
return [...defaults];
|
|
114
|
+
let next = 0;
|
|
115
|
+
return defaults.map((id) => (listedSet.has(id) ? listed[next++] : id));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The navigation as the layout sees it: every section and item (hidden ones
|
|
119
|
+
* flagged, not removed) in display order. An editor renders this; the shell
|
|
120
|
+
* renders {@link applyShellLayout}'s filtered result.
|
|
121
|
+
*/
|
|
122
|
+
export function resolveShellNavModel(nav = [], groups = [], layout) {
|
|
123
|
+
const used = new Set([SHELL_NAV_ROOT_SECTION_ID]);
|
|
124
|
+
const unique = (id) => {
|
|
125
|
+
let candidate = id;
|
|
126
|
+
for (let n = 2; used.has(candidate); n += 1)
|
|
127
|
+
candidate = `${id}#${n}`;
|
|
128
|
+
used.add(candidate);
|
|
129
|
+
return candidate;
|
|
130
|
+
};
|
|
131
|
+
const natives = [
|
|
132
|
+
{
|
|
133
|
+
id: SHELL_NAV_ROOT_SECTION_ID,
|
|
134
|
+
group: null,
|
|
135
|
+
items: nav.map((item) => ({ id: unique(shellNavItemId(item)), item })),
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
for (const group of groups) {
|
|
139
|
+
const id = unique(shellNavGroupId(group));
|
|
140
|
+
natives.push({
|
|
141
|
+
id,
|
|
142
|
+
group,
|
|
143
|
+
items: group.items.map((item) => ({
|
|
144
|
+
id: unique(shellNavItemId(item)),
|
|
145
|
+
item,
|
|
146
|
+
})),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const hidden = new Set(stringList(layout?.hidden) ?? []);
|
|
150
|
+
const moved = isRecord(layout?.moved)
|
|
151
|
+
? layout?.moved
|
|
152
|
+
: {};
|
|
153
|
+
const sectionIds = new Set(natives.map((section) => section.id));
|
|
154
|
+
const itemIds = new Set(natives.flatMap((section) => section.items.map((entry) => entry.id)));
|
|
155
|
+
/** Where an item is displayed: its moved-to section when that exists. */
|
|
156
|
+
const displayedIn = (id, native) => {
|
|
157
|
+
const target = itemIds.has(id) ? moved[id] : undefined;
|
|
158
|
+
return typeof target === 'string' && sectionIds.has(target)
|
|
159
|
+
? target
|
|
160
|
+
: native;
|
|
161
|
+
};
|
|
162
|
+
const groupSections = natives.slice(1).map((section) => section.id);
|
|
163
|
+
const orderedGroupIds = mergeOrder(groupSections, stringList(layout?.sectionOrder));
|
|
164
|
+
const byId = new Map(natives.map((section) => [section.id, section]));
|
|
165
|
+
const orderedSections = [
|
|
166
|
+
natives[0],
|
|
167
|
+
...orderedGroupIds.map((id) => byId.get(id)),
|
|
168
|
+
];
|
|
169
|
+
const itemOrder = isRecord(layout?.itemOrder)
|
|
170
|
+
? layout?.itemOrder
|
|
171
|
+
: {};
|
|
172
|
+
return orderedSections.map((section) => {
|
|
173
|
+
const defaults = [];
|
|
174
|
+
for (const native of natives) {
|
|
175
|
+
for (const entry of native.items) {
|
|
176
|
+
if (displayedIn(entry.id, native.id) !== section.id)
|
|
177
|
+
continue;
|
|
178
|
+
defaults.push({
|
|
179
|
+
id: entry.id,
|
|
180
|
+
item: entry.item,
|
|
181
|
+
nativeSectionId: native.id,
|
|
182
|
+
sectionId: section.id,
|
|
183
|
+
hidden: hidden.has(entry.id),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Items the host placed here keep their order; moved-in items follow.
|
|
188
|
+
defaults.sort((a, b) => Number(a.nativeSectionId !== section.id) -
|
|
189
|
+
Number(b.nativeSectionId !== section.id));
|
|
190
|
+
const byItem = new Map(defaults.map((entry) => [entry.id, entry]));
|
|
191
|
+
const order = mergeOrder(defaults.map((entry) => entry.id), stringList(itemOrder[section.id]));
|
|
192
|
+
return {
|
|
193
|
+
id: section.id,
|
|
194
|
+
group: section.group,
|
|
195
|
+
heading: section.group ? section.group.heading : null,
|
|
196
|
+
hidden: section.group ? hidden.has(section.id) : false,
|
|
197
|
+
items: order.map((id) => byItem.get(id)),
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/** The state an edge starts in once a layout override is applied. */
|
|
202
|
+
export function applyShellLayoutPanel(base, panel) {
|
|
203
|
+
if (!panel)
|
|
204
|
+
return base;
|
|
205
|
+
// `false` and `initial: 'hidden'` mean the host has no such panel; a
|
|
206
|
+
// layout can hide a panel but never conjures one the host removed.
|
|
207
|
+
if (base === false || base?.initial === 'hidden')
|
|
208
|
+
return base;
|
|
209
|
+
if (panel.visible === false)
|
|
210
|
+
return false;
|
|
211
|
+
if (panel.initial === 'collapsed' || panel.initial === 'expanded') {
|
|
212
|
+
return { ...base, initial: panel.initial };
|
|
213
|
+
}
|
|
214
|
+
return base;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Apply a layout over host-generated navigation and panel defaults.
|
|
218
|
+
*
|
|
219
|
+
* Pure and total: ids the host no longer has are ignored, items and sections
|
|
220
|
+
* the layout does not mention keep their default positions, and an empty or
|
|
221
|
+
* missing layout returns the inputs unchanged. Sections the layout empties
|
|
222
|
+
* (every item hidden or moved away) are dropped from `groups`.
|
|
223
|
+
*/
|
|
224
|
+
export function applyShellLayout(nav, groups, panels, layout) {
|
|
225
|
+
const basePanels = panels ?? {};
|
|
226
|
+
if (isShellLayoutEmpty(layout)) {
|
|
227
|
+
return { nav, groups, panels: basePanels };
|
|
228
|
+
}
|
|
229
|
+
const model = resolveShellNavModel(nav, groups, layout);
|
|
230
|
+
const visible = (section) => section.hidden
|
|
231
|
+
? []
|
|
232
|
+
: section.items.filter((entry) => !entry.hidden).map((e) => e.item);
|
|
233
|
+
const [root, ...sections] = model;
|
|
234
|
+
const nextGroups = [];
|
|
235
|
+
for (const section of sections) {
|
|
236
|
+
const items = visible(section);
|
|
237
|
+
const group = section.group;
|
|
238
|
+
if (section.hidden || (items.length === 0 && group.items.length > 0)) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
nextGroups.push({ ...group, items });
|
|
242
|
+
}
|
|
243
|
+
const nextPanels = { ...basePanels };
|
|
244
|
+
const overrides = isRecord(layout?.panels) ? layout?.panels : undefined;
|
|
245
|
+
for (const edge of EDGES) {
|
|
246
|
+
const override = overrides?.[edge];
|
|
247
|
+
if (!isRecord(override))
|
|
248
|
+
continue;
|
|
249
|
+
const next = applyShellLayoutPanel(basePanels[edge], override);
|
|
250
|
+
if (next !== undefined)
|
|
251
|
+
nextPanels[edge] = next;
|
|
252
|
+
}
|
|
253
|
+
return { nav: visible(root), groups: nextGroups, panels: nextPanels };
|
|
254
|
+
}
|
|
255
|
+
// Mutations. Each returns a new sparse layout and never mutates its input.
|
|
256
|
+
function compact(layout) {
|
|
257
|
+
const next = { version: SHELL_LAYOUT_VERSION };
|
|
258
|
+
if (layout.sectionOrder?.length)
|
|
259
|
+
next.sectionOrder = layout.sectionOrder;
|
|
260
|
+
if (hasKeys(layout.itemOrder))
|
|
261
|
+
next.itemOrder = layout.itemOrder;
|
|
262
|
+
if (layout.hidden?.length)
|
|
263
|
+
next.hidden = layout.hidden;
|
|
264
|
+
if (hasKeys(layout.moved))
|
|
265
|
+
next.moved = layout.moved;
|
|
266
|
+
if (hasKeys(layout.panels))
|
|
267
|
+
next.panels = layout.panels;
|
|
268
|
+
return next;
|
|
269
|
+
}
|
|
270
|
+
function sameList(a, b) {
|
|
271
|
+
return a.length === b.length && a.every((value, i) => value === b[i]);
|
|
272
|
+
}
|
|
273
|
+
function clampIndex(index, max) {
|
|
274
|
+
if (!Number.isFinite(index))
|
|
275
|
+
return max;
|
|
276
|
+
return Math.max(0, Math.min(Math.trunc(index), max));
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Move a group section to `toIndex` among the groups (the root section is not
|
|
280
|
+
* counted). Returns the layout unchanged when `sectionId` is not a group.
|
|
281
|
+
*/
|
|
282
|
+
export function moveShellSection(nav, groups, layout, sectionId, toIndex) {
|
|
283
|
+
const current = normalizeShellLayout(layout ?? createShellLayout());
|
|
284
|
+
const ids = resolveShellNavModel(nav, groups, current)
|
|
285
|
+
.slice(1)
|
|
286
|
+
.map((section) => section.id);
|
|
287
|
+
if (!ids.includes(sectionId))
|
|
288
|
+
return current;
|
|
289
|
+
const next = ids.filter((id) => id !== sectionId);
|
|
290
|
+
next.splice(clampIndex(toIndex, next.length), 0, sectionId);
|
|
291
|
+
const defaults = resolveShellNavModel(nav, groups)
|
|
292
|
+
.slice(1)
|
|
293
|
+
.map((section) => section.id);
|
|
294
|
+
const sectionOrder = sameList(next, defaults) ? undefined : next;
|
|
295
|
+
return compact({ ...current, sectionOrder });
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Move an item into `toSectionId` (a group or the root section) at `toIndex`
|
|
299
|
+
* among that section's items, hidden ones included. `toIndex` defaults to the
|
|
300
|
+
* end. Returns the layout unchanged when either id is unknown.
|
|
301
|
+
*/
|
|
302
|
+
export function moveShellItem(nav, groups, layout, itemId, toSectionId, toIndex) {
|
|
303
|
+
const current = normalizeShellLayout(layout ?? createShellLayout());
|
|
304
|
+
const model = resolveShellNavModel(nav, groups, current);
|
|
305
|
+
const source = model.find((section) => section.items.some((entry) => entry.id === itemId));
|
|
306
|
+
const target = model.find((section) => section.id === toSectionId);
|
|
307
|
+
if (!source || !target)
|
|
308
|
+
return current;
|
|
309
|
+
const entry = source.items.find((candidate) => candidate.id === itemId);
|
|
310
|
+
if (!entry)
|
|
311
|
+
return current;
|
|
312
|
+
const lists = new Map(model.map((section) => [
|
|
313
|
+
section.id,
|
|
314
|
+
section.items.map((candidate) => candidate.id),
|
|
315
|
+
]));
|
|
316
|
+
lists.set(source.id, (lists.get(source.id) ?? []).filter((id) => id !== itemId));
|
|
317
|
+
const targetList = [...(lists.get(target.id) ?? [])];
|
|
318
|
+
targetList.splice(clampIndex(toIndex ?? targetList.length, targetList.length), 0, itemId);
|
|
319
|
+
lists.set(target.id, targetList);
|
|
320
|
+
const moved = { ...(current.moved ?? {}) };
|
|
321
|
+
if (target.id === entry.nativeSectionId)
|
|
322
|
+
delete moved[itemId];
|
|
323
|
+
else
|
|
324
|
+
moved[itemId] = target.id;
|
|
325
|
+
// With `moved` settled, a section's default order is what an empty
|
|
326
|
+
// itemOrder would give; write an order only where it differs.
|
|
327
|
+
const settled = { ...current, moved };
|
|
328
|
+
const defaultModel = resolveShellNavModel(nav, groups, {
|
|
329
|
+
version: SHELL_LAYOUT_VERSION,
|
|
330
|
+
moved,
|
|
331
|
+
sectionOrder: current.sectionOrder,
|
|
332
|
+
});
|
|
333
|
+
const itemOrder = { ...(current.itemOrder ?? {}) };
|
|
334
|
+
for (const id of new Set([source.id, target.id])) {
|
|
335
|
+
const defaults = defaultModel
|
|
336
|
+
.find((section) => section.id === id)
|
|
337
|
+
?.items.map((candidate) => candidate.id);
|
|
338
|
+
const list = lists.get(id) ?? [];
|
|
339
|
+
if (defaults && sameList(list, defaults))
|
|
340
|
+
delete itemOrder[id];
|
|
341
|
+
else
|
|
342
|
+
itemOrder[id] = list;
|
|
343
|
+
}
|
|
344
|
+
return compact({ ...settled, itemOrder });
|
|
345
|
+
}
|
|
346
|
+
/** Hide a section or item. Unknown ids and the root section are ignored. */
|
|
347
|
+
export function hideShellEntry(nav, groups, layout, id) {
|
|
348
|
+
const current = normalizeShellLayout(layout ?? createShellLayout());
|
|
349
|
+
if (!isKnownEntry(nav, groups, id) || current.hidden?.includes(id)) {
|
|
350
|
+
return current;
|
|
351
|
+
}
|
|
352
|
+
return compact({ ...current, hidden: [...(current.hidden ?? []), id] });
|
|
353
|
+
}
|
|
354
|
+
/** Show a hidden section or item again. */
|
|
355
|
+
export function showShellEntry(layout, id) {
|
|
356
|
+
const current = normalizeShellLayout(layout ?? createShellLayout());
|
|
357
|
+
if (!current.hidden?.includes(id))
|
|
358
|
+
return current;
|
|
359
|
+
return compact({
|
|
360
|
+
...current,
|
|
361
|
+
hidden: current.hidden.filter((entry) => entry !== id),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
function isKnownEntry(nav, groups, id) {
|
|
365
|
+
return resolveShellNavModel(nav, groups).some((section) => (section.group !== null && section.id === id) ||
|
|
366
|
+
section.items.some((entry) => entry.id === id));
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Set an edge panel's `visible` and/or `initial`. Values equal to the host's
|
|
370
|
+
* defaults (`defaults`, when given) are dropped to keep the layout sparse.
|
|
371
|
+
*/
|
|
372
|
+
export function setShellLayoutPanel(layout, edge, patch, defaults) {
|
|
373
|
+
const current = normalizeShellLayout(layout ?? createShellLayout());
|
|
374
|
+
if (!EDGES.includes(edge))
|
|
375
|
+
return current;
|
|
376
|
+
const panel = { ...(current.panels?.[edge] ?? {}) };
|
|
377
|
+
if ('visible' in patch) {
|
|
378
|
+
if (patch.visible === false)
|
|
379
|
+
panel.visible = false;
|
|
380
|
+
else
|
|
381
|
+
delete panel.visible;
|
|
382
|
+
}
|
|
383
|
+
if ('initial' in patch) {
|
|
384
|
+
if (patch.initial === 'collapsed' || patch.initial === 'expanded') {
|
|
385
|
+
panel.initial = patch.initial;
|
|
386
|
+
}
|
|
387
|
+
else
|
|
388
|
+
delete panel.initial;
|
|
389
|
+
}
|
|
390
|
+
const base = defaults?.[edge];
|
|
391
|
+
if (panel.initial !== undefined &&
|
|
392
|
+
base !== false &&
|
|
393
|
+
panel.initial === (base?.initial ?? 'collapsed')) {
|
|
394
|
+
delete panel.initial;
|
|
395
|
+
}
|
|
396
|
+
const panels = { ...(current.panels ?? {}) };
|
|
397
|
+
if (hasKeys(panel))
|
|
398
|
+
panels[edge] = panel;
|
|
399
|
+
else
|
|
400
|
+
delete panels[edge];
|
|
401
|
+
return compact({ ...current, panels });
|
|
402
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { type ShellLayoutPanel } from './layout.js';
|
|
1
2
|
import type { PanelEdge, PanelState, ResolvedShellConfig, ShellHotkeyBinding, ShellPanelConfig, ShellPanelDefaults, ShellPanelResize, ShellSettingsAdapter, ShellSettingsDelta, ShellViewport } from './types.js';
|
|
2
3
|
export declare const DEFAULT_SHELL_KEYMAP: Record<PanelEdge, ShellHotkeyBinding>;
|
|
3
4
|
export declare function resolveShellConfig(appDefaults?: ShellPanelDefaults): ResolvedShellConfig;
|
|
4
5
|
/**
|
|
5
|
-
* An edge's state: hidden edges
|
|
6
|
-
* then the edge's default for
|
|
7
|
-
* `viewportDefaults`), then
|
|
6
|
+
* An edge's state: hidden edges (by the host or the user's layout) stay
|
|
7
|
+
* hidden; otherwise the user's setting, then the edge's default for
|
|
8
|
+
* `viewport` (when it declares `viewportDefaults`), then the layout's
|
|
9
|
+
* `initial`, then the configured initial state.
|
|
8
10
|
*/
|
|
9
|
-
export declare function resolveInitialPanelState(edge: PanelEdge, config: ShellPanelConfig, settings?: ShellSettingsDelta, viewport?: ShellViewport): PanelState;
|
|
11
|
+
export declare function resolveInitialPanelState(edge: PanelEdge, config: ShellPanelConfig, settings?: ShellSettingsDelta, viewport?: ShellViewport, layoutPanel?: ShellLayoutPanel): PanelState;
|
|
10
12
|
/** Default limits for a resizable side edge (CSS px). */
|
|
11
13
|
export declare const DEFAULT_SHELL_PANEL_RESIZE: Required<ShellPanelResize>;
|
|
12
14
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/settings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,SAAS,EAAE,kBAAkB,CAKtE,CAAC;AA6CF,wBAAgB,kBAAkB,CAChC,WAAW,GAAE,kBAAuB,GACnC,mBAAmB,CA2BrB;AAED
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,SAAS,EAAE,kBAAkB,CAKtE,CAAC;AA6CF,wBAAgB,kBAAkB,CAChC,WAAW,GAAE,kBAAuB,GACnC,mBAAmB,CA2BrB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,gBAAgB,EACxB,QAAQ,GAAE,kBAAuB,EACjC,QAAQ,CAAC,EAAE,aAAa,EACxB,WAAW,CAAC,EAAE,gBAAgB,GAC7B,UAAU,CASZ;AAED,yDAAyD;AACzD,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,gBAAgB,CAIjE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,gBAAgB,GACvB,QAAQ,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAUnC;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GACjC,MAAM,CAER;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,OAAO,GAAG,MAAM,GACrB,OAAO,CAKT;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,mBAAmB,GAC1B,kBAAkB,CAqBpB;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,gBAAgB,EACxB,QAAQ,GAAE,kBAAuB,GAChC,kBAAkB,GAAG,IAAI,CAM3B;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,kBAAkB,GACvB,kBAAkB,CAiBpB;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,kBAAkB,GACxB,kBAAkB,CA2BpB;AAED,qBAAa,gCAAiC,YAAW,oBAAoB;IAC/D,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,MAAM;IAExC,IAAI,IAAI,kBAAkB,GAAG,IAAI;IAWjC,KAAK,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI;CAOvC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isShellLayoutEmpty, normalizeShellLayout, } from './layout.js';
|
|
1
2
|
import { EDGE_SCOPES, PANEL_EDGES } from './types.js';
|
|
2
3
|
export const DEFAULT_SHELL_KEYMAP = {
|
|
3
4
|
top: { code: 'KeyW' },
|
|
@@ -73,15 +74,19 @@ export function resolveShellConfig(appDefaults = {}) {
|
|
|
73
74
|
return { panels };
|
|
74
75
|
}
|
|
75
76
|
/**
|
|
76
|
-
* An edge's state: hidden edges
|
|
77
|
-
* then the edge's default for
|
|
78
|
-
* `viewportDefaults`), then
|
|
77
|
+
* An edge's state: hidden edges (by the host or the user's layout) stay
|
|
78
|
+
* hidden; otherwise the user's setting, then the edge's default for
|
|
79
|
+
* `viewport` (when it declares `viewportDefaults`), then the layout's
|
|
80
|
+
* `initial`, then the configured initial state.
|
|
79
81
|
*/
|
|
80
|
-
export function resolveInitialPanelState(edge, config, settings = {}, viewport) {
|
|
82
|
+
export function resolveInitialPanelState(edge, config, settings = {}, viewport, layoutPanel) {
|
|
81
83
|
if (config.initial === 'hidden')
|
|
82
84
|
return 'hidden';
|
|
85
|
+
if (layoutPanel?.visible === false)
|
|
86
|
+
return 'hidden';
|
|
83
87
|
return (settings.panels?.[edge] ??
|
|
84
88
|
(viewport ? config.viewportDefaults?.[viewport] : undefined) ??
|
|
89
|
+
layoutPanel?.initial ??
|
|
85
90
|
config.initial);
|
|
86
91
|
}
|
|
87
92
|
/** Default limits for a resizable side edge (CSS px). */
|
|
@@ -182,6 +187,11 @@ export function pruneShellSettingsDelta(delta) {
|
|
|
182
187
|
if (delta.activeFocusToolId !== undefined) {
|
|
183
188
|
pruned.activeFocusToolId = delta.activeFocusToolId;
|
|
184
189
|
}
|
|
190
|
+
if (delta.layout !== undefined) {
|
|
191
|
+
const layout = normalizeShellLayout(delta.layout);
|
|
192
|
+
if (!isShellLayoutEmpty(layout))
|
|
193
|
+
pruned.layout = layout;
|
|
194
|
+
}
|
|
185
195
|
if (delta.keymap && Object.keys(delta.keymap).length > 0) {
|
|
186
196
|
pruned.keymap = delta.keymap;
|
|
187
197
|
}
|