@isoftdata/svelte-user-configuration 2.0.4 → 2.0.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.
@@ -1,282 +1,282 @@
1
- <script lang="ts">
2
- import type { ButtonColors } from '@isoftdata/utility-bootstrap'
3
- import type { i18n } from 'i18next'
4
- import type { Merge } from 'type-fest'
5
- import type { IconName, SiteLabel, HTMLDivAttributes } from './'
6
- import type { Snippet } from 'svelte'
7
-
8
- import { getContext } from 'svelte'
9
- import camelCase from 'just-camel-case'
10
- import Icon from '@isoftdata/svelte-icon'
11
- import Button from '@isoftdata/svelte-button'
12
- import Select from '@isoftdata/svelte-select'
13
- import { getEventValueEnum } from '@isoftdata/browser-event'
14
- import { Table, Td, type Column } from '@isoftdata/svelte-table'
15
- import { translate as defaultTranslate } from '@isoftdata/utility-string'
16
- import { SvelteSet, SvelteMap } from 'svelte/reactivity'
17
- import { RadioButtonGroup } from '@isoftdata/svelte-checkbox'
18
- import Badge from '@isoftdata/svelte-badge'
19
-
20
- const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
21
-
22
- type Scope = 'NONE' | 'SITE' | 'GLOBAL'
23
- type Permission = {
24
- id: number
25
- displayName: string
26
- category: string
27
- codeName: string
28
- }
29
- type ComputedPermission = Merge<
30
- Permission,
31
- { value: PermissionValue; groupValue?: PermissionValue | undefined; computedValue?: PermissionValue }
32
- >
33
- type ComputedPermissionColumns = Array<Column<ComputedPermission>>
34
- type PermissionValue = keyof typeof permissionValueList
35
- type PermissionValueMap = Map<number, PermissionValue>
36
-
37
- interface Props extends HTMLDivAttributes {
38
- permissions: Array<Permission>
39
- siteLabel?: SiteLabel
40
- permissionValueChange?:
41
- | ((change: { value: PermissionValue; permissionIds: Array<number> }) => void | Promise<void>)
42
- | undefined
43
- icon?: IconName
44
- cardHeaderTitle?: string
45
- permissionValueMap: PermissionValueMap
46
- groupPermissionValueMap?: PermissionValueMap | undefined
47
- children?: Snippet
48
- }
49
-
50
- let {
51
- permissions,
52
- siteLabel = 'Site',
53
- permissionValueChange = undefined,
54
- icon = 'user-lock',
55
- cardHeaderTitle = translate('common:permissions', 'Permissions'),
56
- permissionValueMap = $bindable(new SvelteMap()),
57
- groupPermissionValueMap = undefined,
58
- children,
59
- ...rest
60
- }: Props = $props()
61
-
62
- let selectedPermissionIds: SvelteSet<number> = $state(new SvelteSet())
63
- let selectedPermissionValue: string | null = $state(null)
64
-
65
- const permissionColumns: ComputedPermissionColumns = getColumns(groupPermissionValueMap)
66
- const permissionValueList: Record<Scope, { label: string; value: Scope; color: ButtonColors }> = {
67
- NONE: { label: translate('common:permissionLevel.none', 'None'), value: 'NONE', color: 'danger' },
68
- SITE: {
69
- label: translate(`common:permissionLevel.${siteLabel.toLowerCase()}`, siteLabel),
70
- value: 'SITE',
71
- color: 'primary',
72
- },
73
- GLOBAL: { label: translate('common:permissionLevel.global', 'Global'), value: 'GLOBAL', color: 'success' },
74
- }
75
- const permissionValues = Object.values(permissionValueList)
76
-
77
- function getColumns(groupPermissionValueMap: PermissionValueMap | undefined): ComputedPermissionColumns {
78
- let columns: ComputedPermissionColumns = [
79
- {
80
- property: 'value',
81
- name: groupPermissionValueMap ? translate('common:user', 'User') : translate('common:permission', 'Permission'),
82
- width: '1rem',
83
- },
84
- { property: 'category', name: translate('common:category', 'Category') },
85
- { property: 'displayName', name: translate('common:name', 'Name') },
86
- ]
87
-
88
- if (groupPermissionValueMap) {
89
- columns.splice(
90
- 1,
91
- 0,
92
- {
93
- property: 'groupValue',
94
- name: translate('common:group', 'Group'),
95
- align: 'center',
96
- width: '1rem',
97
- minWidth: '25%',
98
- },
99
- {
100
- property: 'computedValue',
101
- name: translate('common:access', 'Access'),
102
- align: 'center',
103
- width: '1rem',
104
- minWidth: '25%',
105
- },
106
- )
107
- }
108
-
109
- return columns
110
- }
111
-
112
- function selectPermissions(permissionId: number) {
113
- selectedPermissionIds.has(permissionId)
114
- ? selectedPermissionIds.delete(permissionId)
115
- : selectedPermissionIds.add(permissionId)
116
- }
117
-
118
- function selectAndDeselectAllPermissions() {
119
- if (selectedPermissionIds.size === computedPermissions.length) {
120
- selectedPermissionIds.clear()
121
- selectedPermissionValue = null
122
- } else {
123
- selectedPermissionIds = new SvelteSet(permissions.map(permission => permission.id))
124
- }
125
- }
126
-
127
- async function updatePermissionValues(newValue: Scope, permissionIds: SvelteSet<number>) {
128
- const permissionIdsArray = Array.from(permissionIds)
129
- for (const id of permissionIdsArray) {
130
- permissionValueMap.set(id, newValue)
131
- }
132
- permissionValueMap = permissionValueMap
133
- await permissionValueChange?.({
134
- value: newValue,
135
- permissionIds: permissionIdsArray,
136
- })
137
- }
138
-
139
- function getComputedPermissions(
140
- permissions: Array<Permission>,
141
- permissionValueMap: PermissionValueMap,
142
- groupPermissionValueMap: PermissionValueMap | undefined,
143
- ): Array<ComputedPermission> {
144
- return permissions.map(permission => {
145
- const value = permissionValueMap.get(permission.id) ?? 'NONE'
146
- const groupValue = groupPermissionValueMap?.get(permission.id)
147
-
148
- return {
149
- ...permission,
150
- displayName: translate(`permissions:${camelCase(permission.codeName)}.displayName`, permission.displayName),
151
- category: translate(`permissions:categories.${camelCase(permission.category)}`, permission.category),
152
- value,
153
- groupValue,
154
- computedValue:
155
- (value === 'NONE' && groupValue) || (value === 'SITE' && groupValue === 'GLOBAL') ? groupValue : value,
156
- }
157
- })
158
- }
159
-
160
- let computedPermissions = $derived(getComputedPermissions(permissions, permissionValueMap, groupPermissionValueMap))
161
-
162
- $inspect(computedPermissions)
163
- </script>
164
-
165
- <div
166
- class="card"
167
- {...rest}
168
- >
169
- <div class="card-header">
170
- <h5 class="mb-0">
171
- <Icon
172
- {icon}
173
- class="mr-1 me-1"
174
- />
175
- {cardHeaderTitle}
176
- </h5>
177
- </div>
178
- <div class="card-body">
179
- <Table
180
- class="mb-0"
181
- idProp="id"
182
- parentClass="mh-60vh overflow-y-auto"
183
- filterPlaceholder={translate('common:filterPermissions', 'Filter Permissions')}
184
- filterColumnClass="col-6 col-lg-4 align-self-end"
185
- rows={computedPermissions}
186
- columns={permissionColumns}
187
- responsive
188
- filterEnabled
189
- >
190
- {#snippet children({ row: permission })}
191
- <tr
192
- onclick={() => selectPermissions(permission.id)}
193
- class:table-primary={selectedPermissionIds.has(permission.id)}
194
- class="cursor-pointer"
195
- >
196
- <Td property="value">
197
- <div
198
- class="btn-group btn-group-toggle"
199
- data-toggle="buttons"
200
- >
201
- <RadioButtonGroup
202
- options={permissionValues}
203
- value={permission.value}
204
- unselectedColor="secondary"
205
- change={value => updatePermissionValues(value, new SvelteSet([permission.id]))}
206
- ></RadioButtonGroup>
207
- </div>
208
- </Td>
209
- {#if groupPermissionValueMap}
210
- <Td
211
- property="groupValue"
212
- class="px-4"
213
- >
214
- <Badge
215
- pill
216
- color={permissionValueList[permission.groupValue ?? 'NONE'].color}
217
- >{permissionValueList[permission.groupValue ?? 'NONE'].label}</Badge
218
- >
219
- </Td>
220
- <Td
221
- property="computedValue"
222
- class="px-4"
223
- >
224
- <Badge
225
- pill
226
- color={permissionValueList[permission.computedValue ?? 'NONE'].color}
227
- >{permissionValueList[permission.computedValue ?? 'NONE'].label}</Badge
228
- >
229
- </Td>
230
- {/if}
231
- <Td property="category">{permission.category}</Td>
232
- <Td property="displayName">{permission.displayName}</Td>
233
- </tr>
234
- {/snippet}
235
- </Table>
236
- {@render children?.()}
237
- </div>
238
- <div class="card-footer">
239
- <div class="d-flex justify-content-between align-items-end">
240
- <Select
241
- label="{translate('common:setSelectedTo', 'Set Selected To')}:"
242
- bind:value={selectedPermissionValue}
243
- showEmptyOption={selectedPermissionValue === null}
244
- emptyValue={null}
245
- emptyText={translate('common:selectValue', 'Select a Value')}
246
- labelParentClass="form-inline mr-2 me-2"
247
- labelClass="mr-2 me-2 p-0"
248
- disabled={selectedPermissionIds.size === 0}
249
- onchange={e => {
250
- const value = getEventValueEnum(e, 'NONE', 'SITE', 'GLOBAL')
251
- updatePermissionValues(value, selectedPermissionIds)
252
- selectedPermissionIds.clear()
253
- selectedPermissionValue = null
254
- }}
255
- >
256
- {#each Object.values(permissionValueList) as permission}
257
- <option value={permission.value}>{permission.label}</option>
258
- {/each}
259
- </Select>
260
- <Button
261
- class="mb-1"
262
- outline
263
- size="sm"
264
- iconClass="check-double"
265
- onclick={selectAndDeselectAllPermissions}
266
- >
267
- <span class:d-none={selectedPermissionIds.size !== computedPermissions.length}
268
- >{translate('common:deselectAll', 'Deselect All')}</span
269
- >
270
- <span class:d-none={selectedPermissionIds.size === computedPermissions.length}
271
- >{translate('common:selectAll', 'Select All')}</span
272
- >
273
- </Button>
274
- </div>
275
- </div>
276
- </div>
277
-
278
- <style>
279
- .cursor-pointer {
280
- cursor: pointer;
281
- }
282
- </style>
1
+ <script lang="ts">
2
+ import type { ButtonColors } from '@isoftdata/utility-bootstrap'
3
+ import type { i18n } from 'i18next'
4
+ import type { Merge } from 'type-fest'
5
+ import type { IconName, SiteLabel, HTMLDivAttributes } from './'
6
+ import type { Snippet } from 'svelte'
7
+
8
+ import { getContext } from 'svelte'
9
+ import camelCase from 'just-camel-case'
10
+ import Icon from '@isoftdata/svelte-icon'
11
+ import Button from '@isoftdata/svelte-button'
12
+ import Select from '@isoftdata/svelte-select'
13
+ import { getEventValueEnum } from '@isoftdata/browser-event'
14
+ import { Table, Td, type Column } from '@isoftdata/svelte-table'
15
+ import { translate as defaultTranslate } from '@isoftdata/utility-string'
16
+ import { SvelteSet, SvelteMap } from 'svelte/reactivity'
17
+ import { RadioButtonGroup } from '@isoftdata/svelte-checkbox'
18
+ import Badge from '@isoftdata/svelte-badge'
19
+
20
+ const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
21
+
22
+ type Scope = 'NONE' | 'SITE' | 'GLOBAL'
23
+ type Permission = {
24
+ id: number
25
+ displayName: string
26
+ category: string
27
+ codeName: string
28
+ }
29
+ type ComputedPermission = Merge<
30
+ Permission,
31
+ { value: PermissionValue; groupValue?: PermissionValue | undefined; computedValue?: PermissionValue }
32
+ >
33
+ type ComputedPermissionColumns = Array<Column<ComputedPermission>>
34
+ type PermissionValue = keyof typeof permissionValueList
35
+ type PermissionValueMap = Map<number, PermissionValue>
36
+
37
+ interface Props extends HTMLDivAttributes {
38
+ permissions: Array<Permission>
39
+ siteLabel?: SiteLabel
40
+ permissionValueChange?:
41
+ | ((change: { value: PermissionValue; permissionIds: Array<number> }) => void | Promise<void>)
42
+ | undefined
43
+ icon?: IconName
44
+ cardHeaderTitle?: string
45
+ permissionValueMap: PermissionValueMap
46
+ groupPermissionValueMap?: PermissionValueMap | undefined
47
+ children?: Snippet
48
+ }
49
+
50
+ let {
51
+ permissions,
52
+ siteLabel = 'Site',
53
+ permissionValueChange = undefined,
54
+ icon = 'user-lock',
55
+ cardHeaderTitle = translate('common:permissions', 'Permissions'),
56
+ permissionValueMap = $bindable(new SvelteMap()),
57
+ groupPermissionValueMap = undefined,
58
+ children,
59
+ ...rest
60
+ }: Props = $props()
61
+
62
+ let selectedPermissionIds: SvelteSet<number> = $state(new SvelteSet())
63
+ let selectedPermissionValue: string | null = $state(null)
64
+
65
+ const permissionColumns: ComputedPermissionColumns = getColumns(groupPermissionValueMap)
66
+ const permissionValueList: Record<Scope, { label: string; value: Scope; color: ButtonColors }> = {
67
+ NONE: { label: translate('common:permissionLevel.none', 'None'), value: 'NONE', color: 'danger' },
68
+ SITE: {
69
+ label: translate(`common:permissionLevel.${siteLabel.toLowerCase()}`, siteLabel),
70
+ value: 'SITE',
71
+ color: 'primary',
72
+ },
73
+ GLOBAL: { label: translate('common:permissionLevel.global', 'Global'), value: 'GLOBAL', color: 'success' },
74
+ }
75
+ const permissionValues = Object.values(permissionValueList)
76
+
77
+ function getColumns(groupPermissionValueMap: PermissionValueMap | undefined): ComputedPermissionColumns {
78
+ let columns: ComputedPermissionColumns = [
79
+ {
80
+ property: 'value',
81
+ name: groupPermissionValueMap ? translate('common:user', 'User') : translate('common:permission', 'Permission'),
82
+ width: '1rem',
83
+ },
84
+ { property: 'category', name: translate('common:category', 'Category') },
85
+ { property: 'displayName', name: translate('common:name', 'Name') },
86
+ ]
87
+
88
+ if (groupPermissionValueMap) {
89
+ columns.splice(
90
+ 1,
91
+ 0,
92
+ {
93
+ property: 'groupValue',
94
+ name: translate('common:group', 'Group'),
95
+ align: 'center',
96
+ width: '1rem',
97
+ minWidth: '25%',
98
+ },
99
+ {
100
+ property: 'computedValue',
101
+ name: translate('common:access', 'Access'),
102
+ align: 'center',
103
+ width: '1rem',
104
+ minWidth: '25%',
105
+ },
106
+ )
107
+ }
108
+
109
+ return columns
110
+ }
111
+
112
+ function selectPermissions(permissionId: number) {
113
+ selectedPermissionIds.has(permissionId)
114
+ ? selectedPermissionIds.delete(permissionId)
115
+ : selectedPermissionIds.add(permissionId)
116
+ }
117
+
118
+ function selectAndDeselectAllPermissions() {
119
+ if (selectedPermissionIds.size === computedPermissions.length) {
120
+ selectedPermissionIds.clear()
121
+ selectedPermissionValue = null
122
+ } else {
123
+ selectedPermissionIds = new SvelteSet(permissions.map(permission => permission.id))
124
+ }
125
+ }
126
+
127
+ async function updatePermissionValues(newValue: Scope, permissionIds: SvelteSet<number>) {
128
+ const permissionIdsArray = Array.from(permissionIds)
129
+ for (const id of permissionIdsArray) {
130
+ permissionValueMap.set(id, newValue)
131
+ }
132
+ permissionValueMap = permissionValueMap
133
+ await permissionValueChange?.({
134
+ value: newValue,
135
+ permissionIds: permissionIdsArray,
136
+ })
137
+ }
138
+
139
+ function getComputedPermissions(
140
+ permissions: Array<Permission>,
141
+ permissionValueMap: PermissionValueMap,
142
+ groupPermissionValueMap: PermissionValueMap | undefined,
143
+ ): Array<ComputedPermission> {
144
+ return permissions.map(permission => {
145
+ const value = permissionValueMap.get(permission.id) ?? 'NONE'
146
+ const groupValue = groupPermissionValueMap?.get(permission.id)
147
+
148
+ return {
149
+ ...permission,
150
+ displayName: translate(`permissions:${camelCase(permission.codeName)}.displayName`, permission.displayName),
151
+ category: translate(`permissions:categories.${camelCase(permission.category)}`, permission.category),
152
+ value,
153
+ groupValue,
154
+ computedValue:
155
+ (value === 'NONE' && groupValue) || (value === 'SITE' && groupValue === 'GLOBAL') ? groupValue : value,
156
+ }
157
+ })
158
+ }
159
+
160
+ let computedPermissions = $derived(getComputedPermissions(permissions, permissionValueMap, groupPermissionValueMap))
161
+
162
+ $inspect(computedPermissions)
163
+ </script>
164
+
165
+ <div
166
+ class="card"
167
+ {...rest}
168
+ >
169
+ <div class="card-header">
170
+ <h5 class="mb-0">
171
+ <Icon
172
+ {icon}
173
+ class="mr-1 me-1"
174
+ />
175
+ {cardHeaderTitle}
176
+ </h5>
177
+ </div>
178
+ <div class="card-body">
179
+ <Table
180
+ class="mb-0"
181
+ idProp="id"
182
+ parentClass="mh-60vh overflow-y-auto"
183
+ filterPlaceholder={translate('common:filterPermissions', 'Filter Permissions')}
184
+ filterColumnClass="col-6 col-lg-4 align-self-end"
185
+ rows={computedPermissions}
186
+ columns={permissionColumns}
187
+ responsive
188
+ filterEnabled
189
+ >
190
+ {#snippet children({ row: permission })}
191
+ <tr
192
+ onclick={() => selectPermissions(permission.id)}
193
+ class:table-primary={selectedPermissionIds.has(permission.id)}
194
+ class="cursor-pointer"
195
+ >
196
+ <Td property="value">
197
+ <div
198
+ class="btn-group btn-group-toggle"
199
+ data-toggle="buttons"
200
+ >
201
+ <RadioButtonGroup
202
+ options={permissionValues}
203
+ value={permission.value}
204
+ unselectedColor="secondary"
205
+ change={value => updatePermissionValues(value, new SvelteSet([permission.id]))}
206
+ ></RadioButtonGroup>
207
+ </div>
208
+ </Td>
209
+ {#if groupPermissionValueMap}
210
+ <Td
211
+ property="groupValue"
212
+ class="px-4"
213
+ >
214
+ <Badge
215
+ pill
216
+ color={permissionValueList[permission.groupValue ?? 'NONE'].color}
217
+ >{permissionValueList[permission.groupValue ?? 'NONE'].label}</Badge
218
+ >
219
+ </Td>
220
+ <Td
221
+ property="computedValue"
222
+ class="px-4"
223
+ >
224
+ <Badge
225
+ pill
226
+ color={permissionValueList[permission.computedValue ?? 'NONE'].color}
227
+ >{permissionValueList[permission.computedValue ?? 'NONE'].label}</Badge
228
+ >
229
+ </Td>
230
+ {/if}
231
+ <Td property="category">{permission.category}</Td>
232
+ <Td property="displayName">{permission.displayName}</Td>
233
+ </tr>
234
+ {/snippet}
235
+ </Table>
236
+ {@render children?.()}
237
+ </div>
238
+ <div class="card-footer">
239
+ <div class="d-flex justify-content-between align-items-end">
240
+ <Select
241
+ label="{translate('common:setSelectedTo', 'Set Selected To')}:"
242
+ bind:value={selectedPermissionValue}
243
+ showEmptyOption={selectedPermissionValue === null}
244
+ emptyValue={null}
245
+ emptyText={translate('common:selectValue', 'Select a Value')}
246
+ labelParentClass="form-inline mr-2 me-2"
247
+ labelClass="mr-2 me-2 p-0"
248
+ disabled={selectedPermissionIds.size === 0}
249
+ onchange={e => {
250
+ const value = getEventValueEnum(e, 'NONE', 'SITE', 'GLOBAL')
251
+ updatePermissionValues(value, selectedPermissionIds)
252
+ selectedPermissionIds.clear()
253
+ selectedPermissionValue = null
254
+ }}
255
+ >
256
+ {#each Object.values(permissionValueList) as permission}
257
+ <option value={permission.value}>{permission.label}</option>
258
+ {/each}
259
+ </Select>
260
+ <Button
261
+ class="mb-1"
262
+ outline
263
+ size="sm"
264
+ iconClass="check-double"
265
+ onclick={selectAndDeselectAllPermissions}
266
+ >
267
+ <span class:d-none={selectedPermissionIds.size !== computedPermissions.length}
268
+ >{translate('common:deselectAll', 'Deselect All')}</span
269
+ >
270
+ <span class:d-none={selectedPermissionIds.size === computedPermissions.length}
271
+ >{translate('common:selectAll', 'Select All')}</span
272
+ >
273
+ </Button>
274
+ </div>
275
+ </div>
276
+ </div>
277
+
278
+ <style>
279
+ .cursor-pointer {
280
+ cursor: pointer;
281
+ }
282
+ </style>