@proj-airi/ui 0.10.2 → 0.11.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@proj-airi/ui",
3
3
  "type": "module",
4
- "version": "0.10.2",
4
+ "version": "0.11.0",
5
5
  "description": "A collection of UI components that used by Project AIRI",
6
6
  "author": {
7
7
  "name": "Moeru AI Project AIRI Team",
@@ -30,7 +30,7 @@
30
30
  "vue": "^3.5.32"
31
31
  },
32
32
  "devDependencies": {
33
- "@vue-macros/volar": "3.0.0-beta.8",
33
+ "@vue-macros/volar": "^3.1.2",
34
34
  "vue-tsc": "^3.2.6"
35
35
  },
36
36
  "scripts": {
@@ -35,10 +35,12 @@ const props = withDefaults(defineProps<{
35
35
  options: ComboboxOptionItem<T>[] | ComboboxOptionGroupItem<T>[]
36
36
  placeholder?: string
37
37
  disabled?: boolean
38
+ openOnClick?: boolean
38
39
  contentMinWidth?: string | number
39
40
  contentWidth?: string | number
40
41
  }>(), {
41
42
  disabled: false,
43
+ openOnClick: true,
42
44
  })
43
45
 
44
46
  const modelValue = defineModel<T>({ required: false })
@@ -83,6 +85,7 @@ function toCssSize(value?: string | number): string | undefined {
83
85
  <ComboboxRoot
84
86
  v-model="modelValue"
85
87
  :disabled="props.disabled"
88
+ :open-on-click="props.openOnClick"
86
89
  :class="['relative', 'w-full', 'h-fit']"
87
90
  >
88
91
  <ComboboxAnchor
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { Combobox } from '../combobox'
3
3
 
4
- const props = defineProps<{
4
+ const props = withDefaults(defineProps<{
5
5
  options?: {
6
6
  label: string
7
7
  value: string | number
@@ -11,11 +11,15 @@ const props = defineProps<{
11
11
  }[]
12
12
  placeholder?: string
13
13
  disabled?: boolean
14
+ openOnClick?: boolean
14
15
  title?: string
15
16
  layout?: 'horizontal' | 'vertical'
16
17
  contentMinWidth?: string | number
17
18
  contentWidth?: string | number
18
- }>()
19
+ }>(), {
20
+ disabled: false,
21
+ openOnClick: true,
22
+ })
19
23
 
20
24
  const modelValue = defineModel<string | number>({ required: false })
21
25
  </script>
@@ -25,6 +29,7 @@ const modelValue = defineModel<string | number>({ required: false })
25
29
  v-model="modelValue"
26
30
  :options="[{ groupLabel: '', children: props.options }]"
27
31
  :disabled="props.disabled"
32
+ :open-on-click="props.openOnClick"
28
33
  :content-min-width="props.contentMinWidth"
29
34
  :content-width="props.contentWidth"
30
35
  :placeholder="props.placeholder"
@@ -13,12 +13,15 @@ const props = withDefaults(defineProps<{
13
13
  }[]
14
14
  placeholder?: string
15
15
  disabled?: boolean
16
+ openOnClick?: boolean
16
17
  layout?: 'horizontal' | 'vertical'
17
18
  selectClass?: string | string[]
18
19
  contentMinWidth?: string | number
19
20
  contentWidth?: string | number
20
21
  }>(), {
21
22
  layout: 'horizontal',
23
+ disabled: false,
24
+ openOnClick: true,
22
25
  })
23
26
 
24
27
  const modelValue = defineModel<string>({ required: false })
@@ -55,6 +58,7 @@ const modelValue = defineModel<string>({ required: false })
55
58
  :options="props.options?.filter(option => option.label && option.value) || []"
56
59
  :placeholder="props.placeholder"
57
60
  :disabled="props.disabled"
61
+ :open-on-click="props.openOnClick"
58
62
  :content-min-width="props.contentMinWidth"
59
63
  :content-width="props.contentWidth"
60
64
  :title="label"
@@ -27,6 +27,7 @@ const props = withDefaults(defineProps<{
27
27
  */
28
28
  hideRequiredMark?: boolean
29
29
  type?: InputType
30
+ autocomplete?: string
30
31
  inputClass?: string
31
32
  singleLine?: boolean
32
33
  }>(), {
@@ -57,6 +58,7 @@ const modelValue = defineModel<T>({ required: false })
57
58
  v-model.number="modelValue"
58
59
  :type="props.type"
59
60
  :placeholder="props.placeholder"
61
+ :autocomplete="props.autocomplete"
60
62
  :required="props.required"
61
63
  :class="props.inputClass"
62
64
  />
@@ -65,6 +67,7 @@ const modelValue = defineModel<T>({ required: false })
65
67
  v-model="modelValue"
66
68
  :type="props.type"
67
69
  :placeholder="props.placeholder"
70
+ :autocomplete="props.autocomplete"
68
71
  :required="props.required"
69
72
  :class="props.inputClass"
70
73
  />
@@ -73,6 +76,7 @@ const modelValue = defineModel<T>({ required: false })
73
76
  v-model="modelValue as string | undefined"
74
77
  :type="props.type"
75
78
  :placeholder="props.placeholder"
79
+ :autocomplete="props.autocomplete"
76
80
  :required="props.required"
77
81
  :class="[
78
82
  props.inputClass,
@@ -81,6 +85,7 @@ const modelValue = defineModel<T>({ required: false })
81
85
  'text-disabled:neutral-400 dark:text-disabled:neutral-600',
82
86
  'cursor-disabled:not-allowed',
83
87
  'w-full rounded-lg px-2 py-1 text-sm outline-none',
88
+ 'text-neutral-900 dark:text-neutral-100 placeholder:text-neutral-400 dark:placeholder:text-neutral-500',
84
89
  'shadow-sm',
85
90
  'bg-neutral-50 dark:bg-neutral-950 focus:bg-neutral-50 dark:focus:bg-neutral-900',
86
91
  ]"
@@ -9,11 +9,22 @@ const props = withDefaults(defineProps<{
9
9
  description?: string
10
10
  formatValue?: (value: number) => string
11
11
  as?: 'label' | 'div'
12
+ /**
13
+ * When provided, renders a reset control next to the label that restores the
14
+ * value to this default. Prefer `as="div"` when using this, since the reset
15
+ * button should not live inside a `<label>` element.
16
+ */
17
+ defaultValue?: number
12
18
  }>(), {
13
19
  as: 'label',
14
20
  })
15
21
 
16
22
  const modelValue = defineModel<number>({ required: true })
23
+
24
+ function resetToDefault() {
25
+ if (props.defaultValue !== undefined)
26
+ modelValue.value = props.defaultValue
27
+ }
17
28
  </script>
18
29
 
19
30
  <template>
@@ -24,6 +35,15 @@ const modelValue = defineModel<number>({ required: true })
24
35
  <slot name="label">
25
36
  {{ label }}
26
37
  </slot>
38
+ <button
39
+ v-if="defaultValue !== undefined"
40
+ type="button"
41
+ title="Reset to default"
42
+ :class="['px-2', 'text-xs', 'outline-none']"
43
+ @click.prevent="resetToDefault"
44
+ >
45
+ <div :class="['i-solar:forward-linear', 'transform-scale-x--100', 'text-neutral-500', 'dark:text-neutral-400']" />
46
+ </button>
27
47
  </div>
28
48
  <div :class="['text-xs', 'text-neutral-500', 'dark:text-neutral-400']">
29
49
  <slot name="description">
@@ -40,6 +40,7 @@ const variantClasses: Record<InputVariant, Record<InputTheme, {
40
40
  default: [
41
41
  'w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none',
42
42
  'bg-neutral-50 dark:bg-neutral-950 focus:bg-neutral-50 dark:focus:bg-neutral-900',
43
+ 'text-neutral-900 dark:text-neutral-100 placeholder:text-neutral-400 dark:placeholder:text-neutral-500',
43
44
  'focus:border-primary-300 dark:focus:border-primary-400/50 border-2 border-solid border-neutral-100 dark:border-neutral-900',
44
45
  'text-disabled:neutral-400 dark:text-disabled:neutral-600',
45
46
  'shadow-sm',
@@ -51,6 +52,7 @@ const variantClasses: Record<InputVariant, Record<InputTheme, {
51
52
  default: [
52
53
  'w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none',
53
54
  'bg-neutral-50 dark:bg-neutral-950 focus:bg-neutral-50 dark:focus:bg-neutral-900',
55
+ 'text-neutral-900 dark:text-neutral-100 placeholder:text-neutral-400 dark:placeholder:text-neutral-500',
54
56
  'focus:border-primary-300 dark:focus:border-primary-400/50 border-2 border-solid border-neutral-100 dark:border-neutral-900',
55
57
  'text-disabled:neutral-400 dark:text-disabled:neutral-600',
56
58
  'shadow-sm',
@@ -62,6 +64,7 @@ const variantClasses: Record<InputVariant, Record<InputTheme, {
62
64
  default: [
63
65
  'w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none',
64
66
  'bg-neutral-100 dark:bg-neutral-800 focus:bg-neutral-50 dark:focus:bg-neutral-950',
67
+ 'text-neutral-900 dark:text-neutral-100 placeholder:text-neutral-400 dark:placeholder:text-neutral-500',
65
68
  'focus:border-primary-500/30 dark:focus:border-primary-400/50 border-2 border-solid border-neutral-500/5 dark:border-neutral-700/40',
66
69
  'text-disabled:neutral-400 dark:text-disabled:neutral-600',
67
70
  ],
@@ -64,11 +64,13 @@ function handleInput(e: Event) {
64
64
  }
65
65
 
66
66
  function onWheelInput(ev: WheelEvent) {
67
- let valueAfter = 0
67
+ if (ev.deltaY === 0)
68
+ return
69
+ let valueAfter = modelValue.value
68
70
  if (ev.deltaY < 0)
69
- valueAfter = modelValue.value += props.step * (shiftPressed.value ? 50 : 1)
71
+ valueAfter += props.step * (shiftPressed.value ? 50 : 1)
70
72
  if (ev.deltaY > 0)
71
- valueAfter = modelValue.value -= props.step * (shiftPressed.value ? 50 : 1)
73
+ valueAfter -= props.step * (shiftPressed.value ? 50 : 1)
72
74
  modelValue.value = Math.min(Math.max(valueAfter, props.min), props.max)
73
75
  }
74
76
  </script>
@@ -13,6 +13,7 @@ const modelValue = defineModel<string>({ default: '' })
13
13
  'text-disabled:neutral-400 dark:text-disabled:neutral-600',
14
14
  'cursor-disabled:not-allowed',
15
15
  'w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none',
16
+ 'text-neutral-900 dark:text-neutral-100 placeholder:text-neutral-400 dark:placeholder:text-neutral-500',
16
17
  'shadow-sm',
17
18
  'bg-neutral-50 dark:bg-neutral-950 focus:bg-neutral-50 dark:focus:bg-neutral-900',
18
19
  ]"
@@ -63,6 +63,10 @@ onMounted(async () => {
63
63
 
64
64
  containerElementBounding.update()
65
65
  })
66
+
67
+ defineExpose({
68
+ containerRef,
69
+ })
66
70
  </script>
67
71
 
68
72
  <template>
@@ -154,7 +154,7 @@ const baseClasses = computed(() => {
154
154
  const theme = variant[props.theme] || variant.default
155
155
 
156
156
  return [
157
- 'font-medium outline-none',
157
+ 'font-medium outline-none whitespace-nowrap',
158
158
  'transition-all duration-200 ease-in-out',
159
159
  'disabled:cursor-not-allowed disabled:opacity-50',
160
160
  'backdrop-blur-md',