@xen-orchestra/web-core 0.20.1 → 0.21.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.
Files changed (109) hide show
  1. package/lib/components/backup-state/VtsBackupState.vue +20 -17
  2. package/lib/components/cell-object/VtsCellObject.vue +4 -1
  3. package/lib/components/console/VtsActionsConsole.vue +7 -4
  4. package/lib/components/console/VtsClipboardConsole.vue +9 -6
  5. package/lib/components/copy-button/VtsCopyButton.vue +7 -14
  6. package/lib/components/dropdown/DropdownTitle.vue +5 -2
  7. package/lib/components/icon/NewVtsIcon.vue +49 -0
  8. package/lib/components/input-group/VtsInputGroup.vue +41 -0
  9. package/lib/components/input-wrapper/VtsInputWrapper.vue +2 -2
  10. package/lib/components/layout/VtsLayoutSidebar.vue +6 -3
  11. package/lib/components/linear-chart/VtsLinearChart.vue +4 -0
  12. package/lib/components/object-icon/VtsObjectIcon.vue +22 -0
  13. package/lib/components/quick-info-card/VtsQuickInfoCard.vue +4 -1
  14. package/lib/components/select/VtsOption.vue +10 -6
  15. package/lib/components/select/VtsSelect.vue +74 -50
  16. package/lib/components/state-hero/VtsAllDoneHero.vue +4 -1
  17. package/lib/components/state-hero/VtsAllGoodHero.vue +4 -1
  18. package/lib/components/state-hero/VtsComingSoonHero.vue +4 -1
  19. package/lib/components/state-hero/VtsErrorNoDataHero.vue +4 -1
  20. package/lib/components/state-hero/VtsLoadingHero.vue +4 -1
  21. package/lib/components/state-hero/VtsNoDataHero.vue +4 -1
  22. package/lib/components/state-hero/VtsNoSelectionHero.vue +4 -1
  23. package/lib/components/state-hero/VtsObjectNotFoundHero.vue +4 -1
  24. package/lib/components/state-hero/VtsOfflineHero.vue +4 -1
  25. package/lib/components/state-hero/VtsPageNotFoundHero.vue +4 -1
  26. package/lib/components/table/ColumnTitle.vue +2 -2
  27. package/lib/components/task/VtsQuickTaskButton.vue +4 -1
  28. package/lib/components/task/VtsQuickTaskList.vue +5 -2
  29. package/lib/components/task/VtsQuickTaskTabBar.vue +8 -5
  30. package/lib/components/ui/card-numbers/UiCardNumbers.vue +4 -1
  31. package/lib/components/ui/character-limit/UiCharacterLimit.vue +4 -1
  32. package/lib/components/ui/input/UiInput.vue +2 -2
  33. package/lib/components/ui/label/UiLabel.vue +4 -1
  34. package/lib/components/ui/progress-bar/UiProgressBar.vue +5 -2
  35. package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +9 -6
  36. package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +6 -3
  37. package/lib/components/ui/stacked-bar/StackedBarSegment.vue +4 -1
  38. package/lib/components/ui/table-pagination/UiTablePagination.vue +6 -3
  39. package/lib/components/ui/text-area/UiTextarea.vue +4 -1
  40. package/lib/components/ui/top-bottom-table/UiTopBottomTable.vue +6 -3
  41. package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +4 -1
  42. package/lib/composables/local-time-ago.composable.ts +53 -0
  43. package/lib/composables/locale-time-ago.composable.ts +53 -0
  44. package/lib/icons/fa-icons.ts +164 -0
  45. package/lib/icons/index.ts +15 -0
  46. package/lib/icons/legacy-icons.ts +80 -0
  47. package/lib/icons/object-icons.ts +187 -0
  48. package/lib/layouts/CoreLayout.vue +7 -3
  49. package/lib/locales/cs.json +0 -1
  50. package/lib/locales/de.json +1 -1
  51. package/lib/locales/en.json +32 -4
  52. package/lib/locales/es.json +1 -1
  53. package/lib/locales/fr.json +31 -3
  54. package/lib/locales/it.json +1 -1
  55. package/lib/locales/nl.json +1 -1
  56. package/lib/locales/ru.json +1 -1
  57. package/lib/locales/sv.json +1 -2
  58. package/lib/packages/collection/README.md +23 -18
  59. package/lib/packages/collection/create-collection.ts +22 -21
  60. package/lib/packages/collection/create-item.ts +21 -20
  61. package/lib/packages/collection/create-use-subset.ts +23 -0
  62. package/lib/packages/collection/guess-item-id.ts +26 -16
  63. package/lib/packages/collection/index.ts +4 -0
  64. package/lib/packages/collection/types.ts +65 -37
  65. package/lib/packages/collection/use-collection.ts +68 -18
  66. package/lib/packages/collection/use-flag-registry.ts +38 -17
  67. package/lib/packages/form-select/guess-label.ts +45 -0
  68. package/lib/packages/form-select/guess-value.ts +23 -0
  69. package/lib/packages/form-select/index.ts +6 -0
  70. package/lib/packages/form-select/normalize-search-term.ts +11 -0
  71. package/lib/packages/form-select/types.ts +90 -42
  72. package/lib/packages/form-select/use-form-option-controller.ts +7 -3
  73. package/lib/packages/form-select/use-form-select-controller.ts +38 -27
  74. package/lib/packages/form-select/use-form-select-keyboard-navigation.ts +1 -1
  75. package/lib/packages/form-select/use-form-select.ts +308 -130
  76. package/lib/packages/icon/DisplayIcon.vue +25 -0
  77. package/lib/packages/icon/DisplayIconAny.vue +16 -0
  78. package/lib/packages/icon/DisplayIconSingle.vue +35 -0
  79. package/lib/packages/icon/DisplayIconStack.vue +34 -0
  80. package/lib/packages/icon/README.md +286 -0
  81. package/lib/packages/icon/create-icon-bindings.ts +27 -0
  82. package/lib/packages/icon/define-icon-pack.ts +23 -0
  83. package/lib/packages/icon/define-icon-single.ts +17 -0
  84. package/lib/packages/icon/define-icon-stack.ts +20 -0
  85. package/lib/packages/icon/define-icon.ts +40 -0
  86. package/lib/packages/icon/generate-icon-variants.ts +17 -0
  87. package/lib/packages/icon/index.ts +8 -0
  88. package/lib/packages/icon/is-icon-stack.ts +5 -0
  89. package/lib/packages/icon/merge-icons.ts +25 -0
  90. package/lib/packages/icon/merge-transforms.ts +12 -0
  91. package/lib/packages/icon/normalize-icon.ts +25 -0
  92. package/lib/packages/icon/to-tuple.ts +7 -0
  93. package/lib/packages/icon/types.ts +72 -0
  94. package/lib/packages/job/README.md +2 -2
  95. package/lib/packages/mapper/README.md +166 -0
  96. package/lib/packages/mapper/convert-to-map.ts +5 -0
  97. package/lib/packages/mapper/create-mapper.ts +30 -0
  98. package/lib/packages/mapper/index.ts +4 -0
  99. package/lib/packages/mapper/types.ts +1 -0
  100. package/lib/packages/mapper/use-mapper.ts +31 -0
  101. package/lib/stores/sidebar.store.ts +1 -1
  102. package/lib/types/chart.ts +2 -2
  103. package/lib/types/utility.type.ts +9 -0
  104. package/lib/utils/object.util.ts +16 -0
  105. package/lib/utils/size.util.ts +4 -2
  106. package/package.json +2 -1
  107. package/lib/components/backup-item/VtsBackupItem.vue +0 -47
  108. package/lib/composables/mapper.composable.md +0 -74
  109. package/lib/composables/mapper.composable.ts +0 -18
@@ -0,0 +1,286 @@
1
+ # Icon System
2
+
3
+ A flexible icon system for Vue applications supporting single icons, icon stacks, icon packs, and transformations.
4
+
5
+ Currently supported icon format: FontAwesome, SimpleIcons
6
+
7
+ ## Core Concepts
8
+
9
+ - **Icon**: A single SVG icon with transformations
10
+ - **Icon Stack**: Multiple icons layered on top of each other
11
+ - **Icon Pack**: A collection of related icons
12
+ - **Transformations**: Size, color, rotation, flip, and other visual modifications
13
+
14
+ ## Usage
15
+
16
+ ### Basic Icon
17
+
18
+ ```vue
19
+ <template>
20
+ <DisplayIcon :icon />
21
+ </template>
22
+
23
+ <script lang="ts" setup>
24
+ const icon = defineIcon({
25
+ icon: faUser,
26
+ color: 'blue',
27
+ size: 24,
28
+ })
29
+ </script>
30
+ ```
31
+
32
+ ### Icon Stack
33
+
34
+ ```vue
35
+ <template>
36
+ <DisplayIcon :icon="stackedIcon" />
37
+ </template>
38
+
39
+ <script lang="ts" setup>
40
+ const stackedIcon = defineIcon([
41
+ { icon: faCircle, size: 24, color: 'blue' },
42
+ { icon: faStar, size: 18, color: 'white' },
43
+ ])
44
+ </script>
45
+ ```
46
+
47
+ ### Icon Pack
48
+
49
+ ```vue
50
+ <template>
51
+ <DisplayIcon :icon="icons.user" />
52
+ <DisplayIcon :icon="icons.star" />
53
+ </template>
54
+
55
+ <script lang="ts" setup>
56
+ const icons = defineIconPack({
57
+ user: { icon: faUser, color: 'blue' },
58
+ star: { icon: faStar, color: 'gold' },
59
+ })
60
+ </script>
61
+ ```
62
+
63
+ ### Icon Variants
64
+
65
+ ```vue
66
+ <template>
67
+ <DisplayIcon :icon="alerts['error:triangle']" />
68
+ <DisplayIcon :icon="alerts['warning:circle']" />
69
+ </template>
70
+
71
+ <script lang="ts" setup>
72
+ const alerts = defineIcon(
73
+ [
74
+ ['error', 'warning'],
75
+ ['circle', 'triangle'],
76
+ ],
77
+ (status, shape) => {
78
+ const colors = {
79
+ error: 'red',
80
+ warning: 'orange',
81
+ }
82
+
83
+ const shapes = {
84
+ circle: faCircle,
85
+ triangle: defineIcon({ icon: faPlay, rotate: 90, size: 20 }),
86
+ }
87
+
88
+ return [
89
+ { icon: shapes[shape], color: colors[status] },
90
+ { icon: faExclamation, color: 'white' },
91
+ ]
92
+ }
93
+ )
94
+ </script>
95
+ ```
96
+
97
+ ## API Reference
98
+
99
+ ### Functions
100
+
101
+ #### `defineIcon`
102
+
103
+ Create a single icon, an icon stack, or an icon pack with variants.
104
+
105
+ **Signature 1: Single Icon or Icon Stack**
106
+
107
+ ```ts
108
+ defineIcon(config: IconSingleConfig): Icon
109
+ defineIcon(icons: (IconSingleConfig | Icon)[], config?: IconStackConfig): IconStack
110
+ ```
111
+
112
+ **Signature 2: Icon Variants**
113
+
114
+ ```ts
115
+ defineIcon(
116
+ args: string[][],
117
+ builder: (...args: string[]) => DefineIconConfig,
118
+ stackConfig?: IconStackConfig
119
+ ): IconPack<string>
120
+ ```
121
+
122
+ #### `defineIconPack`
123
+
124
+ Create a collection of related icons.
125
+
126
+ ```ts
127
+ defineIconPack(config: IconPackConfig): IconPack<string>
128
+ ```
129
+
130
+ ### Components
131
+
132
+ #### `DisplayIcon`
133
+
134
+ Root component that renders either a single icon or an icon stack.
135
+
136
+ ```vue
137
+ <DisplayIcon :icon />
138
+ ```
139
+
140
+ ### Types
141
+
142
+ #### `IconTransforms`
143
+
144
+ Transformations that can be applied to icons.
145
+
146
+ ```ts
147
+ type IconTransforms = {
148
+ borderColor?: string // Add a border around the icon
149
+ translate?: number | [number, number] // Move the icon
150
+ size?: number | [number, number] // Resize the icon
151
+ rotate?: number // Rotate the icon (in degrees)
152
+ flip?: 'horizontal' | 'vertical' | 'both' // Flip the icon
153
+ color?: string // Change icon color
154
+ }
155
+ ```
156
+
157
+ #### `IconSingleConfig`
158
+
159
+ Configuration for a single icon.
160
+
161
+ ```ts
162
+ type IconSingleConfig = {
163
+ icon?: IconDefinition | SimpleIcon | IconSingle | IconStack
164
+ } & IconTransforms
165
+ ```
166
+
167
+ #### `IconStackConfig`
168
+
169
+ Configuration for an icon stack.
170
+
171
+ ```ts
172
+ type IconStackConfig = IconTransforms
173
+ ```
174
+
175
+ ## Examples
176
+
177
+ ### Basic Transformations
178
+
179
+ ```vue
180
+ <script lang="ts" setup>
181
+ // Color
182
+ const blueIcon = defineIcon({ icon: faUser, color: 'blue' })
183
+
184
+ // Size
185
+ const largeIcon = defineIcon({ icon: faUser, size: 32 })
186
+
187
+ // Rotate
188
+ const rotatedIcon = defineIcon({ icon: faUser, rotate: 45 })
189
+
190
+ // Flip
191
+ const flippedIcon = defineIcon({ icon: faUser, flip: 'horizontal' })
192
+
193
+ // Multiple transformations
194
+ const customIcon = defineIcon({
195
+ icon: faUser,
196
+ color: 'green',
197
+ size: 24,
198
+ rotate: 15,
199
+ translate: [2, 0],
200
+ })
201
+ </script>
202
+ ```
203
+
204
+ ### Complex Icon Stack
205
+
206
+ ```vue
207
+ <script lang="ts" setup>
208
+ import { defineIcon, DisplayIconAny } from '@core/packages/icon'
209
+ import { faCircle, faSquare, faStar } from '@fortawesome/free-solid-svg-icons'
210
+
211
+ const notificationIcon = defineIcon(
212
+ [
213
+ { icon: faCircle, size: 24, color: 'red' },
214
+ { icon: faSquare, size: 16, color: 'white', rotate: 45 },
215
+ { icon: faStar, size: 10, color: 'gold' },
216
+ ],
217
+ { translate: [2, 0] } // Global transforms applied to the entire stack
218
+ )
219
+ </script>
220
+ ```
221
+
222
+ ### Icon Variants
223
+
224
+ ```vue
225
+ <script lang="ts" setup>
226
+ const icon = defineIcon(
227
+ [
228
+ ['circle', 'square'],
229
+ ['!', '?'],
230
+ ],
231
+ (shape, type) => [
232
+ { icon: shape === 'circle' ? faCircle : faSquare },
233
+ {
234
+ icon: type === '!' ? faExclamation : faQuestion,
235
+ color: 'white',
236
+ size: 12,
237
+ },
238
+ ]
239
+ )
240
+
241
+ // You can now use icon['circle:!'], icon['circle:?'], icon['square:!'], icon['square:?']
242
+ </script>
243
+ ```
244
+
245
+ ### Icon Pack with Namespaces
246
+
247
+ ```vue
248
+ <script lang="ts" setup>
249
+ import { defineIconPack, DisplayIconAny } from '@core/packages/icon'
250
+ import { faUser, faUsers, faUserPlus } from '@fortawesome/free-solid-svg-icons'
251
+ import { faFile, faFolder, faImage } from '@fortawesome/free-solid-svg-icons'
252
+
253
+ const icons = defineIconPack({
254
+ user: defineIconPack({
255
+ single: { icon: faUser },
256
+ group: { icon: faUsers },
257
+ add: { icon: faUserPlus },
258
+ }),
259
+ file: defineIconPack({
260
+ document: { icon: faFile },
261
+ folder: { icon: faFolder },
262
+ image: { icon: faImage },
263
+ }),
264
+ })
265
+
266
+ // Access icons with namespace
267
+ // icons['user:single']
268
+ // icons['file:folder']
269
+ </script>
270
+ ```
271
+
272
+ ### Reusable Icon with Different Transformations
273
+
274
+ ```vue
275
+ <script lang="ts" setup>
276
+ import { defineIcon, DisplayIconAny } from '@core/packages/icon'
277
+ import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'
278
+
279
+ // Define base icon
280
+ const warningIcon = defineIcon({ icon: faExclamationTriangle })
281
+
282
+ // Create variations with different transformations
283
+ const smallWarningIcon = defineIcon({ icon: warningIcon, size: 12 })
284
+ const redWarningIcon = defineIcon({ icon: warningIcon, color: 'red' })
285
+ </script>
286
+ ```
@@ -0,0 +1,27 @@
1
+ import type { IconBindings, IconTransforms } from './types.ts'
2
+ import { toTuple } from './to-tuple.ts'
3
+
4
+ export function createIconBindings(transforms: IconTransforms): IconBindings {
5
+ const [translateX, translateY] = toTuple(transforms.translate, [0, 0])
6
+
7
+ const shouldTranslate = translateX !== 0 || translateY !== 0
8
+
9
+ const [sizeX, sizeY] = toTuple(transforms.size, [16, 16])
10
+
11
+ const flipX = transforms.flip === 'horizontal' || transforms.flip === 'both' ? -1 : 1
12
+
13
+ const flipY = transforms.flip === 'vertical' || transforms.flip === 'both' ? -1 : 1
14
+
15
+ const [scaleX, scaleY] = [(sizeX / 16) * flipX, (sizeY / 16) * flipY]
16
+
17
+ const shouldScale = scaleX !== 1 || scaleY !== 1
18
+
19
+ return {
20
+ style: {
21
+ color: transforms.color,
22
+ translate: shouldTranslate ? `${(translateX / 16) * 100}% ${(translateY / 16) * 100}%` : undefined,
23
+ rotate: transforms.rotate ? `${transforms.rotate}deg` : undefined,
24
+ scale: shouldScale ? `${scaleX} ${scaleY}` : undefined,
25
+ },
26
+ }
27
+ }
@@ -0,0 +1,23 @@
1
+ import { defineIcon } from './define-icon.ts'
2
+ import { type Icon, ICON_SYMBOL, type IconPack, type IconPackConfig, type IconPackKeys } from './types.ts'
3
+
4
+ export function defineIconPack<TConfig extends IconPackConfig>(
5
+ config: TConfig
6
+ ): IconPack<IconPackKeys<NoInfer<TConfig>>> {
7
+ const result: Record<string, Icon> = Object.fromEntries(
8
+ Object.entries(config).flatMap(([key, value]) => {
9
+ if (!(ICON_SYMBOL in value)) {
10
+ return [[key, defineIcon(value)]]
11
+ }
12
+
13
+ return value[ICON_SYMBOL] === 'pack'
14
+ ? Object.entries(value).map(([subKey, subValue]) => [`${key}:${subKey}`, subValue])
15
+ : [[key, value as Icon]]
16
+ })
17
+ )
18
+
19
+ return {
20
+ ...result,
21
+ [ICON_SYMBOL]: 'pack',
22
+ } as IconPack<IconPackKeys<TConfig>>
23
+ }
@@ -0,0 +1,17 @@
1
+ import { createIconBindings } from './create-icon-bindings.ts'
2
+ import { mergeIcons } from './merge-icons.ts'
3
+ import { normalizeIcon } from './normalize-icon.ts'
4
+ import { type Icon, ICON_SYMBOL, type IconSingleConfig } from './types.ts'
5
+
6
+ export function defineIconSingle(config: IconSingleConfig): Icon {
7
+ if (config.icon !== undefined && ICON_SYMBOL in config.icon) {
8
+ return mergeIcons(config.icon, config)
9
+ }
10
+
11
+ return {
12
+ [ICON_SYMBOL]: 'icon',
13
+ ...normalizeIcon(config.icon),
14
+ config,
15
+ bindings: createIconBindings(config),
16
+ }
17
+ }
@@ -0,0 +1,20 @@
1
+ import { createIconBindings } from './create-icon-bindings.ts'
2
+ import { defineIconSingle } from './define-icon-single.ts'
3
+ import { mergeIcons } from './merge-icons.ts'
4
+ import { type Icon, ICON_SYMBOL, type IconSingleConfig, type IconStack, type IconStackConfig } from './types.ts'
5
+
6
+ export function defineIconStack(
7
+ icons: IconStack | (IconSingleConfig | Icon)[],
8
+ config: IconStackConfig = {}
9
+ ): IconStack {
10
+ if (ICON_SYMBOL in icons) {
11
+ return mergeIcons(icons, config)
12
+ }
13
+
14
+ return {
15
+ [ICON_SYMBOL]: 'stack',
16
+ config,
17
+ icons: icons.map(icon => (ICON_SYMBOL in icon ? icon : defineIconSingle(icon))),
18
+ bindings: createIconBindings(config),
19
+ }
20
+ }
@@ -0,0 +1,40 @@
1
+ import { defineIconSingle } from './define-icon-single.ts'
2
+ import { defineIconStack } from './define-icon-stack.ts'
3
+ import { generateIconVariants } from './generate-icon-variants.ts'
4
+ import {
5
+ type DefineIconConfig,
6
+ type Icon,
7
+ ICON_SYMBOL,
8
+ type IconArgsToNames,
9
+ type IconPack,
10
+ type IconStackConfig,
11
+ type ArgsConfigToBuilderArgs,
12
+ } from './types.ts'
13
+
14
+ type ArgsValue = string[][]
15
+
16
+ export function defineIcon(configOrIcons: DefineIconConfig, stackConfig?: IconStackConfig): Icon
17
+
18
+ export function defineIcon<
19
+ const TArgsConfig extends ArgsValue,
20
+ TArgs extends ArgsConfigToBuilderArgs<TArgsConfig>,
21
+ TNames extends IconArgsToNames<TArgs>,
22
+ >(args: TArgsConfig, builder: (...args: TArgs) => DefineIconConfig, stackConfig?: IconStackConfig): IconPack<TNames>
23
+
24
+ export function defineIcon(
25
+ configOrIconsOrArgs: DefineIconConfig | ArgsValue,
26
+ stackConfigOrBuilder?: IconStackConfig | ((...args: string[]) => DefineIconConfig),
27
+ stackConfigOrNone?: IconStackConfig
28
+ ): Icon | IconPack<any> {
29
+ if (typeof stackConfigOrBuilder === 'function') {
30
+ return generateIconVariants(configOrIconsOrArgs as ArgsValue, stackConfigOrBuilder, stackConfigOrNone)
31
+ }
32
+
33
+ const configOrIcons = configOrIconsOrArgs as DefineIconConfig
34
+
35
+ if (Array.isArray(configOrIcons) || ICON_SYMBOL in configOrIcons) {
36
+ return defineIconStack(configOrIcons, stackConfigOrBuilder)
37
+ }
38
+
39
+ return defineIconSingle(configOrIcons)
40
+ }
@@ -0,0 +1,17 @@
1
+ import { defineIconPack } from './define-icon-pack.ts'
2
+ import { defineIcon } from './define-icon.ts'
3
+ import { type DefineIconConfig, type IconPack, type IconStackConfig } from './types.ts'
4
+
5
+ export function generateIconVariants<TArgs extends string[][]>(
6
+ argsConfigs: TArgs,
7
+ setup: (...args: any[]) => DefineIconConfig,
8
+ stackConfig: IconStackConfig | undefined
9
+ ): IconPack<any> {
10
+ const argsCombinations = argsConfigs.reduce((acc, arr) => acc.flatMap(prefix => arr.map(item => [...prefix, item])), [
11
+ [],
12
+ ] as string[][])
13
+
14
+ return defineIconPack(
15
+ Object.fromEntries(argsCombinations.map(args => [args.join(':'), defineIcon(setup(...args), stackConfig)]))
16
+ )
17
+ }
@@ -0,0 +1,8 @@
1
+ export * from './define-icon.ts'
2
+ export * from './define-icon-pack.ts'
3
+ export * from './types.ts'
4
+
5
+ export { default as DisplayIcon } from './DisplayIcon.vue'
6
+ export { default as DisplayIconAny } from './DisplayIconAny.vue'
7
+ export { default as DisplayIconSingle } from './DisplayIconSingle.vue'
8
+ export { default as DisplayIconStack } from './DisplayIconStack.vue'
@@ -0,0 +1,5 @@
1
+ import { type Icon, ICON_SYMBOL, type IconStack } from './types.ts'
2
+
3
+ export function isIconStack(icon: Icon): icon is IconStack {
4
+ return icon[ICON_SYMBOL] === 'stack'
5
+ }
@@ -0,0 +1,25 @@
1
+ import { defineIconSingle } from './define-icon-single.ts'
2
+ import { defineIconStack } from './define-icon-stack.ts'
3
+ import { mergeTransforms } from './merge-transforms.ts'
4
+ import {
5
+ type Icon,
6
+ ICON_SYMBOL,
7
+ type IconSingle,
8
+ type IconSingleConfig,
9
+ type IconStack,
10
+ type IconStackConfig,
11
+ } from './types.ts'
12
+
13
+ export function mergeIcons<TOriginal extends Icon>(
14
+ original: TOriginal,
15
+ newConfig: TOriginal extends IconSingle ? IconSingleConfig : IconSingleConfig | IconStackConfig
16
+ ): TOriginal extends IconSingle ? Icon : IconStack {
17
+ if (original[ICON_SYMBOL] === 'stack') {
18
+ return defineIconStack(original.icons, mergeTransforms(original.config, newConfig))
19
+ }
20
+
21
+ return defineIconSingle({
22
+ icon: original.config.icon,
23
+ ...mergeTransforms(original.config, newConfig),
24
+ }) as TOriginal extends IconSingle ? Icon : IconStack
25
+ }
@@ -0,0 +1,12 @@
1
+ import type { IconTransforms } from './types.ts'
2
+
3
+ export function mergeTransforms(source: IconTransforms, target: IconTransforms): IconTransforms {
4
+ return {
5
+ translate: target.translate ?? source.translate,
6
+ size: target.size ?? source.size,
7
+ rotate: target.rotate ?? source.rotate,
8
+ flip: target.flip ?? source.flip,
9
+ color: target.color ?? source.color,
10
+ borderColor: target.borderColor ?? source.borderColor,
11
+ }
12
+ }
@@ -0,0 +1,25 @@
1
+ import { toArray } from '@core/utils/to-array.utils.ts'
2
+ import type { NormalizedIcon } from './types.ts'
3
+ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
4
+ import type { SimpleIcon } from 'simple-icons'
5
+
6
+ export function normalizeIcon(icon: IconDefinition | SimpleIcon | undefined): NormalizedIcon {
7
+ if (icon === undefined) {
8
+ return {
9
+ viewBox: '',
10
+ paths: [],
11
+ }
12
+ }
13
+
14
+ if ('icon' in icon) {
15
+ return {
16
+ viewBox: `0 0 ${icon.icon[0]} ${icon.icon[1]}`,
17
+ paths: toArray(icon.icon[4]),
18
+ }
19
+ }
20
+
21
+ return {
22
+ viewBox: '0 0 24 24',
23
+ paths: toArray(icon.path),
24
+ }
25
+ }
@@ -0,0 +1,7 @@
1
+ export function toTuple<T>(value: undefined | T | [T, T], defaultValue: [T, T]): [T, T] {
2
+ if (value === undefined) {
3
+ return defaultValue
4
+ }
5
+
6
+ return Array.isArray(value) ? value : [value, value]
7
+ }
@@ -0,0 +1,72 @@
1
+ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
2
+ import type { SimpleIcon } from 'simple-icons'
3
+
4
+ export const ICON_SYMBOL = Symbol('Icon')
5
+
6
+ export type IconTransforms = {
7
+ borderColor?: string
8
+ translate?: number | [number, number]
9
+ size?: number | [number, number]
10
+ rotate?: number
11
+ flip?: 'horizontal' | 'vertical' | 'both'
12
+ color?: string
13
+ }
14
+
15
+ export type IconSingleConfig = {
16
+ icon?: IconDefinition | SimpleIcon | IconSingle | IconStack
17
+ } & IconTransforms
18
+
19
+ export type IconStackConfig = IconTransforms
20
+
21
+ export type IconBindings = {
22
+ style?: {
23
+ color?: string
24
+ rotate?: string
25
+ translate?: string
26
+ scale?: string
27
+ }
28
+ }
29
+
30
+ export type IconSingle = NormalizedIcon & {
31
+ [ICON_SYMBOL]: 'icon'
32
+ config: IconSingleConfig
33
+ bindings: IconBindings
34
+ }
35
+
36
+ export type IconStack = {
37
+ [ICON_SYMBOL]: 'stack'
38
+ icons: Icon[]
39
+ config: IconStackConfig
40
+ bindings: IconBindings
41
+ }
42
+
43
+ export type Icon = IconSingle | IconStack
44
+
45
+ export type IconPackKeys<TConfig extends object> = keyof {
46
+ [K in keyof TConfig as TConfig[K] extends IconPack<infer TKey> ? `${K & string}:${TKey}` : K]: true
47
+ }
48
+
49
+ export type IconPackConfig = Record<string, Icon | IconPack<string> | DefineIconConfig>
50
+
51
+ export type IconPack<TKeys extends string> = {
52
+ [ICON_SYMBOL]: 'pack'
53
+ } & {
54
+ [K in TKeys]: Icon
55
+ }
56
+
57
+ export type ArgsConfigToBuilderArgs<T extends string[][]> = {
58
+ [K in keyof T]: T[K][number]
59
+ }
60
+
61
+ export type IconArgsToNames<TArgs extends any[]> = TArgs extends [infer TFirst, ...infer TRest]
62
+ ? TRest extends []
63
+ ? TFirst
64
+ : `${TFirst & string}:${IconArgsToNames<TRest>}`
65
+ : never
66
+
67
+ export type DefineIconConfig = IconSingleConfig | IconStack | (IconSingleConfig | IconStack)[]
68
+
69
+ export type NormalizedIcon = {
70
+ viewBox: string
71
+ paths: string[]
72
+ }
@@ -42,7 +42,7 @@ const forceArg = defineJobArg<boolean>({
42
42
  ```typescript
43
43
  const myJob = defineJob('processItems', [userIdArg, itemsArg, optionsArg], () => ({
44
44
  validate(isRunning, userId, items) {
45
- // You can use custom running check additionnal to internal one
45
+ // You can use custom running check additional to internal one
46
46
  if (isRunning || isProcessingItems(userId, items)) {
47
47
  throw new JobRunningError('Items are being processed')
48
48
  }
@@ -57,7 +57,7 @@ const myJob = defineJob('processItems', [userIdArg, itemsArg, optionsArg], () =>
57
57
  },
58
58
  run(userId, items, force) {
59
59
  // Job implementation
60
- return procesItems(userId, items, force)
60
+ return processItems(userId, items, force)
61
61
  },
62
62
  }))
63
63
  ```