@svadmin/ui 0.88.1 → 0.92.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/dist/app.css +4 -0
- package/dist/app.theme.css +4 -0
- package/dist/component-registry.svelte.d.ts +2 -2
- package/dist/components/AboutSettings.svelte +23 -4
- package/dist/components/AdminApp.svelte +6 -4
- package/dist/components/AdminApp.svelte.d.ts +3 -3
- package/dist/components/ApiSettings.svelte +43 -27
- package/dist/components/ApprovalActionCard.svelte +8 -9
- package/dist/components/ApprovalCenter.svelte +24 -5
- package/dist/components/AuditLogViewer.svelte +2 -2
- package/dist/components/AutoComplete.svelte +610 -0
- package/dist/components/AutoComplete.svelte.d.ts +25 -0
- package/dist/components/AutoForm.svelte +265 -18
- package/dist/components/AutoForm.svelte.d.ts +19 -2
- package/dist/components/AutoTable.svelte +141 -36
- package/dist/components/AutoTable.svelte.d.ts +8 -0
- package/dist/components/BatchActionBar.svelte +42 -14
- package/dist/components/BatchActionBar.svelte.d.ts +5 -0
- package/dist/components/BoundRecordDetailDrawer.svelte +5 -3
- package/dist/components/CancelTaskButton.svelte +14 -1
- package/dist/components/Cascader.svelte +412 -53
- package/dist/components/Cascader.svelte.d.ts +7 -1
- package/dist/components/ColorPicker.svelte +1 -1
- package/dist/components/ColorPicker.svelte.d.ts +1 -1
- package/dist/components/ComboboxField.svelte +270 -34
- package/dist/components/ComboboxField.svelte.d.ts +4 -0
- package/dist/components/CommandPalette.svelte +4 -4
- package/dist/components/DashboardView.svelte +73 -7
- package/dist/components/DashboardView.svelte.d.ts +3 -1
- package/dist/components/DateRangeInput.svelte +8 -1
- package/dist/components/DetailWorkspaceAsyncSection.svelte +107 -0
- package/dist/components/DetailWorkspaceAsyncSection.svelte.d.ts +36 -0
- package/dist/components/DetailWorkspaceSections.svelte +233 -0
- package/dist/components/DetailWorkspaceSections.svelte.d.ts +9 -0
- package/dist/components/DrawerForm.svelte +78 -9
- package/dist/components/DrawerForm.svelte.d.ts +3 -3
- package/dist/components/ErrorBoundary.svelte +1 -1
- package/dist/components/ErrorComponent.svelte +19 -6
- package/dist/components/ErrorComponent.svelte.d.ts +1 -0
- package/dist/components/ErrorSummary.svelte +9 -4
- package/dist/components/FieldRenderer.svelte +203 -90
- package/dist/components/FieldRenderer.svelte.d.ts +16 -1
- package/dist/components/FileUpload.svelte +116 -51
- package/dist/components/FileUpload.svelte.d.ts +7 -27
- package/dist/components/FileUploadField.svelte +170 -0
- package/dist/components/FileUploadField.svelte.d.ts +21 -0
- package/dist/components/FilterBuilder.svelte +13 -7
- package/dist/components/FilterCollectionInput.svelte +9 -7
- package/dist/components/FilterCollectionInput.svelte.d.ts +3 -0
- package/dist/components/ForgotPasswordPage.svelte +1 -1
- package/dist/components/Header.svelte +1 -1
- package/dist/components/ImportWizard.svelte +197 -28
- package/dist/components/ImportWizard.svelte.d.ts +5 -1
- package/dist/components/InferencerPanel.svelte +4 -2
- package/dist/components/InfiniteList.svelte +52 -9
- package/dist/components/JsonSchemaForm.svelte +237 -42
- package/dist/components/JsonSchemaForm.svelte.d.ts +10 -3
- package/dist/components/KanbanBoard.svelte +43 -7
- package/dist/components/KanbanBoard.svelte.d.ts +2 -2
- package/dist/components/Layout.svelte +1 -1
- package/dist/components/LoginPage.svelte +1 -1
- package/dist/components/MetricStrip.svelte +8 -3
- package/dist/components/MetricStrip.svelte.d.ts +1 -0
- package/dist/components/ModalForm.svelte +85 -9
- package/dist/components/ModalForm.svelte.d.ts +3 -3
- package/dist/components/MoneyInput.svelte +5 -3
- package/dist/components/MoneyInput.svelte.d.ts +4 -2
- package/dist/components/MultiTabKeepAlive.svelte +502 -43
- package/dist/components/MultiTabKeepAlive.svelte.d.ts +40 -6
- package/dist/components/NotificationsSettings.svelte +10 -10
- package/dist/components/NumberInput.svelte +5 -3
- package/dist/components/NumberInput.svelte.d.ts +4 -2
- package/dist/components/NumericInput.svelte +277 -25
- package/dist/components/NumericInput.svelte.d.ts +3 -1
- package/dist/components/PercentInput.svelte +5 -3
- package/dist/components/PercentInput.svelte.d.ts +4 -2
- package/dist/components/PermissionMatrix.svelte +44 -8
- package/dist/components/PermissionMatrix.svelte.d.ts +1 -0
- package/dist/components/ProfilePage.svelte +38 -13
- package/dist/components/RadioGroup.svelte +203 -4
- package/dist/components/RadioGroup.svelte.d.ts +1 -1
- package/dist/components/Rate.svelte +3 -3
- package/dist/components/Rate.svelte.d.ts +2 -2
- package/dist/components/RecordDetailDrawer.svelte +1 -1
- package/dist/components/RecordDetailDrawer.svelte.d.ts +1 -1
- package/dist/components/RegisterPage.svelte +1 -1
- package/dist/components/RelationPicker.svelte +39 -11
- package/dist/components/ResourceAccessGuard.svelte +7 -2
- package/dist/components/RetryTaskButton.svelte +14 -1
- package/dist/components/RolesSettings.svelte +74 -28
- package/dist/components/SecuritySettings.svelte +66 -51
- package/dist/components/SegmentedControl.svelte +4 -2
- package/dist/components/SegmentedControl.svelte.d.ts +1 -1
- package/dist/components/Sidebar.svelte +1 -1
- package/dist/components/StepsForm.svelte +62 -10
- package/dist/components/StepsForm.svelte.d.ts +4 -2
- package/dist/components/SvadminLogo.svelte +1 -0
- package/dist/components/SvarDataGrid.svelte +42 -24
- package/dist/components/SvarDataGrid.svelte.d.ts +10 -10
- package/dist/components/SvarResourceRead.svelte +2 -0
- package/dist/components/SvarResourceTableView.svelte +4 -2
- package/dist/components/TagsInput.svelte +3 -3
- package/dist/components/TagsInput.svelte.d.ts +2 -2
- package/dist/components/TaskDetails.svelte +22 -16
- package/dist/components/TaskDetails.svelte.d.ts +1 -1
- package/dist/components/TaskList.svelte +57 -15
- package/dist/components/TaskQueueDrawer.svelte +54 -34
- package/dist/components/Toast.svelte +13 -2
- package/dist/components/Transfer.svelte +67 -5
- package/dist/components/Transfer.svelte.d.ts +4 -0
- package/dist/components/TreeSelect.svelte +337 -77
- package/dist/components/TreeSelect.svelte.d.ts +4 -0
- package/dist/components/TreeTable.svelte +6 -1
- package/dist/components/TwoFactorAuthPage.svelte +8 -4
- package/dist/components/UpdatePasswordPage.svelte +1 -1
- package/dist/components/VersionDiffViewer.svelte +9 -7
- package/dist/components/account/CompanyProfilePage.svelte +13 -12
- package/dist/components/account/MemberDirectory.svelte +14 -10
- package/dist/components/account/SettingsEnterprisePage.svelte +18 -18
- package/dist/components/content/AuditTimeline.svelte +8 -6
- package/dist/components/content/DataState.svelte +1 -1
- package/dist/components/content/FileList.svelte +44 -13
- package/dist/components/content/FileList.svelte.d.ts +4 -2
- package/dist/components/content/FileListRow.svelte +87 -0
- package/dist/components/content/FileListRow.svelte.d.ts +11 -0
- package/dist/components/content/ImportDropzone.svelte +109 -12
- package/dist/components/content/ImportDropzone.svelte.d.ts +1 -0
- package/dist/components/content/IntegrationCard.svelte +3 -3
- package/dist/components/content/IntegrationCard.svelte.d.ts +2 -0
- package/dist/components/content/MediaThumbnail.svelte +6 -2
- package/dist/components/detail-workspace.d.ts +60 -0
- package/dist/components/detail-workspace.js +3 -0
- package/dist/components/enterprise/json-schema-form.d.ts +1 -1
- package/dist/components/enterprise/recursive-schema-form.d.ts +15 -1
- package/dist/components/enterprise/recursive-schema-form.js +288 -14
- package/dist/components/enterprise/schema-form-references.d.ts +14 -0
- package/dist/components/enterprise/schema-form-references.js +316 -0
- package/dist/components/fields/ArrayField.svelte +178 -14
- package/dist/components/fields/ArrayField.svelte.d.ts +17 -3
- package/dist/components/fields/array-field-metadata.d.ts +4 -0
- package/dist/components/fields/array-field-metadata.js +36 -0
- package/dist/components/file-upload-reference.d.ts +11 -0
- package/dist/components/file-upload-reference.js +50 -0
- package/dist/components/file-upload-types.d.ts +32 -0
- package/dist/components/file-upload-types.js +1 -0
- package/dist/components/form-contract.upload-type-fixture.svelte +108 -0
- package/dist/components/form-contract.upload-type-fixture.svelte.d.ts +18 -0
- package/dist/components/json-schema-form-state.d.ts +19 -0
- package/dist/components/list-deletion.svelte.js +6 -1
- package/dist/components/list-state.svelte.d.ts +12 -1
- package/dist/components/list-state.svelte.js +56 -16
- package/dist/components/numeric-format.d.ts +44 -0
- package/dist/components/numeric-format.js +198 -0
- package/dist/components/record-detail.svelte.js +2 -0
- package/dist/components/saved-list-views.d.ts +9 -2
- package/dist/components/saved-list-views.js +42 -11
- package/dist/components/saved-list-views.svelte.d.ts +3 -0
- package/dist/components/saved-list-views.svelte.js +56 -7
- package/dist/components/task-action.svelte.d.ts +1 -0
- package/dist/components/task-action.svelte.js +55 -3
- package/dist/components/task-utils.d.ts +1 -0
- package/dist/components/task-utils.js +18 -0
- package/dist/components/ui/input/input.svelte +1 -1
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/sheet/sheet.svelte +4 -1
- package/dist/components/version-history.d.ts +14 -0
- package/dist/components/version-history.js +32 -0
- package/dist/components/workspace-tab-state.d.ts +78 -0
- package/dist/components/workspace-tab-state.js +587 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +4 -0
- package/package.json +5 -5
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
let loading = $state(false);
|
|
34
34
|
let saving = $state(false);
|
|
35
35
|
let loaded = $state(false);
|
|
36
|
-
let loadError = $state(
|
|
37
|
-
let saveError = $state(
|
|
36
|
+
let loadError = $state(false);
|
|
37
|
+
let saveError = $state(false);
|
|
38
38
|
let epoch = 0;
|
|
39
39
|
async function load() {
|
|
40
40
|
const provider = preferencesProvider;
|
|
41
41
|
const request = ++epoch;
|
|
42
|
-
loadError =
|
|
42
|
+
loadError = false; saveError = false; loaded = false; saving = false;
|
|
43
43
|
loading = Boolean(provider);
|
|
44
44
|
if (!provider) return;
|
|
45
45
|
try {
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
if (request !== epoch) return;
|
|
48
48
|
emailAlerts = { ...result.email }; pushAlerts = { ...result.push }; smsAlerts = { ...result.sms };
|
|
49
49
|
loaded = true;
|
|
50
|
-
} catch
|
|
51
|
-
if (request === epoch) loadError =
|
|
50
|
+
} catch {
|
|
51
|
+
if (request === epoch) loadError = true;
|
|
52
52
|
} finally {
|
|
53
53
|
if (request === epoch) loading = false;
|
|
54
54
|
}
|
|
@@ -62,12 +62,12 @@
|
|
|
62
62
|
const provider = preferencesProvider;
|
|
63
63
|
if (!provider || !loaded || saving) return;
|
|
64
64
|
const request = epoch;
|
|
65
|
-
saving = true; saveError =
|
|
65
|
+
saving = true; saveError = false;
|
|
66
66
|
try {
|
|
67
67
|
await provider.save({ email: { ...emailAlerts }, push: { ...pushAlerts }, sms: { ...smsAlerts } });
|
|
68
68
|
if (request === epoch) notification.success(i18n.t('common.updateSuccess'), 3000);
|
|
69
|
-
} catch
|
|
70
|
-
if (request === epoch) saveError =
|
|
69
|
+
} catch {
|
|
70
|
+
if (request === epoch) saveError = true;
|
|
71
71
|
} finally {
|
|
72
72
|
if (request === epoch) saving = false;
|
|
73
73
|
}
|
|
@@ -89,9 +89,9 @@
|
|
|
89
89
|
{#if loading}
|
|
90
90
|
<DataState state="loading" />
|
|
91
91
|
{:else if loadError}
|
|
92
|
-
<DataState state="error" description={
|
|
92
|
+
<DataState state="error" description={i18n.t('common.operationFailed')} retry={load} />
|
|
93
93
|
{:else if saveError}
|
|
94
|
-
<FeedbackNotice tone="danger" message={
|
|
94
|
+
<FeedbackNotice tone="danger" message={i18n.t('common.operationFailed')} />
|
|
95
95
|
{/if}
|
|
96
96
|
{#if loaded || !preferencesProvider}
|
|
97
97
|
<fieldset disabled={!preferencesProvider || saving} class="svadmin-u-b3542e058833">
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import NumericInput from './NumericInput.svelte';
|
|
3
|
+
import type { NumericInputLocale } from './numeric-format.js';
|
|
3
4
|
import type { NumericInputMode } from './NumericInput.svelte';
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
value?: number | null;
|
|
8
|
+
locale?: NumericInputLocale | undefined;
|
|
7
9
|
min?: number;
|
|
8
10
|
max?: number;
|
|
9
11
|
step?: number;
|
|
@@ -14,12 +16,12 @@
|
|
|
14
16
|
disabled?: boolean;
|
|
15
17
|
required?: boolean;
|
|
16
18
|
invalid?: boolean;
|
|
17
|
-
describedby?: string;
|
|
19
|
+
describedby?: string | undefined;
|
|
18
20
|
class?: string;
|
|
19
21
|
onchange?: (value: number | null) => void;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
let props: Props = $props();
|
|
24
|
+
let { value = $bindable(null), ...props }: Props = $props();
|
|
23
25
|
</script>
|
|
24
26
|
|
|
25
|
-
<NumericInput {...props} mode={'number' satisfies NumericInputMode} />
|
|
27
|
+
<NumericInput {...props} bind:value mode={'number' satisfies NumericInputMode} />
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { NumericInputLocale } from './numeric-format.js';
|
|
1
2
|
interface Props {
|
|
2
3
|
value?: number | null;
|
|
4
|
+
locale?: NumericInputLocale | undefined;
|
|
3
5
|
min?: number;
|
|
4
6
|
max?: number;
|
|
5
7
|
step?: number;
|
|
@@ -10,10 +12,10 @@ interface Props {
|
|
|
10
12
|
disabled?: boolean;
|
|
11
13
|
required?: boolean;
|
|
12
14
|
invalid?: boolean;
|
|
13
|
-
describedby?: string;
|
|
15
|
+
describedby?: string | undefined;
|
|
14
16
|
class?: string;
|
|
15
17
|
onchange?: (value: number | null) => void;
|
|
16
18
|
}
|
|
17
|
-
declare const NumberInput: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
declare const NumberInput: import("svelte").Component<Props, {}, "value">;
|
|
18
20
|
type NumberInput = ReturnType<typeof NumberInput>;
|
|
19
21
|
export default NumberInput;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Input } from './ui/input/index.js';
|
|
3
3
|
import { cn } from '../utils.js';
|
|
4
|
+
import { useTranslation } from '@svadmin/core/i18n';
|
|
5
|
+
import {
|
|
6
|
+
addNumericStep,
|
|
7
|
+
formatNumericEditingText,
|
|
8
|
+
formatNumericValue,
|
|
9
|
+
isNumericInputLocale,
|
|
10
|
+
parseNumericInput,
|
|
11
|
+
type NumericInputLocale,
|
|
12
|
+
} from './numeric-format.js';
|
|
4
13
|
|
|
5
14
|
export type NumericInputMode = 'number' | 'currency' | 'percent';
|
|
6
15
|
|
|
@@ -9,6 +18,7 @@
|
|
|
9
18
|
mode?: NumericInputMode;
|
|
10
19
|
currency?: string;
|
|
11
20
|
scale?: '100' | '1';
|
|
21
|
+
locale?: NumericInputLocale | undefined;
|
|
12
22
|
min?: number;
|
|
13
23
|
max?: number;
|
|
14
24
|
step?: number;
|
|
@@ -19,7 +29,7 @@
|
|
|
19
29
|
disabled?: boolean;
|
|
20
30
|
required?: boolean;
|
|
21
31
|
invalid?: boolean;
|
|
22
|
-
describedby?: string;
|
|
32
|
+
describedby?: string | undefined;
|
|
23
33
|
class?: string;
|
|
24
34
|
onchange?: (value: number | null) => void;
|
|
25
35
|
}
|
|
@@ -29,6 +39,7 @@
|
|
|
29
39
|
mode = 'number',
|
|
30
40
|
currency,
|
|
31
41
|
scale = '100',
|
|
42
|
+
locale,
|
|
32
43
|
min,
|
|
33
44
|
max,
|
|
34
45
|
step,
|
|
@@ -50,27 +61,138 @@
|
|
|
50
61
|
: Math.min(20, Math.max(0, Math.trunc(precision))),
|
|
51
62
|
);
|
|
52
63
|
|
|
64
|
+
const i18n = useTranslation();
|
|
65
|
+
const localeValue = $derived(isNumericInputLocale(locale) ? locale : undefined);
|
|
66
|
+
const localeRequested = $derived(locale !== undefined);
|
|
67
|
+
const localeUnsupported = $derived(localeRequested && localeValue === undefined);
|
|
68
|
+
|
|
69
|
+
let localeFocused = $state(false);
|
|
70
|
+
let localeDraft = $state<string | null>(null);
|
|
71
|
+
let localeInvalid = $state(false);
|
|
72
|
+
let observedValue = $state<number | null | undefined>(undefined);
|
|
73
|
+
let observedLocale = $state<string | NumericInputLocale | undefined>(undefined);
|
|
74
|
+
let localeInputElement = $state<HTMLInputElement | null>(null);
|
|
75
|
+
let localeComposing = $state(false);
|
|
76
|
+
let composedText: string | null = null;
|
|
77
|
+
|
|
78
|
+
const localeBoundaryInvalid = $derived.by(() => {
|
|
79
|
+
if (!localeRequested || value == null) return false;
|
|
80
|
+
if (!Number.isFinite(value) || !Number.isFinite(displayNumber)) return true;
|
|
81
|
+
const lower = min != null && Number.isFinite(min) ? min : undefined;
|
|
82
|
+
const upper = max != null && Number.isFinite(max) ? max : undefined;
|
|
83
|
+
return (lower !== undefined && value < lower) || (upper !== undefined && value > upper);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const localeErrorMessage = $derived(
|
|
87
|
+
localeUnsupported
|
|
88
|
+
? i18n.t('validation.invalidFormat')
|
|
89
|
+
: localeInvalid || localeBoundaryInvalid || localeComposing
|
|
90
|
+
? i18n.t('validation.invalidNumber')
|
|
91
|
+
: '',
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const displayNumber = $derived.by(() => {
|
|
95
|
+
if (value == null || !Number.isFinite(value)) return null;
|
|
96
|
+
const scaled = mode === 'percent' && scale === '1' ? value * 100 : value;
|
|
97
|
+
return Number.isFinite(scaled) ? scaled : null;
|
|
98
|
+
});
|
|
99
|
+
|
|
53
100
|
const displayValue = $derived.by(() => {
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
101
|
+
if (displayNumber == null) return '';
|
|
102
|
+
return normalizedPrecision === undefined
|
|
103
|
+
? String(displayNumber)
|
|
104
|
+
: displayNumber.toFixed(normalizedPrecision);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const localizedDisplay = $derived(
|
|
108
|
+
localeValue ? formatNumericValue(localeValue, displayNumber, normalizedPrecision) : displayValue,
|
|
109
|
+
);
|
|
110
|
+
const localizedEditing = $derived(
|
|
111
|
+
localeValue
|
|
112
|
+
? formatNumericEditingText(localeValue, displayNumber, normalizedPrecision)
|
|
113
|
+
: displayValue,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const inputValue = $derived(
|
|
117
|
+
localeRequested
|
|
118
|
+
? localeDraft ?? (localeFocused && localeValue ? localizedEditing : localeValue ? localizedDisplay : displayValue)
|
|
119
|
+
: displayValue,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const submittedValue = $derived(
|
|
123
|
+
value == null || !Number.isFinite(value) ? '' : String(value),
|
|
124
|
+
);
|
|
125
|
+
const hiddenDisabled = $derived(
|
|
126
|
+
disabled || localeInvalid || localeUnsupported || localeBoundaryInvalid || localeComposing,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
$effect(() => {
|
|
130
|
+
if (!localeRequested) return;
|
|
131
|
+
const current = value;
|
|
132
|
+
const finite = current == null || Number.isFinite(current);
|
|
133
|
+
const display = displayNumber;
|
|
134
|
+
if (!finite || display == null) {
|
|
135
|
+
if (!Object.is(current, observedValue)) {
|
|
136
|
+
observedValue = current;
|
|
137
|
+
localeInvalid = false;
|
|
138
|
+
}
|
|
139
|
+
localeDraft = null;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (Object.is(current, observedValue)) return;
|
|
143
|
+
observedValue = current;
|
|
144
|
+
localeInvalid = false;
|
|
145
|
+
localeDraft = localeFocused && localeValue
|
|
146
|
+
? formatNumericEditingText(localeValue, display, normalizedPrecision)
|
|
147
|
+
: null;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
$effect(() => {
|
|
151
|
+
const nextLocale = locale;
|
|
152
|
+
if (Object.is(nextLocale, observedLocale)) return;
|
|
153
|
+
observedLocale = nextLocale;
|
|
154
|
+
localeFocused = false;
|
|
155
|
+
localeDraft = null;
|
|
156
|
+
localeInvalid = false;
|
|
157
|
+
localeComposing = false;
|
|
158
|
+
composedText = null;
|
|
159
|
+
observedValue = value;
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
$effect(() => {
|
|
163
|
+
localeInputElement?.setCustomValidity(localeErrorMessage);
|
|
57
164
|
});
|
|
58
165
|
|
|
59
166
|
const displayMin = $derived(mode === 'percent' && scale === '1' && min != null ? min * 100 : min);
|
|
60
167
|
const displayMax = $derived(mode === 'percent' && scale === '1' && max != null ? max * 100 : max);
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
168
|
+
const explicitDisplayStep = $derived.by(() => {
|
|
169
|
+
if (step == null || !Number.isFinite(step) || step <= 0) return undefined;
|
|
170
|
+
const scaled = mode === 'percent' && scale === '1' ? step * 100 : step;
|
|
171
|
+
return Number.isFinite(scaled) && scaled > 0 ? scaled : undefined;
|
|
172
|
+
});
|
|
173
|
+
const displayStep = $derived(
|
|
174
|
+
explicitDisplayStep
|
|
175
|
+
?? (mode === 'percent' && scale === '1'
|
|
176
|
+
? 1
|
|
177
|
+
: normalizedPrecision === undefined
|
|
178
|
+
? undefined
|
|
179
|
+
: 10 ** -normalizedPrecision),
|
|
65
180
|
);
|
|
181
|
+
const resolvedStep = $derived(displayStep ?? 'any');
|
|
66
182
|
|
|
67
183
|
function clamp(next: number): number {
|
|
68
184
|
return Math.min(max ?? next, Math.max(min ?? next, next));
|
|
69
185
|
}
|
|
70
186
|
|
|
71
|
-
function
|
|
187
|
+
function commit(next: number | null): void {
|
|
188
|
+
observedValue = next;
|
|
189
|
+
value = next;
|
|
190
|
+
onchange?.(next);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function handleLegacyInput(event: Event): void {
|
|
72
194
|
const input = event.currentTarget;
|
|
73
|
-
if (!(input instanceof HTMLInputElement)) return;
|
|
195
|
+
if (!(input instanceof HTMLInputElement) || disabled) return;
|
|
74
196
|
if (input.value === '') {
|
|
75
197
|
value = null;
|
|
76
198
|
onchange?.(null);
|
|
@@ -83,27 +205,157 @@
|
|
|
83
205
|
value = next;
|
|
84
206
|
onchange?.(next);
|
|
85
207
|
}
|
|
208
|
+
|
|
209
|
+
function applyLocaleText(text: string): void {
|
|
210
|
+
if (!localeValue) return;
|
|
211
|
+
localeDraft = text;
|
|
212
|
+
const parsed = parseNumericInput(localeValue, text);
|
|
213
|
+
if (!parsed.ok) {
|
|
214
|
+
localeInvalid = true;
|
|
215
|
+
localeInputElement?.setCustomValidity(i18n.t('validation.invalidNumber'));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
localeInvalid = false;
|
|
219
|
+
localeInputElement?.setCustomValidity('');
|
|
220
|
+
if (parsed.value === null) {
|
|
221
|
+
commit(null);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const stored = mode === 'percent' && scale === '1' ? parsed.value / 100 : parsed.value;
|
|
225
|
+
commit(clamp(stored));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function handleLocaleInput(event: Event): void {
|
|
229
|
+
const input = event.currentTarget;
|
|
230
|
+
if (!(input instanceof HTMLInputElement) || disabled) return;
|
|
231
|
+
const text = input.value;
|
|
232
|
+
if (localeUnsupported) {
|
|
233
|
+
localeDraft = text;
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (!localeValue) return;
|
|
237
|
+
localeDraft = text;
|
|
238
|
+
if (localeComposing || (typeof InputEvent !== 'undefined' && event instanceof InputEvent && event.isComposing)) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (composedText !== null && text === composedText) {
|
|
242
|
+
composedText = null;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
composedText = null;
|
|
246
|
+
applyLocaleText(text);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function handleCompositionStart(): void {
|
|
250
|
+
if (disabled || localeUnsupported || !localeValue) return;
|
|
251
|
+
localeComposing = true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function handleCompositionEnd(event: Event): void {
|
|
255
|
+
const input = event.currentTarget;
|
|
256
|
+
if (!(input instanceof HTMLInputElement) || disabled || localeUnsupported || !localeValue) return;
|
|
257
|
+
localeComposing = false;
|
|
258
|
+
composedText = input.value;
|
|
259
|
+
applyLocaleText(input.value);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function handleLocaleFocus(): void {
|
|
263
|
+
if (disabled || localeUnsupported || !localeValue) return;
|
|
264
|
+
localeFocused = true;
|
|
265
|
+
observedValue = value;
|
|
266
|
+
if (!localeInvalid) localeDraft = localizedEditing;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function handleLocaleBlur(): void {
|
|
270
|
+
localeFocused = false;
|
|
271
|
+
if (!localeInvalid) localeDraft = null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function handleLocaleKeyDown(event: KeyboardEvent): void {
|
|
275
|
+
if (disabled || localeUnsupported || !localeValue) return;
|
|
276
|
+
if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return;
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
if (localeComposing || event.isComposing) return;
|
|
279
|
+
const increment = displayStep !== undefined && Number.isFinite(displayStep) && displayStep > 0
|
|
280
|
+
? displayStep
|
|
281
|
+
: 1;
|
|
282
|
+
const base = value ?? min ?? 0;
|
|
283
|
+
const displayedBase = mode === 'percent' && scale === '1' ? base * 100 : base;
|
|
284
|
+
const nextDisplayed = addNumericStep(
|
|
285
|
+
displayedBase,
|
|
286
|
+
increment,
|
|
287
|
+
event.key === 'ArrowUp' ? 1 : -1,
|
|
288
|
+
);
|
|
289
|
+
const stored = mode === 'percent' && scale === '1' ? nextDisplayed / 100 : nextDisplayed;
|
|
290
|
+
const next = clamp(stored);
|
|
291
|
+
localeInvalid = false;
|
|
292
|
+
localeInputElement?.setCustomValidity('');
|
|
293
|
+
localeDraft = formatNumericEditingText(
|
|
294
|
+
localeValue,
|
|
295
|
+
mode === 'percent' && scale === '1' ? next * 100 : next,
|
|
296
|
+
normalizedPrecision,
|
|
297
|
+
);
|
|
298
|
+
commit(next);
|
|
299
|
+
}
|
|
86
300
|
</script>
|
|
87
301
|
|
|
88
302
|
<div class={cn('svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-8ef2268efbbc', className)} data-numeric-input={mode}>
|
|
89
303
|
{#if mode === 'currency' && currency}
|
|
90
304
|
<span class="svadmin-u-bfa603190748 svadmin-u-012fbd121f37" aria-hidden="true">{currency}</span>
|
|
91
305
|
{/if}
|
|
92
|
-
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
{
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
306
|
+
{#if localeRequested}
|
|
307
|
+
{#if name}
|
|
308
|
+
<input
|
|
309
|
+
type="hidden"
|
|
310
|
+
{name}
|
|
311
|
+
value={submittedValue}
|
|
312
|
+
disabled={hiddenDisabled}
|
|
313
|
+
data-numeric-submit-value
|
|
314
|
+
/>
|
|
315
|
+
{/if}
|
|
316
|
+
<Input
|
|
317
|
+
bind:ref={localeInputElement}
|
|
318
|
+
{id}
|
|
319
|
+
type="text"
|
|
320
|
+
inputmode="decimal"
|
|
321
|
+
role="spinbutton"
|
|
322
|
+
lang={localeValue}
|
|
323
|
+
value={inputValue}
|
|
324
|
+
min={displayMin}
|
|
325
|
+
max={displayMax}
|
|
326
|
+
step={resolvedStep}
|
|
327
|
+
aria-valuemin={displayMin}
|
|
328
|
+
aria-valuemax={displayMax}
|
|
329
|
+
aria-valuenow={displayNumber ?? undefined}
|
|
330
|
+
{placeholder}
|
|
331
|
+
{disabled}
|
|
332
|
+
{required}
|
|
333
|
+
aria-invalid={invalid || localeInvalid || localeBoundaryInvalid || localeUnsupported || undefined}
|
|
334
|
+
aria-describedby={describedby}
|
|
335
|
+
onfocus={handleLocaleFocus}
|
|
336
|
+
onblur={handleLocaleBlur}
|
|
337
|
+
onkeydown={handleLocaleKeyDown}
|
|
338
|
+
oninput={handleLocaleInput}
|
|
339
|
+
oncompositionstart={handleCompositionStart}
|
|
340
|
+
oncompositionend={handleCompositionEnd}
|
|
341
|
+
/>
|
|
342
|
+
{:else}
|
|
343
|
+
<Input
|
|
344
|
+
{id}
|
|
345
|
+
{name}
|
|
346
|
+
type="number"
|
|
347
|
+
value={displayValue}
|
|
348
|
+
min={displayMin}
|
|
349
|
+
max={displayMax}
|
|
350
|
+
step={resolvedStep}
|
|
351
|
+
{placeholder}
|
|
352
|
+
{disabled}
|
|
353
|
+
{required}
|
|
354
|
+
aria-invalid={invalid || undefined}
|
|
355
|
+
aria-describedby={describedby}
|
|
356
|
+
oninput={handleLegacyInput}
|
|
357
|
+
/>
|
|
358
|
+
{/if}
|
|
107
359
|
{#if mode === 'percent'}
|
|
108
360
|
<span class="svadmin-u-bfa603190748 svadmin-u-012fbd121f37" aria-hidden="true">%</span>
|
|
109
361
|
{/if}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { type NumericInputLocale } from './numeric-format.js';
|
|
1
2
|
export type NumericInputMode = 'number' | 'currency' | 'percent';
|
|
2
3
|
interface Props {
|
|
3
4
|
value?: number | null;
|
|
4
5
|
mode?: NumericInputMode;
|
|
5
6
|
currency?: string;
|
|
6
7
|
scale?: '100' | '1';
|
|
8
|
+
locale?: NumericInputLocale | undefined;
|
|
7
9
|
min?: number;
|
|
8
10
|
max?: number;
|
|
9
11
|
step?: number;
|
|
@@ -14,7 +16,7 @@ interface Props {
|
|
|
14
16
|
disabled?: boolean;
|
|
15
17
|
required?: boolean;
|
|
16
18
|
invalid?: boolean;
|
|
17
|
-
describedby?: string;
|
|
19
|
+
describedby?: string | undefined;
|
|
18
20
|
class?: string;
|
|
19
21
|
onchange?: (value: number | null) => void;
|
|
20
22
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import NumericInput from './NumericInput.svelte';
|
|
3
|
+
import type { NumericInputLocale } from './numeric-format.js';
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
value?: number | null;
|
|
7
|
+
locale?: NumericInputLocale | undefined;
|
|
6
8
|
scale?: '100' | '1';
|
|
7
9
|
min?: number;
|
|
8
10
|
max?: number;
|
|
@@ -14,12 +16,12 @@
|
|
|
14
16
|
disabled?: boolean;
|
|
15
17
|
required?: boolean;
|
|
16
18
|
invalid?: boolean;
|
|
17
|
-
describedby?: string;
|
|
19
|
+
describedby?: string | undefined;
|
|
18
20
|
class?: string;
|
|
19
21
|
onchange?: (value: number | null) => void;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
let props: Props = $props();
|
|
24
|
+
let { value = $bindable(null), ...props }: Props = $props();
|
|
23
25
|
</script>
|
|
24
26
|
|
|
25
|
-
<NumericInput {...props} mode="percent" />
|
|
27
|
+
<NumericInput {...props} bind:value mode="percent" />
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { NumericInputLocale } from './numeric-format.js';
|
|
1
2
|
interface Props {
|
|
2
3
|
value?: number | null;
|
|
4
|
+
locale?: NumericInputLocale | undefined;
|
|
3
5
|
scale?: '100' | '1';
|
|
4
6
|
min?: number;
|
|
5
7
|
max?: number;
|
|
@@ -11,10 +13,10 @@ interface Props {
|
|
|
11
13
|
disabled?: boolean;
|
|
12
14
|
required?: boolean;
|
|
13
15
|
invalid?: boolean;
|
|
14
|
-
describedby?: string;
|
|
16
|
+
describedby?: string | undefined;
|
|
15
17
|
class?: string;
|
|
16
18
|
onchange?: (value: number | null) => void;
|
|
17
19
|
}
|
|
18
|
-
declare const PercentInput: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
declare const PercentInput: import("svelte").Component<Props, {}, "value">;
|
|
19
21
|
type PercentInput = ReturnType<typeof PercentInput>;
|
|
20
22
|
export default PercentInput;
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
return value === key && labels ? labels[translation.locale === 'zh-CN' ? 1 : 0] : value;
|
|
25
25
|
},
|
|
26
26
|
};
|
|
27
|
+
const instanceId = $props.id();
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
let {
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
// Custom slots for injecting multi-tenant switchers or security filters
|
|
49
50
|
sidebarExtra,
|
|
50
51
|
headerExtra,
|
|
52
|
+
tableContent,
|
|
51
53
|
|
|
52
54
|
// Callback event
|
|
53
55
|
onToggle
|
|
@@ -63,6 +65,7 @@
|
|
|
63
65
|
conflictCount?: number;
|
|
64
66
|
sidebarExtra?: Snippet;
|
|
65
67
|
headerExtra?: Snippet;
|
|
68
|
+
tableContent?: Snippet | undefined;
|
|
66
69
|
onToggle: (role: string, resource: string, action: string, grant: boolean) => void;
|
|
67
70
|
} = $props();
|
|
68
71
|
|
|
@@ -72,6 +75,20 @@
|
|
|
72
75
|
selectedRole = firstRole.code;
|
|
73
76
|
}
|
|
74
77
|
});
|
|
78
|
+
|
|
79
|
+
type PermissionDecision = 'granted' | 'denied' | 'unknown';
|
|
80
|
+
|
|
81
|
+
function resolveDecision(role: string, resource: string, action: string): PermissionDecision {
|
|
82
|
+
if (!role) return 'unknown';
|
|
83
|
+
try {
|
|
84
|
+
const value: unknown = isGranted(role, resource, action);
|
|
85
|
+
if (value === true) return 'granted';
|
|
86
|
+
if (value === false) return 'denied';
|
|
87
|
+
return 'unknown';
|
|
88
|
+
} catch {
|
|
89
|
+
return 'unknown';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
75
92
|
</script>
|
|
76
93
|
|
|
77
94
|
<div class="svadmin-u-668b21aa5409 svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-f46b61a9b310">
|
|
@@ -87,7 +104,11 @@
|
|
|
87
104
|
|
|
88
105
|
<!-- Toast Feedback -->
|
|
89
106
|
{#if message}
|
|
90
|
-
<div
|
|
107
|
+
<div
|
|
108
|
+
role={messageType === 'error' ? 'alert' : 'status'}
|
|
109
|
+
aria-live={messageType === 'error' ? 'assertive' : 'polite'}
|
|
110
|
+
class={`svadmin-u-f0faeb26d656 svadmin-u-03b4dd7f172b svadmin-u-5f22e64f2282 svadmin-u-fc7473ca09eb svadmin-u-2689f3958069 svadmin-u-438b2237b8d6 svadmin-u-0fe7d7d814d0 svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-77a2a20e90d4 ${messageType === 'success' ? 'svadmin-u-17a9f7af2265 svadmin-u-76747e5e02ff svadmin-u-3daca9af0861 svadmin-u-be62e835b692' : 'svadmin-u-43928fcc832f svadmin-u-811148b13d1e svadmin-u-3daca9af0861 svadmin-u-5b92fd698365'}`}
|
|
111
|
+
>
|
|
91
112
|
{#if messageType === 'success'}
|
|
92
113
|
<Check class="svadmin-u-dc7972ebf3f3 svadmin-u-11e59c6d5f6b" />
|
|
93
114
|
{:else}
|
|
@@ -100,8 +121,8 @@
|
|
|
100
121
|
|
|
101
122
|
<div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-2adea12c41f3 svadmin-u-0d304f904cb0 svadmin-u-36e579c0b41c svadmin-u-243d62579c55">
|
|
102
123
|
<div class="svadmin-u-6da6a3c3f741 svadmin-u-5f22e64f2282 svadmin-u-ca6bcd4b6f3f svadmin-u-18049387f0af svadmin-u-cd0ad9a56558 svadmin-u-8e63407b5ceb svadmin-u-438b2237b8d6 svadmin-u-a327049cac5b">
|
|
103
|
-
<label for=
|
|
104
|
-
<select id=
|
|
124
|
+
<label for={`${instanceId}-permission-role-select`} class="svadmin-u-a77ed4d908c0 svadmin-u-0214b4b355d1 svadmin-u-fc7473ca09eb svadmin-u-2689f3958069 svadmin-u-d4108abe6359">{i18n.t('permissions.roles') || 'Roles'}</label>
|
|
125
|
+
<select id={`${instanceId}-permission-role-select`} value={selectedRole} onchange={(event) => { selectedRole = event.currentTarget.value; }} class="svadmin-u-e7a768f922d2 svadmin-u-6da6a3c3f741 svadmin-u-421ac2be5045 svadmin-u-ca6bcd4b6f3f svadmin-u-e5795dad4d22 svadmin-u-e6f9e383a762 svadmin-u-0e17f2bd9074 svadmin-u-fc7473ca09eb svadmin-u-582e6ef4b245 svadmin-u-55d048ebfb1c svadmin-u-608dd26cd5ba svadmin-u-80b9d0ae125f svadmin-u-6b22a22a9752" disabled={roles.length === 0}>
|
|
105
126
|
{#each roles as role (role.code)}<option value={role.code}>{role.name}</option>{/each}
|
|
106
127
|
</select>
|
|
107
128
|
{#if roles.length === 0}<p class="svadmin-u-50d0d216a2f8 svadmin-u-fc7473ca09eb svadmin-u-bfa603190748">{i18n.t('permissions.noRoles') || 'No roles available'}</p>{/if}
|
|
@@ -114,7 +135,9 @@
|
|
|
114
135
|
<div class="svadmin-u-da7c36cd8867 svadmin-u-36e579c0b41c svadmin-u-92bf82f493b1">
|
|
115
136
|
{#each roles as role, _i (_i)}
|
|
116
137
|
<button
|
|
138
|
+
type="button"
|
|
117
139
|
onclick={() => selectedRole = role.code}
|
|
140
|
+
aria-pressed={selectedRole === role.code}
|
|
118
141
|
class="svadmin-u-6da6a3c3f741 svadmin-u-2eba0d65d059 svadmin-u-0e17f2bd9074 svadmin-u-e7ee55ac7ffe svadmin-u-5f22e64f2282 svadmin-u-fc7473ca09eb svadmin-u-0fe7d7d814d0 svadmin-u-625a4c3fbeb2 {selectedRole === role.code ? 'svadmin-u-375dc44df6e9 svadmin-u-20aaf08a7ed1 svadmin-u-2689f3958069 svadmin-u-438b2237b8d6 svadmin-u-3daca9af0861 svadmin-u-2b6f77ad4036' : 'svadmin-u-d4108abe6359 svadmin-u-8e551981c8d7'}"
|
|
119
142
|
>
|
|
120
143
|
{role.name}
|
|
@@ -161,6 +184,9 @@
|
|
|
161
184
|
|
|
162
185
|
<!-- Table Area -->
|
|
163
186
|
<div class="svadmin-u-36e579c0b41c svadmin-u-73fc3fb18ceb svadmin-u-8e63407b5ceb">
|
|
187
|
+
{#if tableContent}
|
|
188
|
+
{@render tableContent()}
|
|
189
|
+
{:else}
|
|
164
190
|
<table class="svadmin-u-a1e7a8069878 svadmin-u-5f22e64f2282 svadmin-u-2cd02d11d1af svadmin-u-438b2237b8d6 svadmin-u-3daca9af0861 svadmin-u-a10fdd7667ee">
|
|
165
191
|
<thead class="svadmin-u-358af0b65a31">
|
|
166
192
|
<tr>
|
|
@@ -197,19 +223,28 @@
|
|
|
197
223
|
|
|
198
224
|
<!-- Action Checkboxes -->
|
|
199
225
|
{#each actions as action, _i (_i)}
|
|
200
|
-
{@const
|
|
226
|
+
{@const decision = selectedRole ? resolveDecision(selectedRole, resource.code, action.code) : 'unknown'}
|
|
227
|
+
{@const granted = decision === 'granted'}
|
|
228
|
+
{@const actionable = !loading && !!selectedRole && decision !== 'unknown'}
|
|
201
229
|
<td class="svadmin-u-e82ae8be04aa svadmin-u-0e17f2bd9074 svadmin-u-1b2d54a3fd12 svadmin-u-ca6bf63030aa">
|
|
202
230
|
<div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227">
|
|
203
231
|
<button
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
232
|
+
type="button"
|
|
233
|
+
disabled={!actionable}
|
|
234
|
+
aria-label={translation.t('permissions.toggleAction').replaceAll('{action}', () => action.name)}
|
|
235
|
+
aria-describedby={`${instanceId}-permission-${resource.code}-${action.code}`}
|
|
236
|
+
aria-pressed={granted}
|
|
237
|
+
onclick={() => { if (actionable) onToggle(selectedRole as string, resource.code, action.code, !granted); }}
|
|
238
|
+
class="svadmin-u-7ec10f86d9b1 svadmin-u-f6fe902450dc svadmin-u-07389a777c1f svadmin-u-ca6bcd4b6f3f svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-0fe7d7d814d0 svadmin-u-55d048ebfb1c svadmin-u-608dd26cd5ba svadmin-u-094cedcfedfd {granted ? 'svadmin-u-75b1bec3ea0e svadmin-u-6cbc84dd9e1a svadmin-u-30ca335ae9c2' : 'svadmin-u-e5795dad4d22 svadmin-u-e6f9e383a762 svadmin-u-0557b88819cd'} {!actionable ? 'svadmin-u-0b8c506a0596 svadmin-u-29b733e4c162' : 'svadmin-u-34516836730d'}"
|
|
208
239
|
>
|
|
209
240
|
<svg class="svadmin-u-bf600f8e029c svadmin-u-7fc7f732bf7e {granted ? 'svadmin-u-0214b4b355d1' : 'svadmin-u-99d72c7fc3e2'}" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3">
|
|
210
241
|
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
|
|
211
242
|
</svg>
|
|
212
243
|
</button>
|
|
244
|
+
<span id={`${instanceId}-permission-${resource.code}-${action.code}`} class="svadmin-sr-only">
|
|
245
|
+
{roles.find(role => role.code === selectedRole)?.name || selectedRole} {selectedRole ? `(${selectedRole})` : ''}
|
|
246
|
+
/ {resource.name} ({resource.code}) / {action.name} ({action.code})
|
|
247
|
+
</span>
|
|
213
248
|
</div>
|
|
214
249
|
</td>
|
|
215
250
|
{/each}
|
|
@@ -217,6 +252,7 @@
|
|
|
217
252
|
{/each}
|
|
218
253
|
</tbody>
|
|
219
254
|
</table>
|
|
255
|
+
{/if}
|
|
220
256
|
</div>
|
|
221
257
|
</div>
|
|
222
258
|
</div>
|
|
@@ -12,6 +12,7 @@ type $$ComponentProps = {
|
|
|
12
12
|
conflictCount?: number;
|
|
13
13
|
sidebarExtra?: Snippet;
|
|
14
14
|
headerExtra?: Snippet;
|
|
15
|
+
tableContent?: Snippet | undefined;
|
|
15
16
|
onToggle: (role: string, resource: string, action: string, grant: boolean) => void;
|
|
16
17
|
};
|
|
17
18
|
declare const PermissionMatrix: import("svelte").Component<$$ComponentProps, {}, "selectedRole">;
|