@happyvertical/smrt-svelte 0.55.3 → 0.55.5
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 +1 -1
- package/agents/components.md +9 -43
- package/agents/workspace.md +8 -0
- package/dist/components/forms/index.d.ts +6 -2
- package/dist/components/forms/index.d.ts.map +1 -1
- package/dist/components/forms/index.js +6 -2
- package/dist/components/forms/types.d.ts +6 -9
- package/dist/components/forms/types.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/AdminShell.svelte +88 -7
- package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts +2 -0
- package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte +18 -1
- package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts +2 -0
- package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts.map +1 -1
- package/package.json +15 -15
- package/dist/components/forms/RelationInput.svelte +0 -379
- package/dist/components/forms/RelationInput.svelte.d.ts +0 -75
- package/dist/components/forms/RelationInput.svelte.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -587,7 +587,7 @@ clears it.
|
|
|
587
587
|
|
|
588
588
|
### Components by Category
|
|
589
589
|
|
|
590
|
-
**Forms**: `AddressInput`, `CheckboxInput`, `DateRangeInput`, `DateTimeInput`, `FileUpload`, `Form`, `FormGroup`, `FormMicButton`, `Input`, `MeasurementInput`, `MoneyInput`, `NumberInput`, `PhoneInput`, `RelationInput
|
|
590
|
+
**Forms**: `AddressInput`, `CheckboxInput`, `DateRangeInput`, `DateTimeInput`, `FileUpload`, `Form`, `FormGroup`, `FormMicButton`, `Input`, `MeasurementInput`, `MoneyInput`, `NumberInput`, `PhoneInput`, `RelationInput` (deprecated re-export; import from `@happyvertical/smrt-ui/forms`), `SearchInput`, `Select`, `SelectInput`, `Textarea`, `TextareaInput`, `TextInput`, `Toggle`
|
|
591
591
|
|
|
592
592
|
**Layout**: `Container`, `EmptyState`, `Footer`, `Grid`, `Header`, `Masthead`, `PageHeader`, `SummaryCard`
|
|
593
593
|
|
package/agents/components.md
CHANGED
|
@@ -15,7 +15,7 @@ the top-of-stack, domain-aware pieces:
|
|
|
15
15
|
| Category | Components |
|
|
16
16
|
|----------|------------|
|
|
17
17
|
| AI | `Provider`, `AILoadingOverlay`, `CapabilityGate`, `DownloadProgress`, `STTTest`, `VoiceInput` |
|
|
18
|
-
| Forms (`/forms`) | `TextInput`, `Select`, `MoneyInput`, `DateTimeInput`, `Toggle`, `FileUpload`, `AddressInput`, `RelationInput
|
|
18
|
+
| Forms (`/forms`) | `TextInput`, `Select`, `MoneyInput`, `DateTimeInput`, `Toggle`, `FileUpload`, `AddressInput`, + more (`RelationInput` is a deprecated re-export of smrt-ui's) (AI-wired inputs use the hooks/browser-ai here) |
|
|
19
19
|
| Module | `ModulePanel` |
|
|
20
20
|
| Settings (`/settings`) | `SettingsCatalog`, `paginateSettingsCatalog` |
|
|
21
21
|
| Audit (root export) | `RecordHistory`, `AuditList` — authorized data props; [contract](../../../docs/content/audit-trail.md) |
|
|
@@ -50,47 +50,13 @@ migrate domain re-rolls *onto* them rather than build new primitives:
|
|
|
50
50
|
flow.
|
|
51
51
|
- **`Card`** (`./ui`) — the standard surface/container; retire local card CSS.
|
|
52
52
|
|
|
53
|
-
### `RelationInput` (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
- **Props.** `name`, `label`, `value` (bindable id, `''` = none), `required`,
|
|
61
|
-
`disabled`, `error`, `placeholder`, `description`, `search(query)`, optional
|
|
62
|
-
`resolve(id)` and `onCreate(query)`, plus `debounceMs` (250), `interaction`,
|
|
63
|
-
`onchange` and overridable text (`createLabel`, `clearLabel`, `loadingText`,
|
|
64
|
-
`emptyText`, `errorText`, `resultsText`).
|
|
65
|
-
- **`search`** runs with `''` when the list opens and then debounced per
|
|
66
|
-
keystroke; a response for a superseded query is dropped. A rejected search
|
|
67
|
-
shows `errorText` and the next keystroke retries.
|
|
68
|
-
- **`resolve`** supplies the label of a `value` that has not been searched yet
|
|
69
|
-
(initial value, parent rebind, form reset). Without it such a value shows an
|
|
70
|
-
empty field, never the raw id. A `null` result also leaves the field empty.
|
|
71
|
-
- **`onCreate`** adds a "New ..." button; it receives the text most recently
|
|
72
|
-
searched for and may return the created `RelationOption`, which is selected.
|
|
73
|
-
- **Form integration** is through `Combobox`'s control registration (rich `Form`
|
|
74
|
-
provides the registry), not a `FieldDefinition`: agents see a `combobox`
|
|
75
|
-
control whose value is the id. The form posts the id under `name`.
|
|
76
|
-
- **Known limit.** An agent staging a value through the registered control can
|
|
77
|
-
only choose among the options last listed (the registry matches against
|
|
78
|
-
`options`; `allowCustom` is off), so it cannot write an id it has not
|
|
79
|
-
searched. It fails closed. Writing a resolved id needs an async hook on
|
|
80
|
-
`Combobox` and is tracked separately.
|
|
81
|
-
- **Stale options.** Typing clears the listed options until the new search
|
|
82
|
-
answers, so Enter or a click can never pick a record for an older query.
|
|
83
|
-
- **Clear button** shows only when not `required`, not `disabled`, and a value
|
|
84
|
-
is set; it returns focus to the field.
|
|
85
|
-
- **Accessibility** is the combobox pattern (`role=combobox`, `aria-expanded`,
|
|
86
|
-
`aria-activedescendant`, listbox/option, Arrow/Enter/Escape) plus a polite
|
|
87
|
-
`role=status` live region announcing searching / "N results" / no matches,
|
|
88
|
-
`aria-busy` while fetching, and `aria-invalid` + `aria-describedby` for
|
|
89
|
-
`error` and `description`.
|
|
90
|
-
- Options render `detail` under the label, so the option's accessible name
|
|
91
|
-
includes it. The `Combobox` additions that make this possible (`filter`,
|
|
92
|
-
`onquery`, `status`, `busy`, `invalid`, `describedby`, `optionContent`) are
|
|
93
|
-
generic and default to the previous behaviour.
|
|
53
|
+
### `RelationInput` (moved to smrt-ui, #3637)
|
|
54
|
+
|
|
55
|
+
`RelationInput` (#3600) now lives in `@happyvertical/smrt-ui/forms` next to
|
|
56
|
+
`Combobox`; its contract is documented in the smrt-ui README ("RelationInput").
|
|
57
|
+
`@happyvertical/smrt-svelte/forms` still re-exports it (and `RelationOption`),
|
|
58
|
+
marked `@deprecated`, so published consumers keep working. New code imports it
|
|
59
|
+
from smrt-ui.
|
|
94
60
|
|
|
95
61
|
### Import convention (S10 #1415)
|
|
96
62
|
|
|
@@ -102,7 +68,7 @@ library. Which barrel for what:
|
|
|
102
68
|
| Need | Import from |
|
|
103
69
|
|------|-------------|
|
|
104
70
|
| Buttons, cards, badges, avatars, chips, skeletons, tooltips, dropdowns, trees, pagination | `@happyvertical/smrt-ui/ui` |
|
|
105
|
-
| Provider-free base inputs — `Input`, `Select`, `Textarea`, `Toggle`, `FormGroup` | `@happyvertical/smrt-ui/forms` (also re-exported from `@happyvertical/smrt-svelte/forms`) |
|
|
71
|
+
| Provider-free base inputs — `Input`, `Select`, `Textarea`, `Toggle`, `FormGroup`, `Combobox`, `RelationInput` | `@happyvertical/smrt-ui/forms` (also re-exported from `@happyvertical/smrt-svelte/forms`) |
|
|
106
72
|
| Provider-free `Form` (plain `<form>` wrapper) | `@happyvertical/smrt-ui/forms` **only** — `@happyvertical/smrt-svelte/forms` exports the *rich* Provider-backed `Form` under that name, so import the plain one straight from smrt-ui |
|
|
107
73
|
| Provider-backed inputs — `TextInput`, `NumberInput`, `MoneyInput`, date/measurement/address inputs, `CheckboxInput`, file upload, the rich `Form` | `@happyvertical/smrt-svelte/forms` |
|
|
108
74
|
| `Modal`, `ConfirmDialog`, `LoadingOverlay`, `ProgressBar` | `@happyvertical/smrt-ui/feedback` |
|
package/agents/workspace.md
CHANGED
|
@@ -45,6 +45,14 @@ App settings, tenant names and environment badges wrap within narrow drawers.
|
|
|
45
45
|
system bars never clip wrapped chips. Custom bars must allow this flex child
|
|
46
46
|
to shrink; use safe alignment if aligning its contents to the end.
|
|
47
47
|
|
|
48
|
+
Sidebar-only shells may set `top: false`. In that mode the configured brand is
|
|
49
|
+
shown at the top of the expanded tenant panel as well as in the compact rail.
|
|
50
|
+
Use `tenantRailFooter` for a control pinned below collapsed navigation; it sits
|
|
51
|
+
outside the rail's scroll clip so menus can open over the workspace. Pair it
|
|
52
|
+
with `WorkspaceAccountMenu compact` for an avatar-only trigger that retains the
|
|
53
|
+
full account identity as its accessible name. Keep the ordinary
|
|
54
|
+
`tenantFooter` for the expanded panel.
|
|
55
|
+
|
|
48
56
|
### Responsive chrome, viewport defaults, resizable edges
|
|
49
57
|
|
|
50
58
|
The root grid has five rows: `header` · top edge · body · bottom edge · phone
|
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
* one-stop barrel for form primitives. The Provider-REQUIRED inputs (the rich
|
|
9
9
|
* `Form`, `CheckboxInput`, `TextInput`, …) still live in this package.
|
|
10
10
|
*/
|
|
11
|
-
export { FormGroup, Input,
|
|
11
|
+
export { FormGroup, Input,
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated `RelationInput` moved to `@happyvertical/smrt-ui/forms`; import it
|
|
14
|
+
* from there. This re-export is kept so published consumers keep working.
|
|
15
|
+
*/
|
|
16
|
+
RelationInput, Select, Textarea, Toggle, } from '@happyvertical/smrt-ui/forms';
|
|
12
17
|
export { default as AddressInput } from './AddressInput.svelte';
|
|
13
18
|
export { default as CheckboxInput } from './CheckboxInput.svelte';
|
|
14
19
|
export { default as DateRangeInput } from './DateRangeInput.svelte';
|
|
@@ -21,7 +26,6 @@ export { default as MeasurementInput } from './MeasurementInput.svelte';
|
|
|
21
26
|
export { default as MoneyInput } from './MoneyInput.svelte';
|
|
22
27
|
export { default as NumberInput } from './NumberInput.svelte';
|
|
23
28
|
export { default as PhoneInput } from './PhoneInput.svelte';
|
|
24
|
-
export { default as RelationInput } from './RelationInput.svelte';
|
|
25
29
|
export { default as SearchInput } from './SearchInput.svelte';
|
|
26
30
|
export { default as SelectInput } from './SelectInput.svelte';
|
|
27
31
|
export { default as TextareaInput } from './TextareaInput.svelte';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/forms/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,SAAS,EACT,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/forms/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,SAAS,EACT,KAAK;AACL;;;GAGG;AACH,aAAa,EACb,MAAM,EACN,QAAQ,EACR,MAAM,GACP,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG1D,YAAY,EACV,YAAY,EACZ,cAAc,EACd,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
* `Form`, `CheckboxInput`, `TextInput`, …) still live in this package.
|
|
10
10
|
*/
|
|
11
11
|
// Provider-free base primitives — relocated to the smrt-ui leaf (#1589).
|
|
12
|
-
export { FormGroup, Input,
|
|
12
|
+
export { FormGroup, Input,
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated `RelationInput` moved to `@happyvertical/smrt-ui/forms`; import it
|
|
15
|
+
* from there. This re-export is kept so published consumers keep working.
|
|
16
|
+
*/
|
|
17
|
+
RelationInput, Select, Textarea, Toggle, } from '@happyvertical/smrt-ui/forms';
|
|
13
18
|
// SMRT-enhanced form components with voice support (v2.0 naming)
|
|
14
19
|
export { default as AddressInput } from './AddressInput.svelte';
|
|
15
20
|
export { default as CheckboxInput } from './CheckboxInput.svelte';
|
|
@@ -23,7 +28,6 @@ export { default as MeasurementInput } from './MeasurementInput.svelte';
|
|
|
23
28
|
export { default as MoneyInput } from './MoneyInput.svelte';
|
|
24
29
|
export { default as NumberInput } from './NumberInput.svelte';
|
|
25
30
|
export { default as PhoneInput } from './PhoneInput.svelte';
|
|
26
|
-
export { default as RelationInput } from './RelationInput.svelte';
|
|
27
31
|
export { default as SearchInput } from './SearchInput.svelte';
|
|
28
32
|
export { default as SelectInput } from './SelectInput.svelte';
|
|
29
33
|
export { default as TextareaInput } from './TextareaInput.svelte';
|
|
@@ -10,15 +10,12 @@ export interface SelectOption {
|
|
|
10
10
|
value: string;
|
|
11
11
|
label: string;
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/** Secondary text shown under the label in the list. */
|
|
20
|
-
detail?: string;
|
|
21
|
-
}
|
|
13
|
+
/**
|
|
14
|
+
* A searchable relation target for the RelationInput component.
|
|
15
|
+
*
|
|
16
|
+
* @deprecated Moved to `@happyvertical/smrt-ui/forms`; import it from there.
|
|
17
|
+
*/
|
|
18
|
+
export type { RelationOption } from '@happyvertical/smrt-ui/forms';
|
|
22
19
|
/** Address value for SMRTAddress component */
|
|
23
20
|
export interface AddressValue {
|
|
24
21
|
street: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/forms/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,kCAAkC;AAClC,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,cAAc,GAAG,aAAa,CAAC;AAE/E,wCAAwC;AACxC,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,cAAc,GACd,cAAc,GACd,cAAc,GACd,cAAc,CAAC;AAEnB,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/forms/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,kCAAkC;AAClC,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,cAAc,GAAG,aAAa,CAAC;AAE/E,wCAAwC;AACxC,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,cAAc,GACd,cAAc,GACd,cAAc,GACd,cAAc,CAAC;AAEnB,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,YAAY,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,8CAA8C;AAC9C,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,6BAA6B;AAC7B,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAErE,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC;CACvB"}
|
|
@@ -81,6 +81,8 @@ function buildLayoutStyle(shell: ModuleShellState): string {
|
|
|
81
81
|
appPanel?: Snippet;
|
|
82
82
|
/** Content for the left tenant rail. */
|
|
83
83
|
tenantRail?: Snippet;
|
|
84
|
+
/** Content pinned below the collapsed tenant rail. */
|
|
85
|
+
tenantRailFooter?: Snippet;
|
|
84
86
|
/** Content for the left tenant panel. */
|
|
85
87
|
tenantPanel?: Snippet;
|
|
86
88
|
/** Content for the tenant panel footer. */
|
|
@@ -164,6 +166,7 @@ function buildLayoutStyle(shell: ModuleShellState): string {
|
|
|
164
166
|
account,
|
|
165
167
|
appPanel,
|
|
166
168
|
tenantRail,
|
|
169
|
+
tenantRailFooter,
|
|
167
170
|
tenantPanel,
|
|
168
171
|
tenantFooter,
|
|
169
172
|
focusRail,
|
|
@@ -239,8 +242,19 @@ function buildLayoutStyle(shell: ModuleShellState): string {
|
|
|
239
242
|
});
|
|
240
243
|
return () => { cancelled = true; queueMicrotask(() => { if (opener?.isConnected) opener.focus(); }); };
|
|
241
244
|
});
|
|
242
|
-
const
|
|
243
|
-
|
|
245
|
+
const showDefaultPhoneTenantOpener = $derived(
|
|
246
|
+
isPhone &&
|
|
247
|
+
!phoneTopBar &&
|
|
248
|
+
panelState('top') === 'hidden' &&
|
|
249
|
+
shell.isEdgeShown('left') &&
|
|
250
|
+
shell.phonePresentation('left') !== 'hidden',
|
|
251
|
+
);
|
|
252
|
+
const showPhoneTop = $derived(
|
|
253
|
+
isPhone && (Boolean(phoneTopBar) || showDefaultPhoneTenantOpener),
|
|
254
|
+
);
|
|
255
|
+
const showHeader = $derived(
|
|
256
|
+
Boolean(header) && !(isPhone && Boolean(phoneTopBar)),
|
|
257
|
+
);
|
|
244
258
|
const bottomBar = $derived<BottomBarMode>(
|
|
245
259
|
isPhone && phoneBottomBar
|
|
246
260
|
? bottomBarMode({ formActions, keyboardOpen })
|
|
@@ -598,7 +612,12 @@ function buildLayoutStyle(shell: ModuleShellState): string {
|
|
|
598
612
|
handle.addEventListener('lostpointercapture', onEnd);
|
|
599
613
|
}
|
|
600
614
|
|
|
601
|
-
const footerInHeader = $derived(
|
|
615
|
+
const footerInHeader = $derived(
|
|
616
|
+
!account &&
|
|
617
|
+
!!tenantFooter &&
|
|
618
|
+
(!shell.isEdgeShown('left') ||
|
|
619
|
+
(!edgeExpanded('left') && !tenantRailFooter)),
|
|
620
|
+
);
|
|
602
621
|
|
|
603
622
|
const layoutStyle = $derived(buildLayoutStyle(shell));
|
|
604
623
|
|
|
@@ -883,7 +902,9 @@ function buildLayoutStyle(shell: ModuleShellState): string {
|
|
|
883
902
|
>
|
|
884
903
|
{@render resizer('left')}
|
|
885
904
|
<div class="smrt-admin-shell__rail">
|
|
886
|
-
{#if !edgeExpanded('left') && (homeHref || logoSrc || brand)}
|
|
905
|
+
{#if (panelState('top') === 'hidden' || !edgeExpanded('left')) && (homeHref || logoSrc || brand)}
|
|
906
|
+
{@render shellBrand(!edgeExpanded('left'))}
|
|
907
|
+
{/if}
|
|
887
908
|
{#if showTenantToggle}{@render edgeToggle('left')}{/if}
|
|
888
909
|
{#if shownOpen('left') || (tenantPanel && keepsContent('left'))}
|
|
889
910
|
<div
|
|
@@ -904,8 +925,19 @@ function buildLayoutStyle(shell: ModuleShellState): string {
|
|
|
904
925
|
{/if}
|
|
905
926
|
</div>
|
|
906
927
|
{/if}
|
|
907
|
-
{#if !shownOpen('left') && tenantRail}
|
|
908
|
-
|
|
928
|
+
{#if !shownOpen('left') && (tenantRail || tenantRailFooter)}
|
|
929
|
+
<div class="smrt-admin-shell__tenant-rail-stack">
|
|
930
|
+
{#if tenantRail}
|
|
931
|
+
<div class="smrt-admin-shell__tenant-rail-content">
|
|
932
|
+
{@render tenantRail()}
|
|
933
|
+
</div>
|
|
934
|
+
{/if}
|
|
935
|
+
{#if tenantRailFooter}
|
|
936
|
+
<div class="smrt-admin-shell__tenant-rail-footer">
|
|
937
|
+
{@render tenantRailFooter()}
|
|
938
|
+
</div>
|
|
939
|
+
{/if}
|
|
940
|
+
</div>
|
|
909
941
|
{/if}
|
|
910
942
|
</div>
|
|
911
943
|
</aside>
|
|
@@ -922,7 +954,24 @@ function buildLayoutStyle(shell: ModuleShellState): string {
|
|
|
922
954
|
|
|
923
955
|
{#if showPhoneTop}
|
|
924
956
|
<div class="smrt-admin-shell__phone-top" data-testid="admin-shell-phone-top">
|
|
925
|
-
{
|
|
957
|
+
{#if phoneTopBar}
|
|
958
|
+
{@render phoneTopBar({ hidden: chromeHidden })}
|
|
959
|
+
{:else if showDefaultPhoneTenantOpener}
|
|
960
|
+
<div class="smrt-admin-shell__phone-default-top">
|
|
961
|
+
<Button
|
|
962
|
+
variant="ghost"
|
|
963
|
+
size="sm"
|
|
964
|
+
aria-label={t(M['ui.admin_shell.menu'])}
|
|
965
|
+
aria-expanded={edgeExpanded('left')}
|
|
966
|
+
aria-controls="smrt-admin-shell-left-panel"
|
|
967
|
+
onclick={() =>
|
|
968
|
+
shell.setPanelState(
|
|
969
|
+
'left',
|
|
970
|
+
edgeExpanded('left') ? 'collapsed' : 'expanded',
|
|
971
|
+
)}
|
|
972
|
+
>{t(M['ui.admin_shell.menu'])}</Button>
|
|
973
|
+
</div>
|
|
974
|
+
{/if}
|
|
926
975
|
</div>
|
|
927
976
|
{/if}
|
|
928
977
|
|
|
@@ -1424,6 +1473,7 @@ function buildLayoutStyle(shell: ModuleShellState): string {
|
|
|
1424
1473
|
display: flex;
|
|
1425
1474
|
flex-direction: column;
|
|
1426
1475
|
gap: var(--smrt-spacing-2);
|
|
1476
|
+
overflow: visible;
|
|
1427
1477
|
}
|
|
1428
1478
|
|
|
1429
1479
|
/* A collapsed `keepMounted` edge keeps its panel in the DOM but hidden;
|
|
@@ -1456,6 +1506,30 @@ function buildLayoutStyle(shell: ModuleShellState): string {
|
|
|
1456
1506
|
border-block-start: 1px solid var(--smrt-color-outline-variant);
|
|
1457
1507
|
}
|
|
1458
1508
|
|
|
1509
|
+
.smrt-admin-shell__tenant-rail-stack {
|
|
1510
|
+
display: grid;
|
|
1511
|
+
grid-template-rows: minmax(0, 1fr) auto;
|
|
1512
|
+
flex: 1;
|
|
1513
|
+
min-width: 0;
|
|
1514
|
+
min-height: 0;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
.smrt-admin-shell__tenant-rail-content {
|
|
1518
|
+
min-width: 0;
|
|
1519
|
+
min-height: 0;
|
|
1520
|
+
overflow: auto;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
.smrt-admin-shell__tenant-rail-footer {
|
|
1524
|
+
position: relative;
|
|
1525
|
+
z-index: 1;
|
|
1526
|
+
grid-row: 2;
|
|
1527
|
+
min-width: 0;
|
|
1528
|
+
padding-block-start: var(--smrt-spacing-2);
|
|
1529
|
+
border-block-start: 1px solid var(--smrt-color-outline-variant);
|
|
1530
|
+
overflow: visible;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1459
1533
|
.smrt-admin-shell__edge--right[data-state='expanded']
|
|
1460
1534
|
.smrt-admin-shell__rail {
|
|
1461
1535
|
grid-column: 2;
|
|
@@ -1643,6 +1717,13 @@ function buildLayoutStyle(shell: ModuleShellState): string {
|
|
|
1643
1717
|
var(--smrt-easing-standard, ease);
|
|
1644
1718
|
}
|
|
1645
1719
|
|
|
1720
|
+
.smrt-admin-shell__phone-default-top {
|
|
1721
|
+
display: flex;
|
|
1722
|
+
align-items: center;
|
|
1723
|
+
min-block-size: var(--smrt-admin-shell-phone-top-size);
|
|
1724
|
+
padding-inline: var(--smrt-spacing-3);
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1646
1727
|
.smrt-admin-shell[data-chrome-hidden] .smrt-admin-shell__phone-top {
|
|
1647
1728
|
transform: translateY(-100%);
|
|
1648
1729
|
}
|
|
@@ -13,6 +13,8 @@ interface Props extends AdminShellProps {
|
|
|
13
13
|
appPanel?: Snippet;
|
|
14
14
|
/** Content for the left tenant rail. */
|
|
15
15
|
tenantRail?: Snippet;
|
|
16
|
+
/** Content pinned below the collapsed tenant rail. */
|
|
17
|
+
tenantRailFooter?: Snippet;
|
|
16
18
|
/** Content for the left tenant panel. */
|
|
17
19
|
tenantPanel?: Snippet;
|
|
18
20
|
/** Content for the tenant panel footer. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminShell.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/AdminShell.svelte.ts"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAQtC,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,mBAAmB,CAAC;AAStE,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EACR,sBAAsB,EACtB,eAAe,EAEf,cAAc,EACd,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB,UAAU,KAAM,SAAQ,eAAe;IACrC,sEAAsE;IACtE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,mCAAmC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,wCAAwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC,CAAC;IACxD;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oCAAoC;IACpC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wCAAwC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,2CAA2C;IAC3C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,QAAQ,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC,CAAC;IAChD;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC,CAAC;IAC7C;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,QAAQ,EAAE,aAAa,CAAC;QAAC,SAAS,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5E,uEAAuE;IACvE,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"AdminShell.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/AdminShell.svelte.ts"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAQtC,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,mBAAmB,CAAC;AAStE,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EACR,sBAAsB,EACtB,eAAe,EAEf,cAAc,EACd,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB,UAAU,KAAM,SAAQ,eAAe;IACrC,sEAAsE;IACtE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,mCAAmC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,wCAAwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC,CAAC;IACxD;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oCAAoC;IACpC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wCAAwC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,2CAA2C;IAC3C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,QAAQ,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC,CAAC;IAChD;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC,CAAC;IAC7C;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,QAAQ,EAAE,aAAa,CAAC;QAAC,SAAS,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5E,uEAAuE;IACvE,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;CACnB;AAs5BH,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -13,6 +13,8 @@ export interface Props {
|
|
|
13
13
|
userName: string;
|
|
14
14
|
/** Target density; omit to inherit the theme density. */
|
|
15
15
|
density?: 'comfortable' | 'touch';
|
|
16
|
+
/** Show only the avatar while preserving the full accessible account name. */
|
|
17
|
+
compact?: boolean;
|
|
16
18
|
/** Custom display name for the user, falls back to userName. */
|
|
17
19
|
userLabel?: string;
|
|
18
20
|
/** URL for the user's avatar image. */
|
|
@@ -38,6 +40,7 @@ export interface Props {
|
|
|
38
40
|
let {
|
|
39
41
|
userName,
|
|
40
42
|
density,
|
|
43
|
+
compact = false,
|
|
41
44
|
userLabel = '',
|
|
42
45
|
avatarUrl,
|
|
43
46
|
tenantLabel = '',
|
|
@@ -103,10 +106,11 @@ function handleSelect(id: string): void {
|
|
|
103
106
|
}
|
|
104
107
|
</script>
|
|
105
108
|
|
|
106
|
-
<div data-density={density} class="smrt-workspace-account-menu" title={summaryTitle}>
|
|
109
|
+
<div data-density={density} data-compact={compact ? '' : undefined} class="smrt-workspace-account-menu" title={summaryTitle}>
|
|
107
110
|
<Dropdown
|
|
108
111
|
{items}
|
|
109
112
|
{placement}
|
|
113
|
+
triggerLabel={compact ? `${t(M['ui.workspace_account_menu.open'])} ${summaryTitle}` : undefined}
|
|
110
114
|
onselect={handleSelect}
|
|
111
115
|
disabled={disabled || items.length === 0}
|
|
112
116
|
>
|
|
@@ -161,6 +165,19 @@ function handleSelect(id: string): void {
|
|
|
161
165
|
text-align: start;
|
|
162
166
|
}
|
|
163
167
|
|
|
168
|
+
.smrt-workspace-account-menu[data-compact] :global(.dropdown__trigger) {
|
|
169
|
+
display: grid;
|
|
170
|
+
place-items: center;
|
|
171
|
+
grid-template-columns: minmax(0, 1fr);
|
|
172
|
+
padding: var(--smrt-spacing-1);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.smrt-workspace-account-menu[data-compact] .smrt-workspace-account-menu__identity,
|
|
176
|
+
.smrt-workspace-account-menu[data-compact] .smrt-workspace-account-menu__indicator,
|
|
177
|
+
.smrt-workspace-account-menu[data-compact] .smrt-workspace-account-menu__open {
|
|
178
|
+
display: none;
|
|
179
|
+
}
|
|
180
|
+
|
|
164
181
|
.smrt-workspace-account-menu :global(.dropdown__menu) {
|
|
165
182
|
box-sizing: border-box;
|
|
166
183
|
width: 100%;
|
|
@@ -5,6 +5,8 @@ export interface Props {
|
|
|
5
5
|
userName: string;
|
|
6
6
|
/** Target density; omit to inherit the theme density. */
|
|
7
7
|
density?: 'comfortable' | 'touch';
|
|
8
|
+
/** Show only the avatar while preserving the full accessible account name. */
|
|
9
|
+
compact?: boolean;
|
|
8
10
|
/** Custom display name for the user, falls back to userName. */
|
|
9
11
|
userLabel?: string;
|
|
10
12
|
/** URL for the user's avatar image. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspaceAccountMenu.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAY,MAAM,wBAAwB,CAAC;AAI1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGzD,MAAM,WAAW,KAAK;IACpB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAClC,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACnC,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sEAAsE;IACtE,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uDAAuD;IACvD,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"WorkspaceAccountMenu.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAY,MAAM,wBAAwB,CAAC;AAI1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGzD,MAAM,WAAW,KAAK;IACpB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAClC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACnC,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sEAAsE;IACtE,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uDAAuD;IACvD,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAuGD,QAAA,MAAM,oBAAoB,2CAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-svelte",
|
|
3
|
-
"version": "0.55.
|
|
3
|
+
"version": "0.55.5",
|
|
4
4
|
"smrtJsdoc": "strict",
|
|
5
5
|
"description": "Svelte 5 components for SMRT user management - auth, users, tenants, roles, permissions, groups",
|
|
6
6
|
"type": "module",
|
|
@@ -148,12 +148,12 @@
|
|
|
148
148
|
"access": "public"
|
|
149
149
|
},
|
|
150
150
|
"dependencies": {
|
|
151
|
-
"@happyvertical/logger": "^0.102.
|
|
152
|
-
"@happyvertical/smrt-languages": "0.55.
|
|
153
|
-
"@happyvertical/smrt-mcp-apps": "0.55.
|
|
154
|
-
"@happyvertical/smrt-types": "0.55.
|
|
155
|
-
"@happyvertical/smrt-ui": "0.55.
|
|
156
|
-
"@happyvertical/smrt-web": "0.55.
|
|
151
|
+
"@happyvertical/logger": "^0.102.4",
|
|
152
|
+
"@happyvertical/smrt-languages": "0.55.5",
|
|
153
|
+
"@happyvertical/smrt-mcp-apps": "0.55.5",
|
|
154
|
+
"@happyvertical/smrt-types": "0.55.5",
|
|
155
|
+
"@happyvertical/smrt-ui": "0.55.5",
|
|
156
|
+
"@happyvertical/smrt-web": "0.55.5",
|
|
157
157
|
"@tanstack/db": "^0.6.14",
|
|
158
158
|
"@tanstack/svelte-db": "^0.1.91",
|
|
159
159
|
"esm-env": "^1.2.2"
|
|
@@ -188,14 +188,14 @@
|
|
|
188
188
|
}
|
|
189
189
|
},
|
|
190
190
|
"devDependencies": {
|
|
191
|
-
"@happyvertical/smrt-agents": "0.55.
|
|
192
|
-
"@happyvertical/smrt-chat": "0.55.
|
|
193
|
-
"@happyvertical/smrt-content": "0.55.
|
|
194
|
-
"@happyvertical/smrt-core": "0.55.
|
|
195
|
-
"@happyvertical/smrt-reports": "0.55.
|
|
196
|
-
"@happyvertical/smrt-scanner": "0.55.
|
|
197
|
-
"@happyvertical/smrt-tenancy": "0.55.
|
|
198
|
-
"@happyvertical/smrt-users": "0.55.
|
|
191
|
+
"@happyvertical/smrt-agents": "0.55.5",
|
|
192
|
+
"@happyvertical/smrt-chat": "0.55.5",
|
|
193
|
+
"@happyvertical/smrt-content": "0.55.5",
|
|
194
|
+
"@happyvertical/smrt-core": "0.55.5",
|
|
195
|
+
"@happyvertical/smrt-reports": "0.55.5",
|
|
196
|
+
"@happyvertical/smrt-scanner": "0.55.5",
|
|
197
|
+
"@happyvertical/smrt-tenancy": "0.55.5",
|
|
198
|
+
"@happyvertical/smrt-users": "0.55.5",
|
|
199
199
|
"@sveltejs/package": "^2.5.8",
|
|
200
200
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
201
201
|
"@testing-library/jest-dom": "^7.0.1",
|
|
@@ -1,379 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { Button } from '@happyvertical/smrt-ui';
|
|
3
|
-
import {
|
|
4
|
-
Combobox,
|
|
5
|
-
type ControlInteractionOptions,
|
|
6
|
-
emitControlChange,
|
|
7
|
-
recordControlUserEdit,
|
|
8
|
-
tryGetControlInteractionContext,
|
|
9
|
-
} from '@happyvertical/smrt-ui/forms';
|
|
10
|
-
import { untrack } from 'svelte';
|
|
11
|
-
import { logger } from '../../internal/logger.js';
|
|
12
|
-
import type { RelationOption } from './types.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* RelationInput - searchable single-select for relation (foreign key) fields.
|
|
16
|
-
*
|
|
17
|
-
* Presentation only: the caller supplies `search` (and optionally `resolve`),
|
|
18
|
-
* so the same lookup an assistant uses to turn "Acme" into a record id backs
|
|
19
|
-
* the picker. Built on the smrt-ui `Combobox`, which supplies the ARIA 1.2
|
|
20
|
-
* combobox pattern (arrow keys, Enter, Escape, `aria-activedescendant`) and
|
|
21
|
-
* registers with the rich Form's control registry. The form posts the
|
|
22
|
-
* committed id under `name`, never the visible label.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```svelte
|
|
26
|
-
* <RelationInput
|
|
27
|
-
* name="customerId"
|
|
28
|
-
* label="Customer"
|
|
29
|
-
* bind:value={customerId}
|
|
30
|
-
* search={(q) => customers.search(q)}
|
|
31
|
-
* resolve={(id) => customers.byId(id)}
|
|
32
|
-
* />
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
export interface Props {
|
|
36
|
-
/** Field name; the selected id posts under it. */
|
|
37
|
-
name: string;
|
|
38
|
-
/** Visible label. */
|
|
39
|
-
label: string;
|
|
40
|
-
/** Selected record id (bindable). Empty string means no selection. */
|
|
41
|
-
value?: string;
|
|
42
|
-
/** Required field: hides the clear button and flags an empty value. */
|
|
43
|
-
required?: boolean;
|
|
44
|
-
/** Disabled state. */
|
|
45
|
-
disabled?: boolean;
|
|
46
|
-
/** Validation error message, shown below the field. */
|
|
47
|
-
error?: string;
|
|
48
|
-
/** Placeholder shown when nothing is selected. */
|
|
49
|
-
placeholder?: string;
|
|
50
|
-
/** Hint announced with the field. */
|
|
51
|
-
description?: string;
|
|
52
|
-
/** Look up records matching the text; called with '' when the list opens. */
|
|
53
|
-
search: (query: string) => Promise<RelationOption[]>;
|
|
54
|
-
/** Look up the current value's label when it has not been searched yet. */
|
|
55
|
-
resolve?: (id: string) => Promise<{ id: string; label: string } | null>;
|
|
56
|
-
/**
|
|
57
|
-
* "New ..." action. Receives the text most recently searched for. May
|
|
58
|
-
* return the created record, which is then selected.
|
|
59
|
-
*/
|
|
60
|
-
onCreate?: (
|
|
61
|
-
query: string,
|
|
62
|
-
) => void | RelationOption | Promise<void | RelationOption>;
|
|
63
|
-
/** Milliseconds to wait after typing before searching. */
|
|
64
|
-
debounceMs?: number;
|
|
65
|
-
/** Combobox interaction options (stable id, subject) for agents. */
|
|
66
|
-
interaction?: ControlInteractionOptions | false;
|
|
67
|
-
/** Text for the "New ..." button. */
|
|
68
|
-
createLabel?: string;
|
|
69
|
-
/** Accessible name for the clear button. */
|
|
70
|
-
clearLabel?: string;
|
|
71
|
-
/** Status text while searching. */
|
|
72
|
-
loadingText?: string;
|
|
73
|
-
/** Status text when nothing matches. */
|
|
74
|
-
emptyText?: string;
|
|
75
|
-
/** Status text when the search fails. */
|
|
76
|
-
errorText?: string;
|
|
77
|
-
/** Status text announcing how many results are listed. */
|
|
78
|
-
resultsText?: (count: number) => string;
|
|
79
|
-
/** Called when the selected id changes ('' when cleared). */
|
|
80
|
-
onchange?: (value: string) => void;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
let {
|
|
84
|
-
name,
|
|
85
|
-
label,
|
|
86
|
-
value = $bindable(''),
|
|
87
|
-
required = false,
|
|
88
|
-
disabled = false,
|
|
89
|
-
error,
|
|
90
|
-
placeholder,
|
|
91
|
-
description,
|
|
92
|
-
search,
|
|
93
|
-
resolve,
|
|
94
|
-
onCreate,
|
|
95
|
-
debounceMs = 250,
|
|
96
|
-
interaction,
|
|
97
|
-
createLabel,
|
|
98
|
-
clearLabel,
|
|
99
|
-
loadingText = 'Searching…',
|
|
100
|
-
emptyText = 'No matches',
|
|
101
|
-
errorText = 'Search failed. Try again.',
|
|
102
|
-
resultsText = (count: number) =>
|
|
103
|
-
`${count} ${count === 1 ? 'result' : 'results'} available`,
|
|
104
|
-
onchange,
|
|
105
|
-
}: Props = $props();
|
|
106
|
-
|
|
107
|
-
const instanceId = $props.id();
|
|
108
|
-
const errorId = `${instanceId}-error`;
|
|
109
|
-
const hintId = `${instanceId}-hint`;
|
|
110
|
-
const interactionContext = tryGetControlInteractionContext();
|
|
111
|
-
|
|
112
|
-
let rootEl = $state<HTMLDivElement | null>(null);
|
|
113
|
-
let results = $state<RelationOption[]>([]);
|
|
114
|
-
let loading = $state(false);
|
|
115
|
-
let failed = $state(false);
|
|
116
|
-
let selectedLabel = $state('');
|
|
117
|
-
let creating = $state(false);
|
|
118
|
-
|
|
119
|
-
// Plain bookkeeping, deliberately not reactive.
|
|
120
|
-
let labelledId = '';
|
|
121
|
-
let resolvingId = '';
|
|
122
|
-
let searchSeq = 0;
|
|
123
|
-
let lastQuery = '';
|
|
124
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
125
|
-
|
|
126
|
-
const options = $derived(
|
|
127
|
-
results.map((item) => ({ value: item.id, label: item.label })),
|
|
128
|
-
);
|
|
129
|
-
const detailById = $derived(
|
|
130
|
-
new Map(results.map((item) => [item.id, item.detail])),
|
|
131
|
-
);
|
|
132
|
-
const status = $derived(
|
|
133
|
-
loading
|
|
134
|
-
? loadingText
|
|
135
|
-
: failed
|
|
136
|
-
? errorText
|
|
137
|
-
: results.length === 0
|
|
138
|
-
? emptyText
|
|
139
|
-
: resultsText(results.length),
|
|
140
|
-
);
|
|
141
|
-
const describedby = $derived(
|
|
142
|
-
[error ? errorId : '', description ? hintId : ''].filter(Boolean).join(' ') ||
|
|
143
|
-
undefined,
|
|
144
|
-
);
|
|
145
|
-
const controlId = $derived(
|
|
146
|
-
interaction === false ? undefined : (interaction?.id ?? name),
|
|
147
|
-
);
|
|
148
|
-
const canClear = $derived(!required && !disabled && value !== '');
|
|
149
|
-
const newLabel = $derived(createLabel ?? `New ${label.toLowerCase()}`);
|
|
150
|
-
|
|
151
|
-
async function runSearch(query: string) {
|
|
152
|
-
const seq = ++searchSeq;
|
|
153
|
-
loading = true;
|
|
154
|
-
failed = false;
|
|
155
|
-
try {
|
|
156
|
-
const found = await search(query);
|
|
157
|
-
if (seq !== searchSeq) return;
|
|
158
|
-
results = found;
|
|
159
|
-
} catch (err) {
|
|
160
|
-
if (seq !== searchSeq) return;
|
|
161
|
-
results = [];
|
|
162
|
-
failed = true;
|
|
163
|
-
logger.warn('[smrt-svelte] RelationInput search failed', {
|
|
164
|
-
error: String(err),
|
|
165
|
-
});
|
|
166
|
-
} finally {
|
|
167
|
-
if (seq === searchSeq) loading = false;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function handleQuery(query: string) {
|
|
172
|
-
clearTimeout(timer);
|
|
173
|
-
lastQuery = query;
|
|
174
|
-
if (query === '') {
|
|
175
|
-
// Opening the list: no typing to wait for.
|
|
176
|
-
void runSearch('');
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
// Show "Searching" straight away and drop any in-flight older request. The
|
|
180
|
-
// listed options answered an older query, so none stay selectable (Enter or
|
|
181
|
-
// a click would otherwise pick a record that no longer matches the text).
|
|
182
|
-
searchSeq++;
|
|
183
|
-
loading = true;
|
|
184
|
-
failed = false;
|
|
185
|
-
results = [];
|
|
186
|
-
timer = setTimeout(() => void runSearch(query), debounceMs);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function handleValueChange(next: string) {
|
|
190
|
-
lastQuery = '';
|
|
191
|
-
const hit = results.find((item) => item.id === next);
|
|
192
|
-
if (hit) {
|
|
193
|
-
selectedLabel = hit.label;
|
|
194
|
-
labelledId = next;
|
|
195
|
-
}
|
|
196
|
-
onchange?.(next);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function focusInput() {
|
|
200
|
-
rootEl?.querySelector<HTMLInputElement>('input[role="combobox"]')?.focus();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function clear() {
|
|
204
|
-
if (!canClear) return;
|
|
205
|
-
value = '';
|
|
206
|
-
selectedLabel = '';
|
|
207
|
-
labelledId = '';
|
|
208
|
-
lastQuery = '';
|
|
209
|
-
onchange?.('');
|
|
210
|
-
recordControlUserEdit(
|
|
211
|
-
interactionContext,
|
|
212
|
-
controlId,
|
|
213
|
-
interaction === false ? undefined : interaction?.subject,
|
|
214
|
-
);
|
|
215
|
-
if (rootEl) emitControlChange(rootEl);
|
|
216
|
-
focusInput();
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
async function create() {
|
|
220
|
-
if (!onCreate || creating || disabled) return;
|
|
221
|
-
// The field has already lost focus (and reset its text) by now, so use the
|
|
222
|
-
// text most recently searched for.
|
|
223
|
-
const typed = lastQuery;
|
|
224
|
-
creating = true;
|
|
225
|
-
try {
|
|
226
|
-
const made = await onCreate(typed);
|
|
227
|
-
if (made && typeof made === 'object') {
|
|
228
|
-
results = [made, ...results.filter((item) => item.id !== made.id)];
|
|
229
|
-
selectedLabel = made.label;
|
|
230
|
-
labelledId = made.id;
|
|
231
|
-
value = made.id;
|
|
232
|
-
onchange?.(made.id);
|
|
233
|
-
recordControlUserEdit(
|
|
234
|
-
interactionContext,
|
|
235
|
-
controlId,
|
|
236
|
-
interaction === false ? undefined : interaction?.subject,
|
|
237
|
-
);
|
|
238
|
-
if (rootEl) emitControlChange(rootEl);
|
|
239
|
-
}
|
|
240
|
-
} catch (err) {
|
|
241
|
-
logger.warn('[smrt-svelte] RelationInput onCreate failed', {
|
|
242
|
-
error: String(err),
|
|
243
|
-
});
|
|
244
|
-
} finally {
|
|
245
|
-
creating = false;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Keep the visible label in step with `value` however it changed (initial
|
|
250
|
-
// value, parent rebind, form reset): known from a search, else resolved.
|
|
251
|
-
$effect(() => {
|
|
252
|
-
const id = value;
|
|
253
|
-
const known = results.find((item) => item.id === id);
|
|
254
|
-
untrack(() => {
|
|
255
|
-
if (!id) {
|
|
256
|
-
selectedLabel = '';
|
|
257
|
-
labelledId = '';
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
if (id === labelledId) return;
|
|
261
|
-
if (known) {
|
|
262
|
-
selectedLabel = known.label;
|
|
263
|
-
labelledId = id;
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
// A different id than the one labelled: never keep the previous label.
|
|
267
|
-
selectedLabel = '';
|
|
268
|
-
labelledId = '';
|
|
269
|
-
if (!resolve || resolvingId === id) return;
|
|
270
|
-
resolvingId = id;
|
|
271
|
-
resolve(id)
|
|
272
|
-
.then((record) => {
|
|
273
|
-
if (value === id && record) {
|
|
274
|
-
selectedLabel = record.label;
|
|
275
|
-
labelledId = id;
|
|
276
|
-
}
|
|
277
|
-
})
|
|
278
|
-
.catch((err) => {
|
|
279
|
-
logger.warn('[smrt-svelte] RelationInput resolve failed', {
|
|
280
|
-
error: String(err),
|
|
281
|
-
});
|
|
282
|
-
})
|
|
283
|
-
.finally(() => {
|
|
284
|
-
if (resolvingId === id) resolvingId = '';
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
$effect(() => () => clearTimeout(timer));
|
|
290
|
-
</script>
|
|
291
|
-
|
|
292
|
-
<div class="relation-input" bind:this={rootEl}>
|
|
293
|
-
<div class="field">
|
|
294
|
-
<Combobox
|
|
295
|
-
{name}
|
|
296
|
-
{label}
|
|
297
|
-
{options}
|
|
298
|
-
{placeholder}
|
|
299
|
-
{disabled}
|
|
300
|
-
{required}
|
|
301
|
-
{interaction}
|
|
302
|
-
{status}
|
|
303
|
-
{describedby}
|
|
304
|
-
bind:value
|
|
305
|
-
valueLabel={selectedLabel}
|
|
306
|
-
filter={false}
|
|
307
|
-
busy={loading}
|
|
308
|
-
invalid={!!error}
|
|
309
|
-
onquery={handleQuery}
|
|
310
|
-
onvaluechange={handleValueChange}
|
|
311
|
-
>
|
|
312
|
-
{#snippet optionContent(option)}
|
|
313
|
-
<span class="option-label">{option.label}</span>
|
|
314
|
-
{#if detailById.get(String(option.value))}
|
|
315
|
-
<span class="option-detail">{detailById.get(String(option.value))}</span>
|
|
316
|
-
{/if}
|
|
317
|
-
{/snippet}
|
|
318
|
-
</Combobox>
|
|
319
|
-
{#if canClear}
|
|
320
|
-
<Button
|
|
321
|
-
variant="ghost"
|
|
322
|
-
size="sm"
|
|
323
|
-
class="clear"
|
|
324
|
-
aria-label={clearLabel ?? `Clear ${label}`}
|
|
325
|
-
onclick={clear}
|
|
326
|
-
>
|
|
327
|
-
×
|
|
328
|
-
</Button>
|
|
329
|
-
{/if}
|
|
330
|
-
</div>
|
|
331
|
-
{#if description}<p id={hintId} class="hint">{description}</p>{/if}
|
|
332
|
-
{#if onCreate}
|
|
333
|
-
<Button
|
|
334
|
-
variant="ghost"
|
|
335
|
-
size="sm"
|
|
336
|
-
class="create"
|
|
337
|
-
disabled={disabled || creating}
|
|
338
|
-
onclick={create}
|
|
339
|
-
>
|
|
340
|
-
+ {newLabel}
|
|
341
|
-
</Button>
|
|
342
|
-
{/if}
|
|
343
|
-
{#if error}<p id={errorId} class="error" role="alert">{error}</p>{/if}
|
|
344
|
-
</div>
|
|
345
|
-
|
|
346
|
-
<style>
|
|
347
|
-
.relation-input {
|
|
348
|
-
display: grid;
|
|
349
|
-
gap: var(--smrt-spacing-1, 4px);
|
|
350
|
-
min-width: 0;
|
|
351
|
-
}
|
|
352
|
-
.field {
|
|
353
|
-
position: relative;
|
|
354
|
-
}
|
|
355
|
-
.field :global(.clear) {
|
|
356
|
-
position: absolute;
|
|
357
|
-
right: var(--smrt-spacing-1, 4px);
|
|
358
|
-
bottom: var(--smrt-spacing-1, 4px);
|
|
359
|
-
}
|
|
360
|
-
.option-label,
|
|
361
|
-
.option-detail {
|
|
362
|
-
display: block;
|
|
363
|
-
}
|
|
364
|
-
.option-detail {
|
|
365
|
-
font-size: var(--smrt-typography-body-small-size, 0.75rem);
|
|
366
|
-
color: var(--smrt-color-on-surface-variant);
|
|
367
|
-
}
|
|
368
|
-
.hint,
|
|
369
|
-
.error {
|
|
370
|
-
margin: 0;
|
|
371
|
-
font-size: var(--smrt-typography-body-small-size, 0.75rem);
|
|
372
|
-
}
|
|
373
|
-
.hint {
|
|
374
|
-
color: var(--smrt-color-on-surface-variant);
|
|
375
|
-
}
|
|
376
|
-
.error {
|
|
377
|
-
color: var(--smrt-color-error);
|
|
378
|
-
}
|
|
379
|
-
</style>
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { type ControlInteractionOptions } from '@happyvertical/smrt-ui/forms';
|
|
2
|
-
import type { RelationOption } from './types.js';
|
|
3
|
-
/**
|
|
4
|
-
* RelationInput - searchable single-select for relation (foreign key) fields.
|
|
5
|
-
*
|
|
6
|
-
* Presentation only: the caller supplies `search` (and optionally `resolve`),
|
|
7
|
-
* so the same lookup an assistant uses to turn "Acme" into a record id backs
|
|
8
|
-
* the picker. Built on the smrt-ui `Combobox`, which supplies the ARIA 1.2
|
|
9
|
-
* combobox pattern (arrow keys, Enter, Escape, `aria-activedescendant`) and
|
|
10
|
-
* registers with the rich Form's control registry. The form posts the
|
|
11
|
-
* committed id under `name`, never the visible label.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```svelte
|
|
15
|
-
* <RelationInput
|
|
16
|
-
* name="customerId"
|
|
17
|
-
* label="Customer"
|
|
18
|
-
* bind:value={customerId}
|
|
19
|
-
* search={(q) => customers.search(q)}
|
|
20
|
-
* resolve={(id) => customers.byId(id)}
|
|
21
|
-
* />
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export interface Props {
|
|
25
|
-
/** Field name; the selected id posts under it. */
|
|
26
|
-
name: string;
|
|
27
|
-
/** Visible label. */
|
|
28
|
-
label: string;
|
|
29
|
-
/** Selected record id (bindable). Empty string means no selection. */
|
|
30
|
-
value?: string;
|
|
31
|
-
/** Required field: hides the clear button and flags an empty value. */
|
|
32
|
-
required?: boolean;
|
|
33
|
-
/** Disabled state. */
|
|
34
|
-
disabled?: boolean;
|
|
35
|
-
/** Validation error message, shown below the field. */
|
|
36
|
-
error?: string;
|
|
37
|
-
/** Placeholder shown when nothing is selected. */
|
|
38
|
-
placeholder?: string;
|
|
39
|
-
/** Hint announced with the field. */
|
|
40
|
-
description?: string;
|
|
41
|
-
/** Look up records matching the text; called with '' when the list opens. */
|
|
42
|
-
search: (query: string) => Promise<RelationOption[]>;
|
|
43
|
-
/** Look up the current value's label when it has not been searched yet. */
|
|
44
|
-
resolve?: (id: string) => Promise<{
|
|
45
|
-
id: string;
|
|
46
|
-
label: string;
|
|
47
|
-
} | null>;
|
|
48
|
-
/**
|
|
49
|
-
* "New ..." action. Receives the text most recently searched for. May
|
|
50
|
-
* return the created record, which is then selected.
|
|
51
|
-
*/
|
|
52
|
-
onCreate?: (query: string) => void | RelationOption | Promise<void | RelationOption>;
|
|
53
|
-
/** Milliseconds to wait after typing before searching. */
|
|
54
|
-
debounceMs?: number;
|
|
55
|
-
/** Combobox interaction options (stable id, subject) for agents. */
|
|
56
|
-
interaction?: ControlInteractionOptions | false;
|
|
57
|
-
/** Text for the "New ..." button. */
|
|
58
|
-
createLabel?: string;
|
|
59
|
-
/** Accessible name for the clear button. */
|
|
60
|
-
clearLabel?: string;
|
|
61
|
-
/** Status text while searching. */
|
|
62
|
-
loadingText?: string;
|
|
63
|
-
/** Status text when nothing matches. */
|
|
64
|
-
emptyText?: string;
|
|
65
|
-
/** Status text when the search fails. */
|
|
66
|
-
errorText?: string;
|
|
67
|
-
/** Status text announcing how many results are listed. */
|
|
68
|
-
resultsText?: (count: number) => string;
|
|
69
|
-
/** Called when the selected id changes ('' when cleared). */
|
|
70
|
-
onchange?: (value: string) => void;
|
|
71
|
-
}
|
|
72
|
-
declare const RelationInput: import("svelte").Component<Props, {}, "value">;
|
|
73
|
-
type RelationInput = ReturnType<typeof RelationInput>;
|
|
74
|
-
export default RelationInput;
|
|
75
|
-
//# sourceMappingURL=RelationInput.svelte.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RelationInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/RelationInput.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,yBAAyB,EAI/B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,KAAK;IACpB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACrD,2EAA2E;IAC3E,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACxE;;;OAGG;IACH,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,KACV,IAAI,GAAG,cAAc,GAAG,OAAO,CAAC,IAAI,GAAG,cAAc,CAAC,CAAC;IAC5D,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAuPD,QAAA,MAAM,aAAa,gDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|