@flux-ui/components 3.0.0-next.33 → 3.0.0-next.35

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 (44) hide show
  1. package/dist/component/FluxBoxedIcon.vue.d.ts +1 -5
  2. package/dist/component/FluxCommandPalette.vue.d.ts +52 -0
  3. package/dist/component/FluxCommandPaletteGroup.vue.d.ts +8 -0
  4. package/dist/component/FluxCommandPaletteItem.vue.d.ts +18 -0
  5. package/dist/component/FluxFormDateRangeInput.vue.d.ts +1 -5
  6. package/dist/component/FluxTag.vue.d.ts +1 -0
  7. package/dist/component/index.d.ts +4 -1
  8. package/dist/composable/private/useCommandPalette.d.ts +38 -0
  9. package/dist/index.css +320 -0
  10. package/dist/index.js +686 -120
  11. package/dist/index.js.map +1 -1
  12. package/package.json +10 -10
  13. package/src/component/FluxBoxedIcon.vue +0 -4
  14. package/src/component/FluxCalendar.vue +2 -1
  15. package/src/component/FluxChip.vue +1 -1
  16. package/src/component/FluxColorPicker.vue +1 -1
  17. package/src/component/FluxColorSelect.vue +3 -3
  18. package/src/component/FluxCommandPalette.vue +290 -0
  19. package/src/component/FluxCommandPaletteGroup.vue +23 -0
  20. package/src/component/FluxCommandPaletteItem.vue +60 -0
  21. package/src/component/FluxDatePicker.vue +0 -1
  22. package/src/component/FluxFader.vue +2 -2
  23. package/src/component/FluxFocalPointEditor.vue +3 -3
  24. package/src/component/FluxFormDateRangeInput.vue +0 -5
  25. package/src/component/FluxFormDateTimeInput.vue +2 -2
  26. package/src/component/FluxFormSelect.vue +2 -2
  27. package/src/component/FluxFormTextArea.vue +1 -0
  28. package/src/component/FluxQuantitySelector.vue +3 -6
  29. package/src/component/FluxSnackbar.vue +5 -3
  30. package/src/component/FluxTableActions.vue +3 -3
  31. package/src/component/FluxTag.vue +3 -1
  32. package/src/component/index.ts +4 -1
  33. package/src/component/primitive/CoordinatePicker.vue +10 -11
  34. package/src/component/primitive/FilterItem.vue +11 -2
  35. package/src/component/primitive/SelectBase.vue +2 -2
  36. package/src/component/primitive/SliderBase.vue +10 -13
  37. package/src/composable/private/useCommandPalette.ts +405 -0
  38. package/src/composable/private/useFormSelect.ts +2 -2
  39. package/src/css/component/Badge.module.scss +7 -0
  40. package/src/css/component/CommandPalette.module.scss +332 -0
  41. package/src/css/reset.scss +0 -107
  42. package/src/css/variables.scss +0 -26
  43. /package/dist/component/{FluxActions.vue.d.ts → FluxActionStack.vue.d.ts} +0 -0
  44. /package/src/component/{FluxActions.vue → FluxActionStack.vue} +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flux-ui/components",
3
3
  "description": "A set of opiniated UI components.",
4
- "version": "3.0.0-next.33",
4
+ "version": "3.0.0-next.35",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/basmilius",
@@ -51,10 +51,10 @@
51
51
  "typings": "./dist/index.d.ts",
52
52
  "sideEffects": false,
53
53
  "dependencies": {
54
- "@basmilius/common": "^3.11.1",
55
- "@basmilius/utils": "^3.11.1",
56
- "@flux-ui/internals": "3.0.0-next.33",
57
- "@flux-ui/types": "3.0.0-next.33",
54
+ "@basmilius/common": "^3.12.1",
55
+ "@basmilius/utils": "^3.12.1",
56
+ "@flux-ui/internals": "3.0.0-next.35",
57
+ "@flux-ui/types": "3.0.0-next.35",
58
58
  "@fortawesome/fontawesome-common-types": "^7.2.0",
59
59
  "clsx": "^2.1.1",
60
60
  "imask": "^7.6.1",
@@ -63,16 +63,16 @@
63
63
  "vue": "^3.6.0-beta.7"
64
64
  },
65
65
  "devDependencies": {
66
- "@basmilius/vite-preset": "^3.11.1",
66
+ "@basmilius/vite-preset": "^3.12.1",
67
67
  "@types/lodash-es": "^4.17.12",
68
68
  "@types/luxon": "^3.7.1",
69
- "@types/node": "^25.3.5",
70
- "@vitejs/plugin-vue": "^6.0.4",
69
+ "@types/node": "^25.5.0",
70
+ "@vitejs/plugin-vue": "^6.0.5",
71
71
  "@vue/tsconfig": "^0.9.0",
72
72
  "pinia": "^3.0.4",
73
- "sass-embedded": "^1.97.3",
73
+ "sass-embedded": "^1.98.0",
74
74
  "typescript": "^5.9.3",
75
- "vite": "^8.0.0-beta.18",
75
+ "vite": "^8.0.0",
76
76
  "vue-tsc": "^3.2.5"
77
77
  }
78
78
  }
@@ -24,10 +24,6 @@
24
24
  import FluxIcon from './FluxIcon.vue';
25
25
  import $style from '$flux/css/component/Icon.module.scss';
26
26
 
27
- defineEmits<{
28
- click: [MouseEvent];
29
- }>();
30
-
31
27
  defineProps<{
32
28
  readonly color?: FluxColor;
33
29
  readonly name: FluxIconName;
@@ -103,6 +103,7 @@
103
103
  <div :class="$style.calendarEvents">
104
104
  <VNodeRenderer
105
105
  v-for="event of getEventsForDate(date)"
106
+ :key="event.index"
106
107
  :class="clsx(
107
108
  event.type === 'single' && $style.isSingle,
108
109
  event.type === 'start' && $style.isStart,
@@ -229,7 +230,7 @@
229
230
  vnode
230
231
  };
231
232
  })
232
- .filter(e => e !== null);
233
+ .filter((e): e is Event => e !== null);
233
234
  }
234
235
 
235
236
  function setToday(): void {
@@ -25,7 +25,7 @@
25
25
  v-if="iconTrailing"
26
26
  :name="iconTrailing"
27
27
  :size="16"/>
28
- </component>
28
+ </Component>
29
29
  </template>
30
30
 
31
31
  <script
@@ -205,7 +205,7 @@
205
205
  return computed({
206
206
  get: () => fromHSV?.(...unref(hsv))[index] ?? unref(hsv)[index],
207
207
  set: value => {
208
- const values = fromHSV?.(...unref(hsv)) ?? unref(hsv);
208
+ const values: [number, number, number] = [...(fromHSV?.(...unref(hsv)) ?? unref(hsv))];
209
209
  values[index] = value;
210
210
  hsv.value = toHSV?.(...values) ?? values;
211
211
  }
@@ -31,11 +31,11 @@
31
31
 
32
32
  <FluxPaneBody :class="$style.colorSelectButtons">
33
33
  <FluxSecondaryButton
34
- :label="t('flux.cancel')"
34
+ :label="translate('flux.cancel')"
35
35
  @click="close()"/>
36
36
 
37
37
  <FluxPrimaryButton
38
- :label="t('flux.ok')"
38
+ :label="translate('flux.ok')"
39
39
  @click="select(customColor, close)"/>
40
40
  </FluxPaneBody>
41
41
  </template>
@@ -68,7 +68,7 @@
68
68
  readonly isCustomAllowed?: boolean;
69
69
  }>();
70
70
 
71
- const t = useTranslate();
71
+ const translate = useTranslate();
72
72
 
73
73
  const customColor = ref('#000000');
74
74
 
@@ -0,0 +1,290 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <div
4
+ v-if="isOpen || isClosing"
5
+ :class="[$style.commandPaletteBackdrop, isClosing && $style.isClosing]"
6
+ @click="close"/>
7
+
8
+ <div
9
+ v-if="isOpen || isClosing"
10
+ ref="dialogRef"
11
+ :class="[$style.commandPaletteDialog, isClosing && $style.isClosing]"
12
+ @click.self="close"
13
+ @keydown="onKeyDown">
14
+ <div
15
+ v-height-transition
16
+ :class="$style.commandPalette"
17
+ @mousedown.prevent>
18
+ <div :class="$style.commandPaletteSearch">
19
+ <FluxIcon
20
+ :class="$style.commandPaletteSearchIcon"
21
+ name="magnifying-glass"/>
22
+
23
+ <template v-if="activeTabSource">
24
+ <button
25
+ :class="$style.commandPaletteBreadcrumb"
26
+ tabindex="-1"
27
+ type="button"
28
+ @click="clearSubActionTarget"
29
+ @mousedown.prevent>
30
+ {{ activeTabSource.label }}
31
+ </button>
32
+
33
+ <span :class="$style.commandPaletteBreadcrumbSeparator">/</span>
34
+ </template>
35
+
36
+ <template v-if="subActionTarget">
37
+ <button
38
+ :class="$style.commandPaletteBreadcrumb"
39
+ tabindex="-1"
40
+ type="button"
41
+ @mousedown.prevent>
42
+ {{ subActionTarget.label }}
43
+ </button>
44
+
45
+ <span :class="$style.commandPaletteBreadcrumbSeparator">/</span>
46
+ </template>
47
+
48
+ <input
49
+ ref="inputRef"
50
+ :class="$style.commandPaletteSearchInput"
51
+ :placeholder="placeholder ?? 'Search...'"
52
+ :value="search"
53
+ type="text"
54
+ @input="setSearch(($event.target as HTMLInputElement).value)"/>
55
+
56
+ <FluxTag
57
+ is-keyboard-shortcut
58
+ label="Esc"/>
59
+ </div>
60
+
61
+ <div
62
+ v-if="tabs.length > 0 && !subActionTarget"
63
+ :class="$style.commandPaletteTabs">
64
+ <button
65
+ :class="activeTab === null ? $style.commandPaletteTabActive : $style.commandPaletteTab"
66
+ tabindex="-1"
67
+ type="button"
68
+ @click="setActiveTab(null)"
69
+ @mousedown.prevent>
70
+ All
71
+ </button>
72
+
73
+ <button
74
+ v-for="tab of tabs"
75
+ :key="tab.key"
76
+ :class="activeTab === tab.key ? $style.commandPaletteTabActive : $style.commandPaletteTab"
77
+ tabindex="-1"
78
+ type="button"
79
+ @click="setActiveTab(tab.key)"
80
+ @mousedown.prevent>
81
+ <FluxIcon
82
+ v-if="tab.icon"
83
+ :class="$style.commandPaletteTabIcon"
84
+ :name="tab.icon"/>
85
+
86
+ {{ tab.label }}
87
+ </button>
88
+ </div>
89
+
90
+ <FluxWindowTransition :is-back="isTransitioningBack">
91
+ <div
92
+ :key="activeTab"
93
+ :class="$style.commandPaletteResults">
94
+ <template v-if="subActionTarget">
95
+ <FluxCommandPaletteItem
96
+ v-for="(action, index) of subActions"
97
+ :key="index"
98
+ ref="itemRefs"
99
+ :icon="action.icon"
100
+ :is-highlighted="highlightedIndex === index"
101
+ :label="action.label"
102
+ @activate="activateSubAction(action)"
103
+ @highlight="highlightedIndex = index"/>
104
+ </template>
105
+
106
+ <template v-else-if="groupedItems.length > 0">
107
+ <template
108
+ v-for="group of groupedItems"
109
+ :key="group.sourceKey">
110
+ <FluxCommandPaletteGroup
111
+ v-if="group.sourceLabel"
112
+ :label="group.sourceLabel"/>
113
+
114
+ <FluxCommandPaletteItem
115
+ v-for="result of group.items"
116
+ :key="result.item.id"
117
+ ref="itemRefs"
118
+ :command="result.item.command"
119
+ :has-sub-actions="!!result.item.subActions?.length"
120
+ :icon="result.item.icon"
121
+ :is-highlighted="highlightedIndex === result.globalIndex"
122
+ :label="result.item.label"
123
+ :sub-label="result.item.subLabel"
124
+ @activate="activateItem(result.item)"
125
+ @highlight="highlightedIndex = result.globalIndex"/>
126
+ </template>
127
+ </template>
128
+
129
+ <div
130
+ v-else-if="isLoading"
131
+ :class="$style.commandPaletteLoading">
132
+ <FluxSpinner :size="22"/>
133
+ </div>
134
+
135
+ <div
136
+ v-else
137
+ :class="$style.commandPaletteEmpty">
138
+ No results found.
139
+ </div>
140
+ </div>
141
+ </FluxWindowTransition>
142
+ </div>
143
+ </div>
144
+ </Teleport>
145
+ </template>
146
+
147
+ <script
148
+ lang="ts"
149
+ setup>
150
+ import type { FluxCommandSource, FluxCommandSourceItem } from '@flux-ui/types';
151
+ import { isSSR, vHeightTransition } from '@flux-ui/internals';
152
+ import { onMounted, onUnmounted, ref, toRef, unref, useTemplateRef } from 'vue';
153
+ import { useCommandPalette } from '$flux/composable/private/useCommandPalette';
154
+ import FluxCommandPaletteGroup from './FluxCommandPaletteGroup.vue';
155
+ import FluxCommandPaletteItem from './FluxCommandPaletteItem.vue';
156
+ import FluxIcon from './FluxIcon.vue';
157
+ import FluxSpinner from './FluxSpinner.vue';
158
+ import FluxTag from './FluxTag.vue';
159
+ import FluxWindowTransition from '$flux/transition/FluxWindowTransition.vue';
160
+ import $style from '$flux/css/component/CommandPalette.module.scss';
161
+
162
+ const props = defineProps<{
163
+ readonly hasKeyboardShortcut?: boolean;
164
+ readonly placeholder?: string;
165
+ readonly sources: FluxCommandSource[];
166
+ }>();
167
+
168
+ const emit = defineEmits<{
169
+ select: [item: FluxCommandSourceItem];
170
+ }>();
171
+
172
+ const dialogRef = useTemplateRef<HTMLDivElement>('dialogRef');
173
+ const inputRef = useTemplateRef<HTMLInputElement>('inputRef');
174
+ const itemRefs = ref<InstanceType<typeof FluxCommandPaletteItem>[]>();
175
+
176
+ const isOpen = ref(false);
177
+ const isClosing = ref(false);
178
+
179
+ const {
180
+ search,
181
+ activeTab,
182
+ activeTabSource,
183
+ highlightedIndex,
184
+ isLoading,
185
+ isTransitioningBack,
186
+ subActionTarget,
187
+ groupedItems,
188
+ subActions,
189
+ tabs,
190
+ setSearch,
191
+ setActiveTab,
192
+ enterSubActions,
193
+ onKeyNavigate,
194
+ reset
195
+ } = useCommandPalette({
196
+ sources: toRef(() => props.sources),
197
+ itemRefs
198
+ });
199
+
200
+ function open(): void {
201
+ if (unref(isOpen)) {
202
+ return;
203
+ }
204
+
205
+ isOpen.value = true;
206
+
207
+ requestAnimationFrame(() => unref(inputRef)?.focus());
208
+ }
209
+
210
+ function close(): void {
211
+ if (!unref(isOpen)) {
212
+ return;
213
+ }
214
+
215
+ const dialog = unref(dialogRef);
216
+
217
+ if (!dialog) {
218
+ return;
219
+ }
220
+
221
+ isClosing.value = true;
222
+
223
+ const finishClose = () => {
224
+ isClosing.value = false;
225
+ isOpen.value = false;
226
+ reset();
227
+ };
228
+
229
+ const fallbackTimeout = setTimeout(finishClose, 250);
230
+
231
+ dialog.addEventListener('animationend', () => {
232
+ clearTimeout(fallbackTimeout);
233
+ finishClose();
234
+ }, {once: true});
235
+ }
236
+
237
+ function clearSubActionTarget(): void {
238
+ subActionTarget.value = null;
239
+ highlightedIndex.value = -1;
240
+ }
241
+
242
+ function activateItem(item: FluxCommandSourceItem): void {
243
+ if (item.subActions?.length) {
244
+ enterSubActions(item);
245
+ } else {
246
+ item.onActivate();
247
+ emit('select', item);
248
+ close();
249
+ }
250
+ }
251
+
252
+ function activateSubAction(action: { readonly onActivate: () => void; }): void {
253
+ action.onActivate();
254
+ close();
255
+ }
256
+
257
+ function onKeyDown(evt: KeyboardEvent): void {
258
+ onKeyNavigate(evt, close, (item) => {
259
+ emit('select', item);
260
+ close();
261
+ });
262
+ }
263
+
264
+ function onGlobalKeyDown(evt: KeyboardEvent): void {
265
+ if (evt.key === 'k' && (evt.metaKey || evt.ctrlKey)) {
266
+ evt.preventDefault();
267
+
268
+ if (unref(isOpen)) {
269
+ close();
270
+ } else {
271
+ open();
272
+ }
273
+ }
274
+ }
275
+
276
+ if (!isSSR && props.hasKeyboardShortcut) {
277
+ onMounted(() => {
278
+ window.addEventListener('keydown', onGlobalKeyDown);
279
+ });
280
+
281
+ onUnmounted(() => {
282
+ window.removeEventListener('keydown', onGlobalKeyDown);
283
+ });
284
+ }
285
+
286
+ defineExpose({
287
+ close,
288
+ open
289
+ });
290
+ </script>
@@ -0,0 +1,23 @@
1
+ <template>
2
+ <div :class="$style.commandPaletteGroup">
3
+ <FluxIcon
4
+ v-if="icon"
5
+ :class="$style.commandPaletteGroupIcon"
6
+ :name="icon"/>
7
+
8
+ <span>{{ label }}</span>
9
+ </div>
10
+ </template>
11
+
12
+ <script
13
+ lang="ts"
14
+ setup>
15
+ import type { FluxIconName } from '@flux-ui/types';
16
+ import FluxIcon from './FluxIcon.vue';
17
+ import $style from '$flux/css/component/CommandPalette.module.scss';
18
+
19
+ defineProps<{
20
+ readonly icon?: FluxIconName;
21
+ readonly label: string;
22
+ }>();
23
+ </script>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <button
3
+ :class="isHighlighted ? $style.commandPaletteItemHighlighted : $style.commandPaletteItem"
4
+ tabindex="-1"
5
+ type="button"
6
+ @click="$emit('activate')"
7
+ @mousedown.prevent
8
+ @mouseenter="$emit('highlight')">
9
+ <div
10
+ v-if="icon"
11
+ :class="$style.commandPaletteItemIcon">
12
+ <FluxIcon :name="icon"/>
13
+ </div>
14
+
15
+ <div :class="$style.commandPaletteItemContent">
16
+ <div :class="$style.commandPaletteItemLabel">
17
+ {{ label }}
18
+ </div>
19
+
20
+ <div
21
+ v-if="subLabel"
22
+ :class="$style.commandPaletteItemSubLabel">
23
+ {{ subLabel }}
24
+ </div>
25
+ </div>
26
+
27
+ <FluxTag
28
+ v-if="command"
29
+ is-keyboard-shortcut
30
+ :label="command"/>
31
+
32
+ <FluxIcon
33
+ v-if="hasSubActions"
34
+ :class="$style.commandPaletteItemSubActionIndicator"
35
+ name="chevron-right"/>
36
+ </button>
37
+ </template>
38
+
39
+ <script
40
+ lang="ts"
41
+ setup>
42
+ import type { FluxIconName } from '@flux-ui/types';
43
+ import FluxIcon from './FluxIcon.vue';
44
+ import FluxTag from './FluxTag.vue';
45
+ import $style from '$flux/css/component/CommandPalette.module.scss';
46
+
47
+ defineEmits<{
48
+ activate: [];
49
+ highlight: [];
50
+ }>();
51
+
52
+ defineProps<{
53
+ readonly command?: string;
54
+ readonly hasSubActions?: boolean;
55
+ readonly icon?: FluxIconName;
56
+ readonly isHighlighted?: boolean;
57
+ readonly label: string;
58
+ readonly subLabel?: string;
59
+ }>();
60
+ </script>
@@ -136,7 +136,6 @@
136
136
  import $style from '$flux/css/component/DatePicker.module.scss';
137
137
 
138
138
  const modelValue = defineModel<DateTime | DateTime[] | null>({
139
- default: null,
140
139
  required: true
141
140
  });
142
141
 
@@ -48,13 +48,13 @@
48
48
  }
49
49
 
50
50
  function previous(): void {
51
- current.value = unref(current) - 1 <= -1 ? unref(count) - 1 : unref(current) + 1;
51
+ current.value = unref(current) - 1 <= -1 ? unref(count) - 1 : unref(current) - 1;
52
52
  }
53
53
 
54
54
  watch(current, current => {
55
55
  const fader = unrefTemplateElement(faderRef);
56
56
 
57
- if (!fader || fader.children.length === 0) {
57
+ if (!fader || fader.children.length === 0 || current < 0) {
58
58
  return;
59
59
  }
60
60
 
@@ -112,11 +112,11 @@
112
112
  }
113
113
 
114
114
  const {top, left, width, height} = image.getBoundingClientRect();
115
- const {pageX, pageY} = evt;
115
+ const {clientX, clientY} = evt;
116
116
 
117
117
  dragging.value = [
118
- Math.max(0, Math.min(1, (pageX - left) / width)) * 100,
119
- Math.max(0, Math.min(1, (pageY - top) / height)) * 100
118
+ Math.max(0, Math.min(1, (clientX - left) / width)) * 100,
119
+ Math.max(0, Math.min(1, (clientY - top) / height)) * 100
120
120
  ];
121
121
  }
122
122
 
@@ -31,7 +31,6 @@
31
31
  <script
32
32
  lang="ts"
33
33
  setup>
34
- import type { FluxAutoCompleteType } from '@flux-ui/types';
35
34
  import { clsx } from 'clsx';
36
35
  import type { DateTime } from 'luxon';
37
36
  import { computed, ref, toRef, unref, useTemplateRef, watch } from 'vue';
@@ -51,13 +50,9 @@
51
50
  disabled: componentDisabled,
52
51
  rangeMode = 'range'
53
52
  } = defineProps<{
54
- readonly autoComplete?: FluxAutoCompleteType;
55
- readonly autoFocus?: boolean;
56
53
  readonly disabled?: boolean;
57
- readonly isReadonly?: boolean;
58
54
  readonly max?: DateTime;
59
55
  readonly min?: DateTime;
60
- readonly placeholder?: string;
61
56
  readonly rangeMode?: 'range' | 'week' | 'month';
62
57
  }>();
63
58
 
@@ -100,8 +100,8 @@
100
100
  const value: DateTime = (localValue.value ?? DateTime.now());
101
101
  localValue.value = value.set({
102
102
  hour: dateTime.hour,
103
- minute: unref(isHourOnly) ? 0 : dateTime.minute,
104
- second: unref(isHourOnly) ? 0 : dateTime.second
103
+ minute: isHourOnly ? 0 : dateTime.minute,
104
+ second: isHourOnly ? 0 : dateTime.second
105
105
  });
106
106
  }
107
107
 
@@ -44,13 +44,13 @@
44
44
  const {groups, selected, values} = useFormSelect(modelValue, isMultiple, toRef(() => options), modelSearch);
45
45
 
46
46
  function onDeselect(id: string | number | null): void {
47
- if (unref(isMultiple)) {
47
+ if (isMultiple) {
48
48
  modelValue.value = unref(values).filter(v => v !== id);
49
49
  }
50
50
  }
51
51
 
52
52
  function onSelect(id: string | number | null): void {
53
- if (unref(isMultiple)) {
53
+ if (isMultiple) {
54
54
  modelValue.value = [...unref(values), id];
55
55
  } else {
56
56
  modelValue.value = id;
@@ -7,6 +7,7 @@
7
7
  :autocomplete="autoComplete"
8
8
  :autofocus="autoFocus"
9
9
  :disabled="disabled"
10
+ :readonly="isReadonly"
10
11
  :maxlength="maxLength"
11
12
  :placeholder="placeholder"
12
13
  :style="{
@@ -94,13 +94,10 @@
94
94
  }
95
95
 
96
96
  watchEffect(() => {
97
- if (unref(modelValue) > max) {
98
- increment();
99
- return;
100
- }
97
+ const value = unref(modelValue);
101
98
 
102
- if (unref(modelValue) < min) {
103
- decrement();
99
+ if (value > max || value < min) {
100
+ modelValue.value = Math.min(max, Math.max(min, value));
104
101
  return;
105
102
  }
106
103
 
@@ -145,9 +145,11 @@
145
145
  return;
146
146
  }
147
147
 
148
- let spec: Omit<FluxSnackbarObject, 'id'> = instance.props;
149
- spec.onAction = onAction;
150
- spec.onClose = onClose;
148
+ const spec: Omit<FluxSnackbarObject, 'id'> = {
149
+ ...instance.props,
150
+ onAction,
151
+ onClose
152
+ };
151
153
 
152
154
  id.value = addSnackbar(spec);
153
155
  }, {immediate: true});
@@ -1,13 +1,13 @@
1
1
  <template>
2
- <FluxActions :class="$style.tableActions">
2
+ <FluxActionStack :class="$style.tableActions">
3
3
  <slot/>
4
- </FluxActions>
4
+ </FluxActionStack>
5
5
  </template>
6
6
 
7
7
  <script
8
8
  lang="ts"
9
9
  setup>
10
- import FluxActions from './FluxActions.vue';
10
+ import FluxActionStack from './FluxActionStack.vue';
11
11
  import $style from '$flux/css/component/Table.module.scss';
12
12
 
13
13
  defineSlots<{
@@ -6,7 +6,8 @@
6
6
  color === 'danger' && $style.tagDanger,
7
7
  color === 'info' && $style.tagInfo,
8
8
  color === 'success' && $style.tagSuccess,
9
- color === 'warning' && $style.tagWarning
9
+ color === 'warning' && $style.tagWarning,
10
+ isKeyboardShortcut && $style.tagKeyboardShortcut
10
11
  )"
11
12
  :component-type="type"
12
13
  :tabindex="tabindex"
@@ -69,6 +70,7 @@
69
70
  readonly icon?: FluxIconName;
70
71
  readonly isClickable?: boolean;
71
72
  readonly isDeletable?: boolean;
73
+ readonly isKeyboardShortcut?: boolean;
72
74
  readonly isLoading?: boolean;
73
75
  readonly label: string;
74
76
  readonly type?: FluxPressableType;