@flux-ui/components 3.0.0-next.32 → 3.0.0-next.34

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/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.32",
4
+ "version": "3.0.0-next.34",
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.0",
55
- "@basmilius/utils": "^3.11.0",
56
- "@flux-ui/internals": "3.0.0-next.32",
57
- "@flux-ui/types": "3.0.0-next.32",
54
+ "@basmilius/common": "^3.12.1",
55
+ "@basmilius/utils": "^3.12.1",
56
+ "@flux-ui/internals": "3.0.0-next.34",
57
+ "@flux-ui/types": "3.0.0-next.34",
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.0",
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.3",
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.16",
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
 
@@ -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});
@@ -19,7 +19,7 @@
19
19
  setup>
20
20
  import { roundStep } from '@basmilius/utils';
21
21
  import { unrefTemplateElement } from '@flux-ui/internals';
22
- import { computed, onMounted, onUnmounted, ref, toRef, unref, useTemplateRef, watch } from 'vue';
22
+ import { computed, onUnmounted, ref, toRef, unref, useTemplateRef, watch } from 'vue';
23
23
  import { useDisabled } from '$flux/composable';
24
24
  import CoordinatePickerThumb from './CoordinatePickerThumb.vue';
25
25
  import $style from '$flux/css/component/primitive/CoordinatePicker.module.scss';
@@ -58,16 +58,6 @@
58
58
  (unref(modelValue)[1] - unref(min)[1]) / (unref(max)[1] - unref(min)[1])
59
59
  ]);
60
60
 
61
- onMounted(() => {
62
- document.addEventListener('pointermove', onPointerMove);
63
- document.addEventListener('pointerup', onPointerUp, {passive: true});
64
- });
65
-
66
- onUnmounted(() => {
67
- document.removeEventListener('pointermove', onPointerMove);
68
- document.removeEventListener('pointerup', onPointerUp);
69
- });
70
-
71
61
  function onDecrement(x: boolean, y: boolean): void {
72
62
  if (unref(disabled)) {
73
63
  return;
@@ -116,6 +106,8 @@
116
106
  }
117
107
 
118
108
  isDragging.value = true;
109
+ document.addEventListener('pointermove', onPointerMove);
110
+ document.addEventListener('pointerup', onPointerUp, {passive: true});
119
111
  requestAnimationFrame(() => onPointerMove(evt));
120
112
  }
121
113
 
@@ -149,7 +141,14 @@
149
141
 
150
142
  function onPointerUp(): void {
151
143
  isDragging.value = false;
144
+ document.removeEventListener('pointermove', onPointerMove);
145
+ document.removeEventListener('pointerup', onPointerUp);
152
146
  }
153
147
 
154
148
  watch(isDragging, isDragging => emit('dragging', isDragging));
149
+
150
+ onUnmounted(() => {
151
+ document.removeEventListener('pointermove', onPointerMove);
152
+ document.removeEventListener('pointerup', onPointerUp);
153
+ });
155
154
  </script>
@@ -38,7 +38,16 @@
38
38
  emit('click', evt);
39
39
  }
40
40
 
41
- watch(() => item, async () => {
42
- valueLabel.value = await unref(getValueLabel)(value) ?? undefined;
41
+ watch([() => item, () => value], async ([, nextValue], _prev, onCleanup) => {
42
+ let cancelled = false;
43
+ onCleanup(() => {
44
+ cancelled = true;
45
+ });
46
+
47
+ const nextLabel = await unref(getValueLabel)(nextValue);
48
+
49
+ if (!cancelled) {
50
+ valueLabel.value = nextLabel ?? undefined;
51
+ }
43
52
  }, {deep: true, immediate: true});
44
53
  </script>
@@ -286,8 +286,8 @@
286
286
  return;
287
287
 
288
288
  default:
289
- if (evt.key.match(/[a-z]/)) {
290
- highlightedIndex.value = unref(rawOptions).findIndex(o => o.label.toLowerCase().startsWith(evt.key));
289
+ if (evt.key.length === 1) {
290
+ highlightedIndex.value = unref(rawOptions).findIndex(o => o.label.toLowerCase().startsWith(evt.key.toLowerCase()));
291
291
  } else {
292
292
  highlightedIndex.value = -1;
293
293
  }
@@ -22,7 +22,7 @@
22
22
  setup>
23
23
  import { unrefTemplateElement } from '@flux-ui/internals';
24
24
  import { clsx } from 'clsx';
25
- import { onMounted, onUnmounted, toRef, unref, useTemplateRef, watch } from 'vue';
25
+ import { onUnmounted, toRef, unref, useTemplateRef } from 'vue';
26
26
  import { useDisabled } from '$flux/composable';
27
27
  import FluxTicks from '$flux/component/FluxTicks.vue';
28
28
  import $style from '$flux/css/component/primitive/Slider.module.scss';
@@ -47,22 +47,14 @@
47
47
  const disabled = useDisabled(toRef(() => componentDisabled));
48
48
  const rootRef = useTemplateRef('root');
49
49
 
50
- onMounted(() => {
51
- document.addEventListener('pointermove', onPointerMove);
52
- document.addEventListener('pointerup', onPointerUp, {passive: true});
53
- });
54
-
55
- onUnmounted(() => {
56
- document.removeEventListener('pointermove', onPointerMove);
57
- document.removeEventListener('pointerup', onPointerUp);
58
- });
59
-
60
50
  function onPointerDown(evt: PointerEvent): void {
61
51
  if (unref(disabled)) {
62
52
  return;
63
53
  }
64
54
 
65
55
  emit('dragging', true);
56
+ document.addEventListener('pointermove', onPointerMove);
57
+ document.addEventListener('pointerup', onPointerUp, {passive: true});
66
58
  requestAnimationFrame(() => onPointerMove(evt));
67
59
  }
68
60
 
@@ -77,13 +69,18 @@
77
69
  left += 6; // margin.
78
70
  width -= 12; // margin times two.
79
71
 
80
- emit('update', Math.max(0, Math.min(1, (evt.pageX - left) / width)));
72
+ emit('update', Math.max(0, Math.min(1, (evt.clientX - left) / width)));
81
73
  evt.preventDefault();
82
74
  }
83
75
 
84
76
  function onPointerUp(): void {
85
77
  emit('dragging', false);
78
+ document.removeEventListener('pointermove', onPointerMove);
79
+ document.removeEventListener('pointerup', onPointerUp);
86
80
  }
87
81
 
88
- watch(() => isDragging, () => emit('dragging', isDragging));
82
+ onUnmounted(() => {
83
+ document.removeEventListener('pointermove', onPointerMove);
84
+ document.removeEventListener('pointerup', onPointerUp);
85
+ });
89
86
  </script>
@@ -14,8 +14,8 @@ export default function (modelValue: Ref<FluxFormSelectValue>, isMultiple: boole
14
14
  const search = unref(searchQuery)?.trim().toLowerCase();
15
15
 
16
16
  const available = unref(options)
17
- .filter(o => !('value' in o) || (!search || o.label.toLowerCase().includes(search)))
18
- .filter(o => !('value' in o) || !isMultiple || !unref(selected).find(s => s.value === o.value));
17
+ .filter(o => isFluxFormSelectGroup(o) || (!search || o.label.toLowerCase().includes(search)))
18
+ .filter(o => isFluxFormSelectGroup(o) || !isMultiple || !unref(selected).find(s => s.value === o.value));
19
19
 
20
20
  if (available.length === 0) {
21
21
  return [];
@@ -9,11 +9,6 @@
9
9
  margin: 0;
10
10
  }
11
11
 
12
- // Keyword animations.
13
- //html {
14
- // interpolate-size: allow-keywords;
15
- //}
16
-
17
12
  body {
18
13
  // Accessible line-height.
19
14
  line-height: 1.5;
@@ -71,105 +66,3 @@
71
66
  isolation: isolate;
72
67
  }
73
68
  }
74
-
75
- //@layer flux-reset {
76
- // *,
77
- // *::before,
78
- // *::after {
79
- // box-sizing: border-box;
80
- // }
81
- //
82
- // html {
83
- // line-height: 1.6;
84
- // -webkit-text-size-adjust: 100%;
85
- // -webkit-tap-highlight-color: rgb(0 0 0 / 0);
86
- // -moz-tab-size: 4;
87
- // tab-size: 4;
88
- // }
89
- //
90
- // body {
91
- // margin: 0;
92
- // padding: 0;
93
- // line-height: inherit;
94
- // }
95
- //
96
- // blockquote,
97
- // dl,
98
- // dd,
99
- // h1,
100
- // h2,
101
- // h3,
102
- // h4,
103
- // h5,
104
- // h6,
105
- // hr,
106
- // figure,
107
- // p,
108
- // pre {
109
- // margin: 0;
110
- // }
111
- //
112
- // h1,
113
- // h2,
114
- // h3,
115
- // h4,
116
- // h5,
117
- // h6 {
118
- // font-size: inherit;
119
- // font-weight: inherit;
120
- // }
121
- //
122
- // ul,
123
- // ol {
124
- // margin: 0;
125
- // }
126
- //
127
- // code,
128
- // kbd,
129
- // samp,
130
- // pre {
131
- // font-family: monospace;
132
- // font-size: 1em;
133
- // }
134
- //
135
- // small {
136
- // font-size: 80%;
137
- // }
138
- //
139
- // sub,
140
- // sup {
141
- // position: relative;
142
- // font-size: 75%;
143
- // line-height: 0;
144
- // vertical-align: baseline;
145
- // }
146
- //
147
- // sub {
148
- // bottom: -0.25em;
149
- // }
150
- //
151
- // sup {
152
- // top: -0.5em;
153
- // }
154
- //
155
- // button,
156
- // select {
157
- // text-transform: none;
158
- // }
159
- //
160
- // button,
161
- // input,
162
- // textarea {
163
- // font: inherit;
164
- // }
165
- //
166
- // a {
167
- // color: inherit;
168
- // text-decoration: none;
169
- // }
170
- //
171
- // b,
172
- // strong {
173
- // font-weight: bolder;
174
- // }
175
- //}
@@ -21,32 +21,6 @@
21
21
  --gray-900: #1a1f2d;
22
22
  --gray-950: #090c19;
23
23
 
24
- //--gray-25: #ffffff;
25
- //--gray-50: #f9fafb;
26
- //--gray-100: #f3f4f6;
27
- //--gray-200: #e5e7eb;
28
- //--gray-300: #d1d5db;
29
- //--gray-400: #9ca3af;
30
- //--gray-500: #6b7280;
31
- //--gray-600: #4b5563;
32
- //--gray-700: #374151;
33
- //--gray-800: #1f2937;
34
- //--gray-900: #111827;
35
- //--gray-950: #030712;
36
-
37
- //--gray-25: #ffffff;
38
- //--gray-50: #f9fafb;
39
- //--gray-100: #f1f5f9;
40
- //--gray-200: #e2e8f0;
41
- //--gray-300: #cbd5e1;
42
- //--gray-400: #94a3b8;
43
- //--gray-500: #64748b;
44
- //--gray-600: #475569;
45
- //--gray-700: #334155;
46
- //--gray-800: #1e293b;
47
- //--gray-900: #0f172a;
48
- //--gray-950: #020617;
49
-
50
24
  --primary-25: #fcfcfc;
51
25
  --primary-50: #ebf2ff;
52
26
  --primary-100: #d7e5ff;