@svadmin/ui 0.72.0 → 0.74.0
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 +62 -22
- package/THIRD_PARTY_NOTICES.md +52 -0
- package/dist/app.css +8488 -6297
- package/dist/app.theme.css +8514 -6374
- package/dist/class-properties.generated.d.ts +1 -0
- package/dist/class-properties.generated.js +2 -0
- package/dist/classnames.d.ts +8 -0
- package/dist/classnames.js +26 -0
- package/dist/component-registry.d.ts +14977 -0
- package/dist/component-registry.js +216 -0
- package/dist/components/AboutSettings.svelte +7 -1
- package/dist/components/AboutSettings.svelte.d.ts +4 -1
- package/dist/components/ActivityFeed.svelte +9 -3
- package/dist/components/ActivityFeed.svelte.d.ts +3 -0
- package/dist/components/AdminApp.svelte +19 -10
- package/dist/components/AdminApp.svelte.d.ts +6 -0
- package/dist/components/ApiSettings.svelte +41 -5
- package/dist/components/ApprovalCenter.svelte +283 -0
- package/dist/components/ApprovalCenter.svelte.d.ts +10 -0
- package/dist/components/AuditLogViewer.svelte +3 -1
- package/dist/components/AutoForm.svelte +53 -9
- package/dist/components/AutoForm.svelte.d.ts +3 -0
- package/dist/components/AutoTable.svelte +846 -43
- package/dist/components/AutoTable.svelte.d.ts +22 -1
- package/dist/components/BoundRecordDetailDrawer.svelte +17 -2
- package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +4 -0
- package/dist/components/Breadcrumbs.svelte +11 -1
- package/dist/components/CancelTaskButton.svelte +12 -42
- package/dist/components/ColorPicker.svelte +73 -0
- package/dist/components/ColorPicker.svelte.d.ts +15 -0
- package/dist/components/ComboboxField.svelte +143 -34
- package/dist/components/ComboboxField.svelte.d.ts +4 -2
- package/dist/components/CreatePage.svelte +4 -0
- package/dist/components/CreatePage.svelte.d.ts +2 -0
- package/dist/components/DashboardView.svelte +99 -0
- package/dist/components/DashboardView.svelte.d.ts +14 -0
- package/dist/components/DatePicker.svelte +10 -0
- package/dist/components/DatePicker.svelte.d.ts +6 -0
- package/dist/components/DateRangeInput.svelte +155 -0
- package/dist/components/DateRangeInput.svelte.d.ts +35 -0
- package/dist/components/DateRangePicker.svelte +10 -0
- package/dist/components/DateRangePicker.svelte.d.ts +6 -0
- package/dist/components/DateTimeInput.svelte +134 -0
- package/dist/components/DateTimeInput.svelte.d.ts +27 -0
- package/dist/components/DateTimePicker.svelte +10 -0
- package/dist/components/DateTimePicker.svelte.d.ts +6 -0
- package/dist/components/DevTools.svelte +36 -28
- package/dist/components/DevframePanel.svelte +123 -0
- package/dist/components/DevframePanel.svelte.d.ts +3 -0
- package/dist/components/EditPage.svelte +4 -0
- package/dist/components/EditPage.svelte.d.ts +2 -0
- package/dist/components/ErrorSummary.svelte +52 -0
- package/dist/components/ErrorSummary.svelte.d.ts +14 -0
- package/dist/components/FieldRenderer.svelte +172 -99
- package/dist/components/FileUpload.svelte +412 -0
- package/dist/components/FileUpload.svelte.d.ts +43 -0
- package/dist/components/FilterBuilder.svelte +244 -265
- package/dist/components/FilterBuilder.svelte.d.ts +3 -0
- package/dist/components/FilterCollectionInput.svelte +80 -0
- package/dist/components/FilterCollectionInput.svelte.d.ts +10 -0
- package/dist/components/GanttChart.svelte +66 -14
- package/dist/components/GanttChart.svelte.d.ts +6 -0
- package/dist/components/Header.svelte +18 -2
- package/dist/components/ImportWizard.svelte +509 -100
- package/dist/components/ImportWizard.svelte.d.ts +11 -0
- package/dist/components/JsonSchemaForm.svelte +322 -98
- package/dist/components/JsonSchemaForm.svelte.d.ts +18 -1
- package/dist/components/KanbanBoard.svelte +195 -40
- package/dist/components/KanbanBoard.svelte.d.ts +9 -2
- package/dist/components/Layout.svelte +4 -3
- package/dist/components/LazyAutoForm.svelte +28 -0
- package/dist/components/LazyAutoForm.svelte.d.ts +8 -0
- package/dist/components/LazyAutoTable.svelte +28 -0
- package/dist/components/LazyAutoTable.svelte.d.ts +8 -0
- package/dist/components/LazySettingsPage.svelte +28 -0
- package/dist/components/LazySettingsPage.svelte.d.ts +8 -0
- package/dist/components/LazyShowPage.svelte +28 -0
- package/dist/components/LazyShowPage.svelte.d.ts +8 -0
- package/dist/components/LazyTaskQueueDrawer.svelte.d.ts +1 -1
- package/dist/components/ListPage.svelte +9 -1
- package/dist/components/ListPage.svelte.d.ts +5 -0
- package/dist/components/LoginPage.svelte +2 -2
- package/dist/components/MoneyInput.svelte +25 -0
- package/dist/components/MoneyInput.svelte.d.ts +20 -0
- package/dist/components/MultiTabKeepAlive.svelte +11 -6
- package/dist/components/NotificationsSettings.svelte +69 -2
- package/dist/components/NotificationsSettings.svelte.d.ts +25 -1
- package/dist/components/NumberInput.svelte +25 -0
- package/dist/components/NumberInput.svelte.d.ts +19 -0
- package/dist/components/NumericInput.svelte +110 -0
- package/dist/components/NumericInput.svelte.d.ts +23 -0
- package/dist/components/PercentInput.svelte +25 -0
- package/dist/components/PercentInput.svelte.d.ts +20 -0
- package/dist/components/PermissionMatrix.svelte +20 -1
- package/dist/components/PivotExportButton.svelte +157 -0
- package/dist/components/PivotExportButton.svelte.d.ts +16 -0
- package/dist/components/PivotTable.svelte +229 -74
- package/dist/components/PivotTable.svelte.d.ts +19 -2
- package/dist/components/QuickEditDrawer.svelte +4 -1
- package/dist/components/QuickEditDrawer.svelte.d.ts +2 -0
- package/dist/components/RadioGroup.svelte +121 -0
- package/dist/components/RadioGroup.svelte.d.ts +43 -0
- package/dist/components/RangeSlider.svelte +127 -0
- package/dist/components/RangeSlider.svelte.d.ts +22 -0
- package/dist/components/Rate.svelte +121 -0
- package/dist/components/Rate.svelte.d.ts +16 -0
- package/dist/components/RecordDetailDrawer.svelte +6 -2
- package/dist/components/RecordDetailDrawer.svelte.d.ts +4 -0
- package/dist/components/RelationPicker.svelte +341 -0
- package/dist/components/RelationPicker.svelte.d.ts +26 -0
- package/dist/components/ResourceOperationsPage.svelte +25 -19
- package/dist/components/ResourceOperationsPage.svelte.d.ts +5 -1
- package/dist/components/RetryTaskButton.svelte +12 -42
- package/dist/components/SecuritySettings.svelte +29 -5
- package/dist/components/SegmentedControl.svelte +145 -0
- package/dist/components/SegmentedControl.svelte.d.ts +38 -0
- package/dist/components/SettingsPage.svelte +3 -1
- package/dist/components/SettingsPage.svelte.d.ts +1 -0
- package/dist/components/ShowInferencer.svelte +4 -2
- package/dist/components/ShowInferencer.svelte.d.ts +2 -0
- package/dist/components/ShowPage.svelte +25 -6
- package/dist/components/ShowPage.svelte.d.ts +5 -0
- package/dist/components/Sidebar.svelte +5 -5
- package/dist/components/Slider.svelte +100 -0
- package/dist/components/Slider.svelte.d.ts +19 -0
- package/dist/components/SpreadsheetView.svelte +1037 -124
- package/dist/components/SpreadsheetView.svelte.d.ts +19 -0
- package/dist/components/StepsForm.svelte +5 -5
- package/dist/components/SvarAutoTable.svelte +39 -0
- package/dist/components/SvarAutoTable.svelte.d.ts +13 -0
- package/dist/components/SvarDataGrid.svelte +362 -0
- package/dist/components/SvarDataGrid.svelte.d.ts +57 -0
- package/dist/components/SvarGridCell.svelte +10 -0
- package/dist/components/SvarGridCell.svelte.d.ts +7 -0
- package/dist/components/SvarResourceRead.svelte +50 -0
- package/dist/components/SvarResourceRead.svelte.d.ts +23 -0
- package/dist/components/SvarResourceTable.svelte +63 -0
- package/dist/components/SvarResourceTable.svelte.d.ts +42 -0
- package/dist/components/SvarResourceTableView.svelte +364 -0
- package/dist/components/SvarResourceTableView.svelte.d.ts +4 -0
- package/dist/components/TagsInput.svelte +178 -0
- package/dist/components/TagsInput.svelte.d.ts +17 -0
- package/dist/components/TaskQueueDrawer.svelte +72 -16
- package/dist/components/TimePicker.svelte +10 -0
- package/dist/components/TimePicker.svelte.d.ts +6 -0
- package/dist/components/TreeSelect.svelte +440 -46
- package/dist/components/TreeSelect.svelte.d.ts +6 -1
- package/dist/components/TreeTable.svelte +484 -82
- package/dist/components/TreeTable.svelte.d.ts +24 -1
- package/dist/components/TwoFactorAuthPage.svelte +85 -48
- package/dist/components/TwoFactorAuthPage.svelte.d.ts +12 -1
- package/dist/components/VirtualTable.svelte +116 -20
- package/dist/components/VirtualTable.svelte.d.ts +5 -0
- package/dist/components/account/CompanyProfilePage.svelte +74 -38
- package/dist/components/account/GetStartedPage.svelte +2 -1
- package/dist/components/account/ImportMembersPage.svelte +22 -167
- package/dist/components/account/ImportMembersPage.svelte.d.ts +4 -1
- package/dist/components/account/MemberDirectory.svelte +156 -0
- package/dist/components/account/MemberDirectory.svelte.d.ts +19 -0
- package/dist/components/account/MembersStarterPage.svelte +9 -55
- package/dist/components/account/MembersStarterPage.svelte.d.ts +5 -1
- package/dist/components/account/SecurityLogPage.svelte +4 -1
- package/dist/components/account/SettingsPlainPage.svelte +7 -38
- package/dist/components/account/SettingsPlainPage.svelte.d.ts +4 -1
- package/dist/components/account/SettingsSidebarPage.svelte +7 -20
- package/dist/components/account/TeamMembersPage.svelte +4 -20
- package/dist/components/account/TeamMembersPage.svelte.d.ts +5 -1
- package/dist/components/account/UserProfilePage.svelte +3 -25
- package/dist/components/account/UserProfilePage.svelte.d.ts +17 -2
- package/dist/components/auto-table-grid.d.ts +26 -0
- package/dist/components/auto-table-grid.js +1 -0
- package/dist/components/buttons/ExportButton.svelte +56 -12
- package/dist/components/buttons/ExportButton.svelte.d.ts +11 -0
- package/dist/components/content/ContentPageHeader.svelte +12 -9
- package/dist/components/content/ContentPageShell.svelte +6 -9
- package/dist/components/content/FileBrowser.svelte +35 -0
- package/dist/components/content/FileBrowser.svelte.d.ts +14 -0
- package/dist/components/content/MetricBlock.svelte +10 -13
- package/dist/components/content/NetworkTable.svelte +3 -3
- package/dist/components/content/NetworkTable.svelte.d.ts +1 -0
- package/dist/components/content/PageToolbar.svelte +6 -10
- package/dist/components/content/ReferenceNotice.svelte +5 -0
- package/dist/components/content/ReferenceNotice.svelte.d.ts +18 -0
- package/dist/components/content/SectionHeader.svelte +8 -13
- package/dist/components/content/SettingsFieldRow.svelte +9 -23
- package/dist/components/content/SettingsGroup.svelte +11 -27
- package/dist/components/content/StatusBadge.svelte +3 -8
- package/dist/components/content/WorkspaceLayout.svelte +8 -12
- package/dist/components/enterprise/filter-tree.d.ts +48 -0
- package/dist/components/enterprise/filter-tree.js +224 -0
- package/dist/components/enterprise/json-schema-form.d.ts +39 -0
- package/dist/components/enterprise/json-schema-form.js +195 -0
- package/dist/components/enterprise/recursive-schema-form.d.ts +30 -0
- package/dist/components/enterprise/recursive-schema-form.js +196 -0
- package/dist/components/enterprise/spreadsheet-formula.d.ts +12 -0
- package/dist/components/enterprise/spreadsheet-formula.js +245 -0
- package/dist/components/export-reference.d.ts +2 -0
- package/dist/components/export-reference.js +9 -0
- package/dist/components/fieldComponentMap.js +2 -0
- package/dist/components/fields/DateField.svelte +12 -12
- package/dist/components/fields/date-display.d.ts +6 -0
- package/dist/components/fields/date-display.js +24 -0
- package/dist/components/fields/date-field-value.d.ts +2 -0
- package/dist/components/fields/date-field-value.js +13 -0
- package/dist/components/json-schema-form-state.d.ts +37 -0
- package/dist/components/json-schema-form-state.js +168 -0
- package/dist/components/network/TeamCrewTablePage.svelte +6 -1
- package/dist/components/network/UserCardsNFTPage.svelte +2 -0
- package/dist/components/profile/PublicProfilePage.svelte +2 -0
- package/dist/components/record-detail.svelte.d.ts +3 -0
- package/dist/components/record-detail.svelte.js +10 -4
- package/dist/components/saved-list-views.d.ts +74 -1
- package/dist/components/saved-list-views.js +191 -3
- package/dist/components/svar-grid-cells.d.ts +14 -0
- package/dist/components/svar-grid-cells.js +2 -0
- package/dist/components/svar-grid-contract.d.ts +80 -0
- package/dist/components/svar-grid-contract.js +159 -0
- package/dist/components/svar-grid-interactions.d.ts +54 -0
- package/dist/components/svar-grid-interactions.js +174 -0
- package/dist/components/svar-grid-loading.d.ts +40 -0
- package/dist/components/svar-grid-loading.js +180 -0
- package/dist/components/svar-grid-operations.d.ts +37 -0
- package/dist/components/svar-grid-operations.js +186 -0
- package/dist/components/svar-grid-panes.d.ts +34 -0
- package/dist/components/svar-grid-panes.js +66 -0
- package/dist/components/svar-grid-resource-loading.d.ts +18 -0
- package/dist/components/svar-grid-resource-loading.js +68 -0
- package/dist/components/svar-grid-resource-session.svelte.d.ts +29 -0
- package/dist/components/svar-grid-resource-session.svelte.js +61 -0
- package/dist/components/table-contract.d.ts +12 -1
- package/dist/components/task-action.svelte.d.ts +13 -0
- package/dist/components/task-action.svelte.js +73 -0
- package/dist/components/ui/DesignSystemShowcase.svelte +164 -0
- package/dist/components/ui/DesignSystemShowcase.svelte.d.ts +3 -0
- package/dist/components/ui/badge/badge-variants.d.ts +3 -2
- package/dist/components/ui/badge/badge-variants.js +2 -1
- package/dist/components/ui/badge/badge.svelte +3 -0
- package/dist/components/ui/badge/badge.svelte.d.ts +1 -0
- package/dist/components/ui/button/button-variants.d.ts +5 -4
- package/dist/components/ui/button/button-variants.js +7 -2
- package/dist/components/ui/checkbox/checkbox.svelte +5 -1
- package/dist/components/ui/checkbox/checkbox.svelte.d.ts +5 -1
- package/dist/components/ui/input/input.svelte +8 -6
- package/dist/components/ui/input/input.svelte.d.ts +4 -1
- package/dist/components/ui/select/select.svelte +4 -1
- package/dist/components/ui/select/select.svelte.d.ts +2 -1
- package/dist/components/ui/textarea/textarea.svelte +2 -1
- package/dist/components.css +3110 -0
- package/dist/date-input-policy.d.ts +4 -0
- package/dist/date-input-policy.js +35 -0
- package/dist/design-contract.d.ts +13 -0
- package/dist/design-contract.js +11 -0
- package/dist/design-system-components.d.ts +100 -0
- package/dist/design-system-components.js +101 -0
- package/dist/design-system.d.ts +15 -0
- package/dist/design-system.js +7 -0
- package/dist/devtools-bridge.d.ts +80 -0
- package/dist/devtools-bridge.js +92 -0
- package/dist/index.d.ts +50 -1
- package/dist/index.js +32 -2
- package/dist/page-chrome.css +12 -49
- package/dist/recipes.d.ts +960 -0
- package/dist/recipes.js +264 -0
- package/dist/rendering/index.d.ts +87 -0
- package/dist/rendering/index.js +117 -0
- package/dist/styles/aliases.css +49 -0
- package/dist/styles/compatibility.epilogue.css +384 -0
- package/dist/styles/compatibility.prelude.css +5513 -0
- package/dist/styles/conditional-states.css +28 -0
- package/dist/styles/conditional-utilities.css +22 -0
- package/dist/tree-table-model.d.ts +24 -0
- package/dist/tree-table-model.js +111 -0
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/package.json +40 -10
- package/styles-compatibility.json +12 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
export const SCHEMA_FORM_LIMITS = Object.freeze({ depth: 32, nodes: 10_000, characters: 1_048_576 });
|
|
2
|
+
const unsafe = new Set(['__proto__', 'prototype', 'constructor']);
|
|
3
|
+
const record = (value) => value !== null
|
|
4
|
+
&& typeof value === 'object' && !Array.isArray(value)
|
|
5
|
+
&& (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
6
|
+
function safeKey(key) {
|
|
7
|
+
if (unsafe.has(key))
|
|
8
|
+
throw new Error('Unsafe form field');
|
|
9
|
+
}
|
|
10
|
+
/** wire 模式仅省略对象的 undefined;数组的空洞/undefined 不能偷偷转成 null。 */
|
|
11
|
+
function copy(value, wire) {
|
|
12
|
+
const active = new Set();
|
|
13
|
+
let nodes = 0, characters = 0;
|
|
14
|
+
function visit(current, depth) {
|
|
15
|
+
if (++nodes > SCHEMA_FORM_LIMITS.nodes || depth > SCHEMA_FORM_LIMITS.depth)
|
|
16
|
+
throw new Error('Form value exceeds limits');
|
|
17
|
+
if (current === undefined && !wire)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (current === null || typeof current === 'boolean')
|
|
20
|
+
return current;
|
|
21
|
+
if (typeof current === 'number' && Number.isFinite(current))
|
|
22
|
+
return current;
|
|
23
|
+
if (typeof current === 'string') {
|
|
24
|
+
characters += current.length;
|
|
25
|
+
if (characters > SCHEMA_FORM_LIMITS.characters)
|
|
26
|
+
throw new Error('Form text exceeds limits');
|
|
27
|
+
return current;
|
|
28
|
+
}
|
|
29
|
+
if ((!record(current) && !Array.isArray(current)) || active.has(current))
|
|
30
|
+
throw new Error('Expected acyclic plain form data');
|
|
31
|
+
active.add(current);
|
|
32
|
+
try {
|
|
33
|
+
if (Reflect.ownKeys(current).some(key => typeof key === 'symbol'))
|
|
34
|
+
throw new Error('Symbol values are not JSON fields');
|
|
35
|
+
if (Array.isArray(current)) {
|
|
36
|
+
if (current.length > SCHEMA_FORM_LIMITS.nodes)
|
|
37
|
+
throw new Error('Form array exceeds limits');
|
|
38
|
+
const result = [];
|
|
39
|
+
for (let index = 0; index < current.length; index++) {
|
|
40
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, String(index));
|
|
41
|
+
if (!descriptor || !('value' in descriptor))
|
|
42
|
+
throw new Error('Sparse arrays and accessors are not form data');
|
|
43
|
+
result.push(visit(descriptor.value, depth + 1));
|
|
44
|
+
}
|
|
45
|
+
if (Object.keys(current).some(key => !/^(0|[1-9][0-9]*)$/u.test(key) || Number(key) >= current.length)) {
|
|
46
|
+
throw new Error('Array properties are not JSON fields');
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
const result = {};
|
|
51
|
+
for (const key of Object.keys(current)) {
|
|
52
|
+
safeKey(key);
|
|
53
|
+
characters += key.length;
|
|
54
|
+
if (characters > SCHEMA_FORM_LIMITS.characters)
|
|
55
|
+
throw new Error('Form text exceeds limits');
|
|
56
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
57
|
+
if (!descriptor || !('value' in descriptor))
|
|
58
|
+
throw new Error('Accessors are not form data');
|
|
59
|
+
if (!wire || descriptor.value !== undefined)
|
|
60
|
+
result[key] = visit(descriptor.value, depth + 1);
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
active.delete(current);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return visit(value, 0);
|
|
69
|
+
}
|
|
70
|
+
/** Svelte 代理也通过普通数据属性读取;回调永远收不到活跃草稿的引用。 */
|
|
71
|
+
export function schemaFormSnapshot(value) {
|
|
72
|
+
if (!record(value))
|
|
73
|
+
throw new Error('Form root must be an object');
|
|
74
|
+
return copy(value, true);
|
|
75
|
+
}
|
|
76
|
+
/** 默认值只填“缺失的键”;显式 undefined/null/false/0/空串都表示调用方的决定。 */
|
|
77
|
+
export function initializeSchemaForm(schema, value) {
|
|
78
|
+
if (!record(value))
|
|
79
|
+
throw new Error('Form root must be an object');
|
|
80
|
+
const active = new Set();
|
|
81
|
+
let nodes = 0;
|
|
82
|
+
function visit(node, current, present, required, depth) {
|
|
83
|
+
if (++nodes > SCHEMA_FORM_LIMITS.nodes || depth > SCHEMA_FORM_LIMITS.depth || !record(node) || active.has(node)) {
|
|
84
|
+
throw new Error('Form schema exceeds supported traversal limits');
|
|
85
|
+
}
|
|
86
|
+
active.add(node);
|
|
87
|
+
try {
|
|
88
|
+
if (!present) {
|
|
89
|
+
if (Object.hasOwn(node, 'default'))
|
|
90
|
+
current = copy(node.default, false);
|
|
91
|
+
else if (node.type === 'object' || node.properties)
|
|
92
|
+
current = {};
|
|
93
|
+
else if (node.type === 'array')
|
|
94
|
+
current = [];
|
|
95
|
+
else if (node.type === 'boolean' && required)
|
|
96
|
+
current = false;
|
|
97
|
+
}
|
|
98
|
+
if (record(current) && (node.type === 'object' || node.properties)) {
|
|
99
|
+
let result = current;
|
|
100
|
+
for (const [key, child] of Object.entries(node.properties ?? {})) {
|
|
101
|
+
safeKey(key);
|
|
102
|
+
const exists = Object.hasOwn(current, key);
|
|
103
|
+
const next = visit(child, exists ? current[key] : undefined, exists, node.required?.includes(key) ?? false, depth + 1);
|
|
104
|
+
if ((!exists && next !== undefined) || (exists && next !== current[key])) {
|
|
105
|
+
if (result === current)
|
|
106
|
+
result = { ...current };
|
|
107
|
+
result[key] = next;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(current) && node.type === 'array' && node.items) {
|
|
113
|
+
if (current.length > SCHEMA_FORM_LIMITS.nodes)
|
|
114
|
+
throw new Error('Form array exceeds limits');
|
|
115
|
+
const array = current;
|
|
116
|
+
const items = array.map(item => visit(node.items, item, true, true, depth + 1));
|
|
117
|
+
return items.some((item, index) => item !== array[index]) ? items : array;
|
|
118
|
+
}
|
|
119
|
+
return current;
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
active.delete(node);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return visit(schema, value, true, true, 0);
|
|
126
|
+
}
|
|
127
|
+
export function schemaFormArrayItem(schema) {
|
|
128
|
+
// 新增对象项也初始化其嵌套默认值及必填 false;数字留空,不猜测为 0。
|
|
129
|
+
const seed = Object.hasOwn(schema, 'default') ? copy(schema.default, false)
|
|
130
|
+
: schema.type === 'object' || schema.properties ? {}
|
|
131
|
+
: schema.type === 'array' ? [] : schema.type === 'boolean' ? false : schema.type === 'number' || schema.type === 'integer' ? undefined : '';
|
|
132
|
+
return initializeSchemaForm({ properties: { item: schema }, required: ['item'] }, { item: seed })['item'];
|
|
133
|
+
}
|
|
134
|
+
export function writeSchemaFormPath(value, path, nextValue) {
|
|
135
|
+
if (!path.length || path.length > SCHEMA_FORM_LIMITS.depth)
|
|
136
|
+
throw new Error('Invalid form path');
|
|
137
|
+
const next = copy(value, false);
|
|
138
|
+
let target = next;
|
|
139
|
+
for (const [index, key] of path.entries()) {
|
|
140
|
+
safeKey(key);
|
|
141
|
+
if (Array.isArray(target) && (!/^(0|[1-9][0-9]*)$/u.test(key) || Number(key) >= target.length))
|
|
142
|
+
throw new Error('Invalid array field');
|
|
143
|
+
if (index === path.length - 1) {
|
|
144
|
+
Reflect.set(target, key, copy(nextValue, false));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
let child = Object.hasOwn(target, key) ? Reflect.get(target, key) : undefined;
|
|
148
|
+
if (!record(child) && !Array.isArray(child)) {
|
|
149
|
+
child = {};
|
|
150
|
+
Reflect.set(target, key, child);
|
|
151
|
+
}
|
|
152
|
+
target = child;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return next;
|
|
156
|
+
}
|
|
157
|
+
/** option.value 是索引,不是 String(value):1 与 "1"、null 与 "null" 必须可区分。 */
|
|
158
|
+
export function schemaFormEnumIndex(choices, value) {
|
|
159
|
+
const index = choices.findIndex(choice => choice === value);
|
|
160
|
+
return index === -1 ? '' : String(index);
|
|
161
|
+
}
|
|
162
|
+
export function schemaFormEnumValue(choices, index) {
|
|
163
|
+
if (index === '')
|
|
164
|
+
return undefined;
|
|
165
|
+
if (!/^(0|[1-9][0-9]*)$/u.test(index) || !Object.hasOwn(choices, Number(index)))
|
|
166
|
+
throw new Error('Invalid form choice');
|
|
167
|
+
return choices[Number(index)];
|
|
168
|
+
}
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
import type { NetworkColumn } from '../content/NetworkTable.types.js';
|
|
10
10
|
import { Avatar } from '../ui/avatar/index.js';
|
|
11
11
|
import { Badge } from '../ui/badge/index.js';
|
|
12
|
+
import ReferenceNotice from '../content/ReferenceNotice.svelte';
|
|
13
|
+
import { captureAdminContext } from '@svadmin/core';
|
|
14
|
+
import { downloadRows } from '../export-reference.js';
|
|
15
|
+
const context = captureAdminContext();
|
|
12
16
|
|
|
13
17
|
interface CrewMember { id: string; name: string; email: string; role: string; department: string; status: 'active' | 'away' | 'offline'; projects: number; }
|
|
14
18
|
const i18n = useTranslation();
|
|
@@ -31,7 +35,8 @@
|
|
|
31
35
|
{/snippet}
|
|
32
36
|
|
|
33
37
|
<ContentPageShell pageId="network-team-crew" width="wide">
|
|
34
|
-
<
|
|
38
|
+
<ReferenceNotice />
|
|
39
|
+
<div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-1004c0c3954c svadmin-u-020ba687fa12 svadmin-u-64cac80d2e9a svadmin-u-3b9871a0bf93"><ContentPageHeader title={i18n.t('network.teamCrew')} description={i18n.t('network.teamCrewDescription')} /><div class="svadmin-u-60fbb7713999 svadmin-u-1eb5c6df38c1 svadmin-u-77a2a20e90d4"><Button variant="outline" size="sm" onclick={() => downloadRows('sample-team.json', filtered)}><Download class="svadmin-u-783b0d9d1e2c" />{i18n.t('common.export')}</Button><Button size="sm" onclick={() => context.navigate('/account/members/team-members')}><UserPlus class="svadmin-u-783b0d9d1e2c" />{i18n.t('account.inviteMember')}</Button></div></div>
|
|
35
40
|
<FilterToolbar bind:query placeholder={i18n.t('common.search')} />
|
|
36
41
|
<NetworkTable rows={filtered} {columns} {row} />
|
|
37
42
|
</ContentPageShell>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import ContentPageHeader from '../content/ContentPageHeader.svelte';
|
|
5
5
|
import FilterToolbar from '../content/FilterToolbar.svelte';
|
|
6
6
|
import DataState from '../content/DataState.svelte';
|
|
7
|
+
import ReferenceNotice from '../content/ReferenceNotice.svelte';
|
|
7
8
|
import NetworkUserCard from '../content/NetworkUserCard.svelte';
|
|
8
9
|
import type { NetworkUser } from '../content/NetworkUserCard.svelte';
|
|
9
10
|
|
|
@@ -24,6 +25,7 @@
|
|
|
24
25
|
</script>
|
|
25
26
|
|
|
26
27
|
<ContentPageShell pageId="network-user-cards" width="wide">
|
|
28
|
+
<ReferenceNotice />
|
|
27
29
|
<ContentPageHeader title={i18n.t('network.userCards')} description={i18n.t('network.userCardsDescription')} />
|
|
28
30
|
<FilterToolbar bind:query placeholder={i18n.t('common.search')} clearLabel={i18n.locale === 'zh-CN' ? '清除搜索' : 'Clear search'} />
|
|
29
31
|
{#if filtered.length === 0}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import ContentPageShell from '../content/ContentPageShell.svelte';
|
|
10
10
|
import ContentPageHeader from '../content/ContentPageHeader.svelte';
|
|
11
11
|
import { referenceDemoData } from '../../reference-data.js';
|
|
12
|
+
import ReferenceNotice from '../content/ReferenceNotice.svelte';
|
|
12
13
|
|
|
13
14
|
type ProfileVariant = 'default' | 'company' | 'gamer';
|
|
14
15
|
type ProfileTab = 'projects' | 'activity' | 'teams';
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
</script>
|
|
36
37
|
|
|
37
38
|
<ContentPageShell {pageId} width="wide">
|
|
39
|
+
<ReferenceNotice />
|
|
38
40
|
<ContentPageHeader title={profileData.name} eyebrow={i18n.t('publicProfile.title')} description={profileData.tagline} />
|
|
39
41
|
{#if showSections}
|
|
40
42
|
<div class="svadmin-u-f3c543ad5fe9 svadmin-u-0d304f904cb0 svadmin-u-bf1b5b54929e"><ProfileCard {...profileData} {variant} /><ProfileVariantSections {variant} /></div>
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import { type ResourceRendering } from '../rendering/index.js';
|
|
1
2
|
/** Views mount this only when they have an explicit record ID. */
|
|
2
3
|
export declare function useRecordDetail(options: () => {
|
|
3
4
|
resourceName: string;
|
|
4
5
|
id: string | number;
|
|
6
|
+
rendering?: ResourceRendering | undefined;
|
|
5
7
|
}): {
|
|
6
8
|
query: import("@tanstack/query-core").QueryObserverResult<import("@svadmin/core").GetOneResult<import("@svadmin/core").BaseRecord & {
|
|
7
9
|
id: string | number;
|
|
8
10
|
}>, unknown> & {
|
|
9
11
|
overtime: import("@svadmin/core").OvertimeResult;
|
|
10
12
|
};
|
|
13
|
+
readonly rendering: ResourceRendering | undefined;
|
|
11
14
|
readonly resource: import("@svadmin/core").ResourceDefinition;
|
|
12
15
|
readonly fields: import("@svadmin/core").FieldDefinition[];
|
|
13
16
|
readonly canRead: boolean;
|
|
@@ -1,20 +1,26 @@
|
|
|
1
|
+
import { bindResourceRendering } from '../rendering/index.js';
|
|
1
2
|
import { captureAdminContext, useOne, useCan, useResourceContract } from '@svadmin/core';
|
|
2
3
|
/** Views mount this only when they have an explicit record ID. */
|
|
3
4
|
export function useRecordDetail(options) {
|
|
4
5
|
const context = captureAdminContext();
|
|
5
6
|
const binding = useResourceContract(() => options().resourceName);
|
|
7
|
+
const rendering = $derived(bindResourceRendering(options().rendering, binding.resource));
|
|
6
8
|
const resource = $derived(context.getResource(options().resourceName));
|
|
7
9
|
const permission = useCan(() => ({
|
|
8
10
|
resource: options().resourceName, action: 'show', id: options().id,
|
|
9
11
|
queryOptions: { enabled: resource.canShow !== false },
|
|
10
12
|
}));
|
|
11
13
|
const canRead = $derived(resource.canShow !== false && permission.allowed === true);
|
|
12
|
-
const query = useOne(() =>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const query = useOne(() => {
|
|
15
|
+
void rendering;
|
|
16
|
+
return {
|
|
17
|
+
resource: binding.resource, id: options().id, dataProviderName: binding.dataProviderName,
|
|
18
|
+
queryOptions: { enabled: canRead },
|
|
19
|
+
};
|
|
20
|
+
});
|
|
16
21
|
return {
|
|
17
22
|
query,
|
|
23
|
+
get rendering() { return rendering; },
|
|
18
24
|
get resource() { return resource; },
|
|
19
25
|
get fields() { return resource.fields.filter(field => field.showInShow !== false); },
|
|
20
26
|
get canRead() { return canRead; },
|
|
@@ -14,13 +14,86 @@ export interface SavedListView {
|
|
|
14
14
|
id: string;
|
|
15
15
|
name: string;
|
|
16
16
|
state: SavedListViewState;
|
|
17
|
+
version?: number;
|
|
18
|
+
source?: 'local' | 'team' | 'system';
|
|
19
|
+
readOnly?: boolean;
|
|
20
|
+
default?: boolean;
|
|
21
|
+
access?: SavedListViewAccess;
|
|
22
|
+
}
|
|
23
|
+
export type SavedListViewAccessMode = 'team' | 'organization' | 'restricted';
|
|
24
|
+
export interface SavedListViewAccess {
|
|
25
|
+
mode: SavedListViewAccessMode;
|
|
26
|
+
subjectIds: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface SavedListViewSubject {
|
|
29
|
+
id: string;
|
|
30
|
+
label: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface SavedListViewMutation {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
state: SavedListViewState;
|
|
37
|
+
/** null 仅允许创建;更新必须提供读取到的版本。 */
|
|
38
|
+
expectedVersion: number | null;
|
|
39
|
+
source: 'team' | 'system';
|
|
40
|
+
}
|
|
41
|
+
export interface SavedListViewMutationSuccess {
|
|
42
|
+
ok: true;
|
|
43
|
+
view: SavedListView;
|
|
44
|
+
version: number;
|
|
45
|
+
}
|
|
46
|
+
export interface SavedListViewMutationConflict {
|
|
47
|
+
ok: false;
|
|
48
|
+
code: 'VERSION_CONFLICT';
|
|
49
|
+
current: SavedListView;
|
|
50
|
+
version: number;
|
|
51
|
+
}
|
|
52
|
+
export interface SavedListViewRemoveSuccess {
|
|
53
|
+
ok: true;
|
|
54
|
+
id: string;
|
|
55
|
+
version: number;
|
|
56
|
+
}
|
|
57
|
+
export interface SavedListViewProvider {
|
|
58
|
+
list: (scope: ListPreferenceScope) => Promise<unknown>;
|
|
59
|
+
/** 可选的成员目录查询;授权仍由宿主服务端负责。 */
|
|
60
|
+
listAccessSubjects?: (scope: ListPreferenceScope, input: {
|
|
61
|
+
query?: string;
|
|
62
|
+
limit?: number;
|
|
63
|
+
}) => Promise<unknown>;
|
|
64
|
+
save?: (scope: ListPreferenceScope, mutation: SavedListViewMutation) => Promise<unknown>;
|
|
65
|
+
remove?: (scope: ListPreferenceScope, input: {
|
|
66
|
+
id: string;
|
|
67
|
+
expectedVersion: number;
|
|
68
|
+
}) => Promise<unknown>;
|
|
69
|
+
setDefault?: (scope: ListPreferenceScope, input: {
|
|
70
|
+
id: string;
|
|
71
|
+
source: 'team' | 'system';
|
|
72
|
+
expectedVersion: number;
|
|
73
|
+
default: boolean;
|
|
74
|
+
}) => Promise<unknown>;
|
|
75
|
+
updateAccess?: (scope: ListPreferenceScope, input: {
|
|
76
|
+
id: string;
|
|
77
|
+
source: 'team' | 'system';
|
|
78
|
+
expectedVersion: number;
|
|
79
|
+
access: SavedListViewAccess;
|
|
80
|
+
}) => Promise<unknown>;
|
|
17
81
|
}
|
|
18
82
|
export interface ListPreferenceScope {
|
|
19
83
|
resourceName: string;
|
|
20
84
|
providerName: string;
|
|
21
85
|
tenantIdentity?: string | number;
|
|
86
|
+
identityKey?: string;
|
|
22
87
|
}
|
|
23
|
-
export declare function
|
|
88
|
+
export declare function decodeSavedListViewAccess(value: unknown): SavedListViewAccess | undefined;
|
|
89
|
+
export declare function decodeSavedListViewSubjects(value: unknown): SavedListViewSubject[] | undefined;
|
|
90
|
+
export declare function readSavedListViews(raw: string | null, allowedColumnIds: Set<string>, source?: SavedListView['source']): SavedListView[];
|
|
91
|
+
export declare function decodeSavedListViewMutationResult(value: unknown, allowedColumnIds: Set<string>, expected?: Pick<SavedListViewMutation, 'id' | 'source' | 'expectedVersion'>): SavedListViewMutationSuccess | SavedListViewMutationConflict | undefined;
|
|
92
|
+
export declare function decodeSavedListViewRemoveResult(value: unknown, expected: {
|
|
93
|
+
id: string;
|
|
94
|
+
expectedVersion: number;
|
|
95
|
+
}): SavedListViewRemoveSuccess | undefined;
|
|
96
|
+
export declare function decodeRemoteSavedListViews(value: unknown, allowedColumnIds: Set<string>, writable?: boolean): SavedListView[];
|
|
24
97
|
export declare function serializeSavedListViews(views: SavedListView[]): string;
|
|
25
98
|
export declare function cloneSavedListViewState(state: SavedListViewState): SavedListViewState;
|
|
26
99
|
export declare function listPreferenceScopeId(scope: ListPreferenceScope): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { snapshotPlainData } from '@svadmin/core/schema';
|
|
1
2
|
const STORAGE_VERSION = 1;
|
|
2
3
|
const MAX_SAVED_VIEWS = 25;
|
|
3
4
|
const MAX_FILTER_NODES = 200;
|
|
@@ -91,7 +92,63 @@ function parseState(value, allowedColumnIds) {
|
|
|
91
92
|
columnOrder,
|
|
92
93
|
};
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
+
function parseAccess(value) {
|
|
96
|
+
if (!isRecord(value) || Object.keys(value).some(key => key !== 'mode' && key !== 'subjectIds'))
|
|
97
|
+
return;
|
|
98
|
+
const mode = value['mode'];
|
|
99
|
+
const ids = value['subjectIds'];
|
|
100
|
+
if ((mode !== 'team' && mode !== 'organization' && mode !== 'restricted')
|
|
101
|
+
|| !Array.isArray(ids) || ids.length > 200)
|
|
102
|
+
return;
|
|
103
|
+
const subjectIds = [];
|
|
104
|
+
for (const id of ids) {
|
|
105
|
+
if (typeof id !== 'string' || !id.trim() || id !== id.trim() || id.length > 200
|
|
106
|
+
|| subjectIds.includes(id))
|
|
107
|
+
return;
|
|
108
|
+
subjectIds.push(id);
|
|
109
|
+
}
|
|
110
|
+
if ((mode === 'restricted') !== (subjectIds.length > 0))
|
|
111
|
+
return;
|
|
112
|
+
return { mode, subjectIds };
|
|
113
|
+
}
|
|
114
|
+
export function decodeSavedListViewAccess(value) {
|
|
115
|
+
try {
|
|
116
|
+
return parseAccess(snapshotPlainData(value));
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export function decodeSavedListViewSubjects(value) {
|
|
123
|
+
try {
|
|
124
|
+
const snapshot = snapshotPlainData(value);
|
|
125
|
+
if (!Array.isArray(snapshot) || snapshot.length > 200)
|
|
126
|
+
return;
|
|
127
|
+
const seen = new Set();
|
|
128
|
+
const subjects = [];
|
|
129
|
+
for (const entry of snapshot) {
|
|
130
|
+
if (!isRecord(entry)
|
|
131
|
+
|| Object.keys(entry).some(key => !['id', 'label', 'description'].includes(key))
|
|
132
|
+
|| typeof entry['id'] !== 'string' || !entry['id'].trim() || entry['id'] !== entry['id'].trim()
|
|
133
|
+
|| entry['id'].length > 200 || seen.has(entry['id'])
|
|
134
|
+
|| typeof entry['label'] !== 'string' || !entry['label'].trim() || entry['label'].length > 200
|
|
135
|
+
|| (entry['description'] !== undefined
|
|
136
|
+
&& (typeof entry['description'] !== 'string' || entry['description'].length > 500)))
|
|
137
|
+
return;
|
|
138
|
+
seen.add(entry['id']);
|
|
139
|
+
subjects.push({
|
|
140
|
+
id: entry['id'],
|
|
141
|
+
label: entry['label'].trim(),
|
|
142
|
+
...(entry['description'] === undefined ? {} : { description: entry['description'] }),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return subjects;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export function readSavedListViews(raw, allowedColumnIds, source = 'local') {
|
|
95
152
|
if (!raw)
|
|
96
153
|
return [];
|
|
97
154
|
try {
|
|
@@ -110,8 +167,20 @@ export function readSavedListViews(raw, allowedColumnIds) {
|
|
|
110
167
|
const state = parseState(entry['state'], allowedColumnIds);
|
|
111
168
|
if (!state)
|
|
112
169
|
continue;
|
|
170
|
+
const access = entry['access'] === undefined ? undefined : parseAccess(entry['access']);
|
|
171
|
+
if (entry['access'] !== undefined && !access)
|
|
172
|
+
continue;
|
|
113
173
|
seenIds.add(entry['id']);
|
|
114
|
-
views.push({
|
|
174
|
+
views.push({
|
|
175
|
+
id: entry['id'], name: entry['name'].trim(), state,
|
|
176
|
+
...(isPositiveInteger(entry['version'], Number.MAX_SAFE_INTEGER) ? { version: entry['version'] } : {}),
|
|
177
|
+
...(source !== 'local' ? {
|
|
178
|
+
source: entry['source'] === 'system' ? 'system' : 'team',
|
|
179
|
+
readOnly: true,
|
|
180
|
+
...(entry['default'] === true ? { default: true } : {}),
|
|
181
|
+
...(access === undefined ? {} : { access }),
|
|
182
|
+
} : {}),
|
|
183
|
+
});
|
|
115
184
|
}
|
|
116
185
|
return views;
|
|
117
186
|
}
|
|
@@ -121,6 +190,124 @@ export function readSavedListViews(raw, allowedColumnIds) {
|
|
|
121
190
|
throw error;
|
|
122
191
|
}
|
|
123
192
|
}
|
|
193
|
+
export function decodeSavedListViewMutationResult(value, allowedColumnIds, expected) {
|
|
194
|
+
try {
|
|
195
|
+
if (expected && (!expected.id.trim()
|
|
196
|
+
|| !['team', 'system'].includes(expected.source)
|
|
197
|
+
|| (expected.expectedVersion !== null
|
|
198
|
+
&& !isPositiveInteger(expected.expectedVersion, Number.MAX_SAFE_INTEGER))))
|
|
199
|
+
return;
|
|
200
|
+
const snapshot = snapshotPlainData(value);
|
|
201
|
+
if (!isRecord(snapshot) || typeof snapshot['ok'] !== 'boolean')
|
|
202
|
+
return;
|
|
203
|
+
const keys = snapshot['ok'] ? ['ok', 'version', 'view'] : ['ok', 'code', 'version', 'current'];
|
|
204
|
+
if (Object.keys(snapshot).some(key => !keys.includes(key)))
|
|
205
|
+
return;
|
|
206
|
+
const parseReceiptView = (candidate, version) => {
|
|
207
|
+
if (!isRecord(candidate)
|
|
208
|
+
|| Object.keys(candidate).some(key => !['id', 'name', 'state', 'version', 'source', 'readOnly', 'default', 'access'].includes(key))
|
|
209
|
+
|| candidate['version'] !== version
|
|
210
|
+
|| (candidate['readOnly'] !== undefined && typeof candidate['readOnly'] !== 'boolean')
|
|
211
|
+
|| (candidate['default'] !== undefined && typeof candidate['default'] !== 'boolean')
|
|
212
|
+
|| (candidate['source'] !== undefined && candidate['source'] !== 'team' && candidate['source'] !== 'system'))
|
|
213
|
+
return;
|
|
214
|
+
const access = candidate['access'] === undefined ? undefined : parseAccess(candidate['access']);
|
|
215
|
+
if (candidate['access'] !== undefined && !access)
|
|
216
|
+
return;
|
|
217
|
+
const state = candidate['state'];
|
|
218
|
+
if (!isRecord(state) || Object.keys(state).some(key => !['search', 'filters', 'sorters', 'pagination', 'columnVisibility', 'columnOrder'].includes(key)))
|
|
219
|
+
return;
|
|
220
|
+
if (!isRecord(state['pagination']) || Object.keys(state['pagination']).some(key => !['current', 'pageSize'].includes(key)))
|
|
221
|
+
return;
|
|
222
|
+
if (!isRecord(state['columnVisibility']) || Object.entries(state['columnVisibility'])
|
|
223
|
+
.some(([key, visible]) => !allowedColumnIds.has(key) || typeof visible !== 'boolean'))
|
|
224
|
+
return;
|
|
225
|
+
if (!Array.isArray(state['columnOrder']) || state['columnOrder'].some(key => typeof key !== 'string' || !allowedColumnIds.has(key))
|
|
226
|
+
|| new Set(state['columnOrder']).size !== state['columnOrder'].length)
|
|
227
|
+
return;
|
|
228
|
+
if (!Array.isArray(state['sorters']) || state['sorters'].some(sorter => !isRecord(sorter) || Object.keys(sorter).some(key => !['field', 'order'].includes(key))))
|
|
229
|
+
return;
|
|
230
|
+
const exactFilter = (filter, depth = 0) => {
|
|
231
|
+
if (!isRecord(filter) || depth > 20)
|
|
232
|
+
return false;
|
|
233
|
+
if ('field' in filter)
|
|
234
|
+
return Object.keys(filter).every(key => ['field', 'operator', 'value'].includes(key));
|
|
235
|
+
return Object.keys(filter).every(key => ['operator', 'value'].includes(key))
|
|
236
|
+
&& Array.isArray(filter['value']) && filter['value'].every(child => exactFilter(child, depth + 1));
|
|
237
|
+
};
|
|
238
|
+
if (!Array.isArray(state['filters']) || !state['filters'].every(filter => exactFilter(filter)))
|
|
239
|
+
return;
|
|
240
|
+
const views = readSavedListViews(JSON.stringify({ version: STORAGE_VERSION, views: [candidate] }), allowedColumnIds);
|
|
241
|
+
const view = views[0];
|
|
242
|
+
if (!view || view.name !== candidate['name'])
|
|
243
|
+
return;
|
|
244
|
+
if (expected && (view.id !== expected.id || candidate['source'] !== expected.source))
|
|
245
|
+
return;
|
|
246
|
+
return {
|
|
247
|
+
...view,
|
|
248
|
+
...(candidate['source'] === undefined ? {} : { source: candidate['source'] }),
|
|
249
|
+
...(candidate['readOnly'] === undefined ? {} : { readOnly: candidate['readOnly'] }),
|
|
250
|
+
...(candidate['default'] === undefined ? {} : { default: candidate['default'] }),
|
|
251
|
+
...(access === undefined ? {} : { access }),
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
if (snapshot['ok'] === true) {
|
|
255
|
+
if (!isPositiveInteger(snapshot['version'], Number.MAX_SAFE_INTEGER))
|
|
256
|
+
return;
|
|
257
|
+
if (expected?.expectedVersion !== undefined && expected.expectedVersion !== null
|
|
258
|
+
&& snapshot['version'] <= expected.expectedVersion)
|
|
259
|
+
return;
|
|
260
|
+
const view = parseReceiptView(snapshot['view'], snapshot['version']);
|
|
261
|
+
if (!view)
|
|
262
|
+
return;
|
|
263
|
+
return { ok: true, view, version: snapshot['version'] };
|
|
264
|
+
}
|
|
265
|
+
if (snapshot['code'] !== 'VERSION_CONFLICT' || !isPositiveInteger(snapshot['version'], Number.MAX_SAFE_INTEGER))
|
|
266
|
+
return;
|
|
267
|
+
const current = parseReceiptView(snapshot['current'], snapshot['version']);
|
|
268
|
+
if (!current)
|
|
269
|
+
return;
|
|
270
|
+
return { ok: false, code: 'VERSION_CONFLICT', current, version: snapshot['version'] };
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
export function decodeSavedListViewRemoveResult(value, expected) {
|
|
277
|
+
try {
|
|
278
|
+
const snapshot = snapshotPlainData(value);
|
|
279
|
+
if (!isRecord(snapshot) || snapshot['ok'] !== true
|
|
280
|
+
|| Object.keys(snapshot).some(key => !['ok', 'id', 'version'].includes(key))
|
|
281
|
+
|| snapshot['id'] !== expected.id
|
|
282
|
+
|| !isPositiveInteger(expected.expectedVersion, Number.MAX_SAFE_INTEGER)
|
|
283
|
+
|| !isPositiveInteger(snapshot['version'], Number.MAX_SAFE_INTEGER)
|
|
284
|
+
|| snapshot['version'] <= expected.expectedVersion)
|
|
285
|
+
return;
|
|
286
|
+
return { ok: true, id: expected.id, version: snapshot['version'] };
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
export function decodeRemoteSavedListViews(value, allowedColumnIds, writable = false) {
|
|
293
|
+
try {
|
|
294
|
+
const snapshot = snapshotPlainData(value);
|
|
295
|
+
if (!Array.isArray(snapshot))
|
|
296
|
+
return [];
|
|
297
|
+
return readSavedListViews(JSON.stringify({ version: STORAGE_VERSION, views: snapshot }), allowedColumnIds, 'team')
|
|
298
|
+
.map(view => {
|
|
299
|
+
const entry = snapshot.find(entry => isRecord(entry) && entry['id'] === view.id);
|
|
300
|
+
return {
|
|
301
|
+
...view,
|
|
302
|
+
readOnly: !writable || !view.version || !isRecord(entry) || entry['readOnly'] !== false,
|
|
303
|
+
...(isRecord(entry) && entry['default'] === true ? { default: true } : {}),
|
|
304
|
+
};
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
return [];
|
|
309
|
+
}
|
|
310
|
+
}
|
|
124
311
|
export function serializeSavedListViews(views) {
|
|
125
312
|
return JSON.stringify({ version: STORAGE_VERSION, views: views.slice(0, MAX_SAVED_VIEWS) });
|
|
126
313
|
}
|
|
@@ -137,10 +324,11 @@ export function listPreferenceScopeId(scope) {
|
|
|
137
324
|
`r:${encodeURIComponent(scope.resourceName)}`,
|
|
138
325
|
`p:${encodeURIComponent(scope.providerName)}`,
|
|
139
326
|
`t:${encodeScopeValue(scope.tenantIdentity)}`,
|
|
327
|
+
...(scope.identityKey === undefined ? [] : [`u:${encodeScopeValue(scope.identityKey)}`]),
|
|
140
328
|
].join('|');
|
|
141
329
|
}
|
|
142
330
|
export function canMigrateLegacyListPreferences(scope) {
|
|
143
|
-
return scope.providerName === 'default' && scope.tenantIdentity === undefined;
|
|
331
|
+
return scope.providerName === 'default' && scope.tenantIdentity === undefined && scope.identityKey === undefined;
|
|
144
332
|
}
|
|
145
333
|
export function savedListViewsStorageKey(scope) {
|
|
146
334
|
return `svadmin-list-views-v2-${listPreferenceScopeId(scope)}`;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { SvarRecordId } from './svar-grid-operations.js';
|
|
3
|
+
export interface SvarCellContent {
|
|
4
|
+
readonly id: SvarRecordId;
|
|
5
|
+
readonly field: string;
|
|
6
|
+
readonly value: unknown;
|
|
7
|
+
readonly record: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export interface SvarCellContext {
|
|
10
|
+
readonly render: () => Snippet<[SvarCellContent]> | undefined;
|
|
11
|
+
readonly resolve: (row: unknown, column: unknown) => SvarCellContent | undefined;
|
|
12
|
+
}
|
|
13
|
+
/** 仅可信宿主可提供原生 snippet;不把引擎 api 或 exec 传给内容组件。 */
|
|
14
|
+
export declare const SVAR_CELL_CONTEXT: unique symbol;
|