@karyl-chan/ui 0.1.0 → 0.2.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,6 +1,6 @@
1
1
  {
2
2
  "name": "@karyl-chan/ui",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Shared Vue UI primitives, composables, and design tokens used by karyl-chan's bot frontend and plugin webuis.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -22,13 +22,12 @@
22
22
  "./use-drawer.css": "./src/composables/use-drawer.css",
23
23
  "./use-popover.css": "./src/composables/use-popover.css"
24
24
  },
25
- "files": ["src"],
25
+ "files": [
26
+ "src"
27
+ ],
26
28
  "engines": {
27
29
  "node": ">=22"
28
30
  },
29
- "scripts": {
30
- "typecheck": "vue-tsc --noEmit"
31
- },
32
31
  "peerDependencies": {
33
32
  "@iconify/vue": ">=5",
34
33
  "@popperjs/core": ">=2",
@@ -38,7 +37,9 @@
38
37
  "vue-router": ">=4"
39
38
  },
40
39
  "peerDependenciesMeta": {
41
- "vue-router": { "optional": true }
40
+ "vue-router": {
41
+ "optional": true
42
+ }
42
43
  },
43
44
  "devDependencies": {
44
45
  "@iconify/vue": "^5.0.0",
@@ -54,5 +55,8 @@
54
55
  "vue": "^3.5.13",
55
56
  "vue-router": "^4.5.0",
56
57
  "vue-tsc": "^2.2.0"
58
+ },
59
+ "scripts": {
60
+ "typecheck": "vue-tsc --noEmit"
57
61
  }
58
- }
62
+ }
@@ -70,8 +70,8 @@ withDefaults(defineProps<{
70
70
  />
71
71
  </slot>
72
72
 
73
- <!-- 主要 label -->
74
- <span class="app-btn-label"><slot /></span>
73
+ <!-- 主要 label — 略過時不渲染,避免 icon-only 按鈕產生多餘 gap -->
74
+ <span v-if="$slots.default" class="app-btn-label"><slot /></span>
75
75
 
76
76
  <!-- 右側 slot -->
77
77
  <slot name="trailing" />
@@ -19,6 +19,8 @@ const props = withDefaults(defineProps<{
19
19
  maxlength?: number;
20
20
  fullWidth?: boolean;
21
21
  name?: string;
22
+ autofocus?: boolean;
23
+ required?: boolean;
22
24
  }>(), {
23
25
  label: '',
24
26
  hint: '',
@@ -28,7 +30,9 @@ const props = withDefaults(defineProps<{
28
30
  readonly: false,
29
31
  rows: 3,
30
32
  fullWidth: false,
31
- name: ''
33
+ name: '',
34
+ autofocus: false,
35
+ required: false
32
36
  });
33
37
 
34
38
  const emit = defineEmits<{
@@ -64,6 +68,8 @@ const rootClass = computed(() => ({
64
68
  :rows="rows"
65
69
  :maxlength="maxlength"
66
70
  :name="name || undefined"
71
+ :autofocus="autofocus"
72
+ :required="required"
67
73
  @input="onInput"
68
74
  @blur="emit('blur', $event)"
69
75
  @focus="emit('focus', $event)"
@@ -1,4 +1,4 @@
1
- <script setup lang="ts" generic="V extends string | number | null">
1
+ <script setup lang="ts" generic="V extends string | number | null | undefined">
2
2
  import { computed, useId } from 'vue';
3
3
 
4
4
  /**
@@ -17,6 +17,8 @@ import { computed, useId } from 'vue';
17
17
 
18
18
  const props = withDefaults(defineProps<{
19
19
  modelValue: V;
20
+ /** v-model modifier bag (e.g. `.number`, `.trim`); Vue populates this. */
21
+ modelModifiers?: { number?: boolean; trim?: boolean; lazy?: boolean };
20
22
  label?: string;
21
23
  hint?: string;
22
24
  error?: string;
@@ -70,7 +72,16 @@ const fieldId = useId();
70
72
 
71
73
  function onInput(e: Event): void {
72
74
  const target = e.target as HTMLInputElement;
73
- emit('update:modelValue', target.value as V);
75
+ let value: string | number = target.value;
76
+ if (props.modelModifiers?.trim) value = (value as string).trim();
77
+ if (props.modelModifiers?.number) {
78
+ const n = Number(value);
79
+ // Mirror Vue's `.number` semantics: only coerce when parseable;
80
+ // otherwise pass through the raw string so the input stays
81
+ // editable during typing (e.g. "1.").
82
+ if (!Number.isNaN(n) && value !== '') value = n;
83
+ }
84
+ emit('update:modelValue', value as V);
74
85
  }
75
86
 
76
87
  const rootClass = computed(() => ({