pukaad-ui-lib 1.38.0 → 1.40.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/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pukaad-ui-lib",
3
3
  "configKey": "pukaadUI",
4
- "version": "1.38.0",
4
+ "version": "1.40.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -1 +1 @@
1
- @import "tailwindcss";@import "tw-animate-css";@import "./system.css";@import "./fonts/Anuphan.css";@import "./fonts/Sarabun.css";@import "./scroll-bar.css";@import "quill/dist/quill.snow.css";@custom-variant dark (&:is(.dark *));@theme inline{--radius-sm:calc(var(--radius) - 4px);--radius-md:calc(var(--radius) - 2px);--radius-lg:var(--radius);--radius-xl:calc(var(--radius) + 4px);--color-background:var(--background);--color-foreground:var(--foreground);--color-card:var(--card);--color-card-foreground:var(--card-foreground);--color-popover:var(--popover);--color-popover-foreground:var(--popover-foreground);--color-primary-foreground:var(--primary-foreground);--color-secondary-foreground:var(--secondary-foreground);--color-muted:var(--muted);--color-muted-foreground:var(--muted-foreground);--color-accent:var(--accent);--color-accent-foreground:var(--accent-foreground);--color-destructive:var(--destructive);--color-destructive-foreground:var(--destructive-foreground);--color-border:var(--border);--color-input:var(--input);--color-ring:var(--ring);--color-chart-1:var(--chart-1);--color-chart-2:var(--chart-2);--color-chart-3:var(--chart-3);--color-chart-4:var(--chart-4);--color-chart-5:var(--chart-5);--color-sidebar:var(--sidebar);--color-sidebar-foreground:var(--sidebar-foreground);--color-sidebar-primary:var(--sidebar-primary);--color-sidebar-primary-foreground:var(--sidebar-primary-foreground);--color-sidebar-accent:var(--sidebar-accent);--color-sidebar-accent-foreground:var(--sidebar-accent-foreground);--color-sidebar-border:var(--sidebar-border);--color-sidebar-ring:var(--sidebar-ring);--color-red:var(--red);--color-error:var(--error);--color-success:var(--success);--color-green:var(--green);--color-warning:var(--warning);--color-yellow:var(--yellow);--color-primary:var(--primary);--color-secondary:var(--secondary);--color-info:var(--info);--color-black:var(--black);--color-dark:var(--dark);--color-gray:var(--gray);--color-cloud:var(--cloud);--color-mercury:var(--mercury);--color-green-light:var(--green-light);--color-red-light:var(--red-light);--color-yellow-light:var(--yellow-light);--color-bright:var(--bright);--color-silver:var(--silver);--color-smoke:var(--smoke);--z-index-announce-tool:10;--z-index-dropdown:11;--z-index-overlay:12;--z-index-loading:13;--z-index-drawer:14;--z-index-modal:15;--z-index-toast:99998;--z-index-loading-page:99999}:root{--radius:0.625rem;--red:#b71c1c;--success:#43a047;--green:#1b5e20;--warning:#f9a825;--yellow:#fbc02d;--info:#e3f2fd;--black:#212121;--dark:#424242;--gray:#616161;--cloud:#c4c4c4;--mercury:#e0e0e0;--green-light:#e3f5e3;--red-light:#fde3e3;--yellow-light:#fef3c7;--bright:#f5f5f5;--silver:#f7f7f7;--smoke:#fafafa;--white:#fff;--background:var(--white);--foreground:var(--black);--card:var(--background);--card-foreground:var(--foreground);--popover:var(--background);--popover-foreground:var(--foreground);--primary:#1976b8;--primary-foreground:var(--white);--secondary:#22a7ef;--secondary-foreground:var(--white);--muted:oklch(0.97 0 0);--muted-foreground:var(--gray);--accent:oklch(0.97 0 0);--accent-foreground:oklch(0.205 0 0);--destructive:#d32f2f;--destructive-foreground:var(--white);--border:var(--color-mercury);--input:var(--color-mercury);--ring:var(--mercury);--chart-1:oklch(0.646 0.222 41.116);--chart-2:oklch(0.6 0.118 184.704);--chart-3:oklch(0.398 0.07 227.392);--chart-4:oklch(0.828 0.189 84.429);--chart-5:oklch(0.769 0.188 70.08);--sidebar:oklch(0.985 0 0);--sidebar-foreground:var(--foreground);--sidebar-primary:oklch(0.205 0 0);--sidebar-primary-foreground:oklch(0.985 0 0);--sidebar-accent:oklch(0.97 0 0);--sidebar-accent-foreground:oklch(0.205 0 0);--sidebar-border:var(--color-mercury);--sidebar-ring:var(--color-mercury)}.dark{--background:oklch(0.145 0 0);--foreground:oklch(0.985 0 0);--card:oklch(0.145 0 0);--card-foreground:oklch(0.985 0 0);--popover:oklch(0.145 0 0);--popover-foreground:oklch(0.985 0 0);--primary:oklch(0.985 0 0);--primary-foreground:oklch(0.205 0 0);--secondary:oklch(0.269 0 0);--secondary-foreground:oklch(0.985 0 0);--muted:oklch(0.269 0 0);--muted-foreground:oklch(0.708 0 0);--accent:oklch(0.269 0 0);--accent-foreground:oklch(0.985 0 0);--destructive:oklch(0.396 0.141 25.723);--destructive-foreground:oklch(0.637 0.237 25.331);--border:oklch(0.269 0 0);--input:oklch(0.269 0 0);--ring:oklch(0.439 0 0);--chart-1:oklch(0.488 0.243 264.376);--chart-2:oklch(0.696 0.17 162.48);--chart-3:oklch(0.769 0.188 70.08);--chart-4:oklch(0.627 0.265 303.9);--chart-5:oklch(0.645 0.246 16.439);--sidebar:oklch(0.205 0 0);--sidebar-foreground:oklch(0.985 0 0);--sidebar-primary:oklch(0.488 0.243 264.376);--sidebar-primary-foreground:oklch(0.985 0 0);--sidebar-accent:oklch(0.269 0 0);--sidebar-accent-foreground:oklch(0.985 0 0);--sidebar-border:oklch(0.269 0 0);--sidebar-ring:oklch(0.439 0 0)}@layer base{*{@apply border-border outline-ring/50}body{@apply bg-background text-foreground}}
1
+ @import "tailwindcss";@import "tw-animate-css";@import "./system.css";@import "./fonts/Anuphan.css";@import "./fonts/Sarabun.css";@import "./scroll-bar.css";@import "quill/dist/quill.snow.css";@custom-variant dark (&:is(.dark *));@theme inline{--radius-sm:calc(var(--radius) - 4px);--radius-md:calc(var(--radius) - 2px);--radius-lg:var(--radius);--radius-xl:calc(var(--radius) + 4px);--color-background:var(--background);--color-foreground:var(--foreground);--color-card:var(--card);--color-card-foreground:var(--card-foreground);--color-popover:var(--popover);--color-popover-foreground:var(--popover-foreground);--color-primary-foreground:var(--primary-foreground);--color-secondary-foreground:var(--secondary-foreground);--color-muted:var(--muted);--color-muted-foreground:var(--muted-foreground);--color-accent:var(--accent);--color-accent-foreground:var(--accent-foreground);--color-destructive:var(--destructive);--color-destructive-foreground:var(--destructive-foreground);--color-border:var(--border);--color-input:var(--input);--color-ring:var(--ring);--color-chart-1:var(--chart-1);--color-chart-2:var(--chart-2);--color-chart-3:var(--chart-3);--color-chart-4:var(--chart-4);--color-chart-5:var(--chart-5);--color-sidebar:var(--sidebar);--color-sidebar-foreground:var(--sidebar-foreground);--color-sidebar-primary:var(--sidebar-primary);--color-sidebar-primary-foreground:var(--sidebar-primary-foreground);--color-sidebar-accent:var(--sidebar-accent);--color-sidebar-accent-foreground:var(--sidebar-accent-foreground);--color-sidebar-border:var(--sidebar-border);--color-sidebar-ring:var(--sidebar-ring);--color-red:var(--red);--color-error:var(--error);--color-success:var(--success);--color-green:var(--green);--color-warning:var(--warning);--color-yellow:var(--yellow);--color-primary:var(--primary);--color-secondary:var(--secondary);--color-info:var(--info);--color-black:var(--black);--color-dark:var(--dark);--color-gray:var(--gray);--color-cloud:var(--cloud);--color-mercury:var(--mercury);--color-green-light:var(--green-light);--color-red-light:var(--red-light);--color-yellow-light:var(--yellow-light);--color-bright:var(--bright);--color-silver:var(--silver);--color-smoke:var(--smoke);--z-index-announce-tool:10;--z-index-dropdown:11;--z-index-overlay:12;--z-index-loading:13;--z-index-drawer:14;--z-index-modal:15;--z-index-toast:99998;--z-index-loading-page:99999}:root{--radius:0.625rem;--red:#b71c1c;--success:#43a047;--green:#1b5e20;--warning:#f9a825;--yellow:#fbc02d;--info:#e3f2fd;--black:#212121;--dark:#424242;--gray:#616161;--cloud:#c4c4c4;--mercury:#e0e0e0;--green-light:#e3f5e3;--red-light:#fde3e3;--yellow-light:#fef3c7;--bright:#f5f5f5;--silver:#f7f7f7;--smoke:#fafafa;--white:#fff;--background:var(--white);--foreground:var(--black);--card:var(--background);--card-foreground:var(--foreground);--popover:var(--background);--popover-foreground:var(--foreground);--primary:#1976b8;--primary-foreground:var(--white);--secondary:#22a7ef;--secondary-foreground:var(--white);--muted:oklch(0.97 0 0);--muted-foreground:var(--cloud);--accent:oklch(0.97 0 0);--accent-foreground:oklch(0.205 0 0);--destructive:#d32f2f;--destructive-foreground:var(--white);--border:var(--color-mercury);--input:var(--color-mercury);--ring:var(--mercury);--chart-1:oklch(0.646 0.222 41.116);--chart-2:oklch(0.6 0.118 184.704);--chart-3:oklch(0.398 0.07 227.392);--chart-4:oklch(0.828 0.189 84.429);--chart-5:oklch(0.769 0.188 70.08);--sidebar:oklch(0.985 0 0);--sidebar-foreground:var(--foreground);--sidebar-primary:oklch(0.205 0 0);--sidebar-primary-foreground:oklch(0.985 0 0);--sidebar-accent:oklch(0.97 0 0);--sidebar-accent-foreground:oklch(0.205 0 0);--sidebar-border:var(--color-mercury);--sidebar-ring:var(--color-mercury)}.dark{--background:oklch(0.145 0 0);--foreground:oklch(0.985 0 0);--card:oklch(0.145 0 0);--card-foreground:oklch(0.985 0 0);--popover:oklch(0.145 0 0);--popover-foreground:oklch(0.985 0 0);--primary:oklch(0.985 0 0);--primary-foreground:oklch(0.205 0 0);--secondary:oklch(0.269 0 0);--secondary-foreground:oklch(0.985 0 0);--muted:oklch(0.269 0 0);--muted-foreground:oklch(0.708 0 0);--accent:oklch(0.269 0 0);--accent-foreground:oklch(0.985 0 0);--destructive:oklch(0.396 0.141 25.723);--destructive-foreground:oklch(0.637 0.237 25.331);--border:oklch(0.269 0 0);--input:oklch(0.269 0 0);--ring:oklch(0.439 0 0);--chart-1:oklch(0.488 0.243 264.376);--chart-2:oklch(0.696 0.17 162.48);--chart-3:oklch(0.769 0.188 70.08);--chart-4:oklch(0.627 0.265 303.9);--chart-5:oklch(0.645 0.246 16.439);--sidebar:oklch(0.205 0 0);--sidebar-foreground:oklch(0.985 0 0);--sidebar-primary:oklch(0.488 0.243 264.376);--sidebar-primary-foreground:oklch(0.985 0 0);--sidebar-accent:oklch(0.269 0 0);--sidebar-accent-foreground:oklch(0.985 0 0);--sidebar-border:oklch(0.269 0 0);--sidebar-ring:oklch(0.439 0 0)}@layer base{*{@apply border-border outline-ring/50}body{@apply bg-background text-foreground}}
@@ -2,7 +2,7 @@ import type { SheetContentProps } from "@/runtime/components/ui/sheet/SheetConte
2
2
  export interface DrawerProps extends SheetContentProps {
3
3
  title?: string;
4
4
  description?: string;
5
- isLoading?: boolean;
5
+ loading?: boolean;
6
6
  loadingText?: string;
7
7
  }
8
8
  type __VLS_Props = DrawerProps;
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <ShadSheet v-model:open="isOpen">
3
3
  <ShadSheetContent v-bind="props" v-slot="{ meta }" @submit="onSubmit">
4
- <Loading :is-loading="props.isLoading" :text="props.loadingText" />
4
+ <Loading :loading="props.loading" :text="props.loadingText" />
5
5
  <ShadSheetHeader class="flex-shrink-0">
6
6
  <slot name="header">
7
7
  <ShadSheetTitle>{{ props.title }}</ShadSheetTitle>
@@ -24,7 +24,7 @@
24
24
  const props = defineProps({
25
25
  title: { type: String, required: false },
26
26
  description: { type: String, required: false },
27
- isLoading: { type: Boolean, required: false },
27
+ loading: { type: Boolean, required: false },
28
28
  loadingText: { type: String, required: false },
29
29
  class: { type: null, required: false },
30
30
  side: { type: String, required: false },
@@ -2,7 +2,7 @@ import type { SheetContentProps } from "@/runtime/components/ui/sheet/SheetConte
2
2
  export interface DrawerProps extends SheetContentProps {
3
3
  title?: string;
4
4
  description?: string;
5
- isLoading?: boolean;
5
+ loading?: boolean;
6
6
  loadingText?: string;
7
7
  }
8
8
  type __VLS_Props = DrawerProps;
@@ -64,15 +64,15 @@ declare const __VLS_export: import("vue").DefineComponent<ImageCropperProps, {
64
64
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ImageCropperProps> & Readonly<{}>, {
65
65
  center: boolean;
66
66
  src: string;
67
- background: boolean;
68
- modal: boolean;
69
67
  responsive: boolean;
70
68
  restore: boolean;
71
69
  checkCrossOrigin: boolean;
72
70
  checkOrientation: boolean;
73
71
  crossorigin: "" | "anonymous" | "use-credentials";
72
+ modal: boolean;
74
73
  guides: boolean;
75
74
  highlight: boolean;
75
+ background: boolean;
76
76
  autoCrop: boolean;
77
77
  movable: boolean;
78
78
  rotatable: boolean;
@@ -64,15 +64,15 @@ declare const __VLS_export: import("vue").DefineComponent<ImageCropperProps, {
64
64
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ImageCropperProps> & Readonly<{}>, {
65
65
  center: boolean;
66
66
  src: string;
67
- background: boolean;
68
- modal: boolean;
69
67
  responsive: boolean;
70
68
  restore: boolean;
71
69
  checkCrossOrigin: boolean;
72
70
  checkOrientation: boolean;
73
71
  crossorigin: "" | "anonymous" | "use-credentials";
72
+ modal: boolean;
74
73
  guides: boolean;
75
74
  highlight: boolean;
75
+ background: boolean;
76
76
  autoCrop: boolean;
77
77
  movable: boolean;
78
78
  rotatable: boolean;
@@ -1,27 +1,54 @@
1
1
  <template>
2
2
  <ShadPopover v-model:open="open" :modal="false">
3
3
  <ShadPopoverAnchor as-child>
4
- <InputTextField ref="fieldRef" v-model="searchText" :label="props.label" :name="props.name" :rules="props.rules"
5
- :required="props.required" :description="props.description" :placeholder="props.placeholder"
6
- :disabled-border="props.disabledBorder" :disabled-error-message="props.disabledErrorMessage" autocomplete="off"
7
- @focus="open = true" @click="open = true" @blur="handleBlur" :show-counter="props.showCounter"
8
- :limit="props.limit">
4
+ <InputTextField
5
+ ref="fieldRef"
6
+ v-model="searchText"
7
+ :label="props.label"
8
+ :name="props.name"
9
+ :rules="props.rules"
10
+ :required="props.required"
11
+ :description="props.description"
12
+ :placeholder="props.placeholder"
13
+ :disabled-border="props.disabledBorder"
14
+ :disabled-error-message="props.disabledErrorMessage"
15
+ autocomplete="off"
16
+ @focus="open = true"
17
+ @click="open = true"
18
+ @blur="handleBlur"
19
+ :show-counter="props.showCounter"
20
+ :limit="props.limit"
21
+ >
9
22
  <template #append>
10
- <Icon @mousedown.stop.prevent="handleIconMouseDown" :name="modelValue ? 'lucide:x' : 'lucide:search'"
11
- size="16" class="cursor-pointer" />
23
+ <Icon
24
+ @mousedown.stop.prevent="handleIconMouseDown"
25
+ :name="modelValue ? 'lucide:x' : 'lucide:search'"
26
+ size="16"
27
+ class="cursor-pointer text-black"
28
+ />
12
29
  </template>
13
30
  </InputTextField>
14
31
  </ShadPopoverAnchor>
15
- <ShadPopoverContent @openAutoFocus.prevent @closeAutoFocus.prevent align="start"
16
- class="max-h-[312px] overflow-auto">
17
- <div v-if="filteredOptions.length === 0" class="flex flex-col gap-[8px] items-center justify-center h-[192px]">
32
+ <ShadPopoverContent
33
+ @openAutoFocus.prevent
34
+ @closeAutoFocus.prevent
35
+ align="start"
36
+ class="max-h-[312px] overflow-auto"
37
+ >
38
+ <div
39
+ v-if="filteredOptions.length === 0"
40
+ class="flex flex-col gap-[8px] items-center justify-center h-[192px]"
41
+ >
18
42
  <Icon name="pukaad:page-not-found" class="w-[77px] h-[64px]" />
19
43
  <div class="font-body-medium text-center text-gray">ไม่พบข้อมูล</div>
20
44
  </div>
21
45
  <div v-else>
22
- <div v-for="option in filteredOptions" :key="option.value"
46
+ <div
47
+ v-for="option in filteredOptions"
48
+ :key="option.value"
23
49
  class="cursor-pointer hover:bg-smoke flex items-center gap-[8px] px-[8px] py-[6px]"
24
- @mousedown.prevent="selectOption(option)">
50
+ @mousedown.prevent="selectOption(option)"
51
+ >
25
52
  <div class="flex flex-col gap-[4px] w-full">
26
53
  <div class="font-body-medium">
27
54
  {{ option.label }}
@@ -30,7 +57,11 @@
30
57
  {{ option.description }}
31
58
  </div>
32
59
  </div>
33
- <Icon v-if="modelValue === option.value" name="lucide:check" size="16" />
60
+ <Icon
61
+ v-if="modelValue === option.value"
62
+ name="lucide:check"
63
+ size="16"
64
+ />
34
65
  </div>
35
66
  </div>
36
67
  </ShadPopoverContent>
@@ -19,16 +19,15 @@ type __VLS_ModelProps = {
19
19
  modelValue?: string | string[];
20
20
  };
21
21
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
22
- declare var __VLS_21: {}, __VLS_81: {};
22
+ declare var __VLS_25: {}, __VLS_67: {};
23
23
  type __VLS_Slots = {} & {
24
- label?: (props: typeof __VLS_21) => any;
24
+ label?: (props: typeof __VLS_25) => any;
25
25
  } & {
26
- options?: (props: typeof __VLS_81) => any;
26
+ options?: (props: typeof __VLS_67) => any;
27
27
  };
28
28
  declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
29
- validate: () => Promise<any>;
30
29
  setErrors: (errMsg: string[]) => void;
31
- fieldRef: import("vue").Ref<any, any>;
30
+ inputTextFieldRef: import("vue").Ref<any, any>;
32
31
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
33
32
  "update:modelValue": (value: string | string[]) => any;
34
33
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
@@ -1,101 +1,87 @@
1
1
  <template>
2
- <ShadFormField
3
- ref="fieldRef"
4
- :name="props.name"
5
- :rules="props.rules || defaultRules"
6
- v-slot="{ componentField }"
7
- v-model="modelValue"
8
- >
9
- <ShadFormItem>
10
- <ShadFormLabel v-if="$slots.label || props.label" class="w-full">
11
- <slot name="label">
12
- <div
13
- :class="[
2
+ <ShadPopover v-model:open="popoverOpen">
3
+ <ShadPopoverAnchor as-child>
4
+ <InputTextField
5
+ ref="inputTextFieldRef"
6
+ v-model="displayText"
7
+ :name="props.name"
8
+ :rules="computedRules"
9
+ :required="props.required"
10
+ :disabled-border="props.disabledBorder"
11
+ :placeholder="props.placeholder"
12
+ readonly
13
+ class="cursor-pointer"
14
+ @click="handleInputClick"
15
+ >
16
+ <template #label>
17
+ <slot name="label">
18
+ <div
19
+ :class="[
14
20
  'flex-1',
15
21
  props.disabledBorder && 'font-body-small-prominent'
16
22
  ]"
17
- >
18
- {{ props.label }}
19
- <span v-if="props.required" class="text-destructive">*</span>
20
- </div>
21
- </slot>
22
- <div
23
- v-if="props.showCounter"
24
- :class="[props.disabledBorder && 'font-body-small']"
25
- >
23
+ >
24
+ {{ props.label }}
25
+ <span v-if="props.required" class="text-destructive">*</span>
26
+ </div>
27
+ </slot>
26
28
  <div
27
- v-if="props.limit > 0"
28
- :class="[
29
- (props.multiple ? modelValue.length : modelValue.length) > props.limit && 'text-destructive'
30
- ]"
29
+ v-if="props.showCounter && props.limit > 0"
30
+ :class="[props.disabledBorder && 'font-body-small']"
31
31
  >
32
- {{
33
- props.multiple ? modelValue.length : modelValue.length
34
- }}/{{ props.limit }}
32
+ <div :class="[selectedCount > props.limit && 'text-destructive']">
33
+ {{ selectedCount }}/{{ props.limit }}
34
+ </div>
35
35
  </div>
36
- </div>
37
- </ShadFormLabel>
38
- <ShadPopover v-bind="componentField">
39
- <ShadPopoverTrigger as-child>
40
- <ShadFormControl>
41
- <ShadButton
42
- variant="outline"
43
- role="combobox"
44
- class="w-full justify-between text-start"
45
- >
46
- <span
47
- :class="{
48
- 'text-cloud': props.placeholder && isShowingPlaceholder
49
- }"
50
- >
51
- {{ displayValue }}
52
- </span>
53
- <Icon name="lucide:chevrons-up-down" class="h-4 w-4" />
54
- </ShadButton>
55
- </ShadFormControl>
56
- </ShadPopoverTrigger>
57
- <ShadPopoverContent>
58
- <ShadCommand>
59
- <ShadCommandInput />
60
- <ShadCommandList>
61
- <ShadCommandGroup v-if="options && options.length > 0">
62
- <slot name="options">
63
- <ShadCommandItem
64
- v-for="option in options"
65
- :key="option.value"
66
- :value="option.value"
67
- :disabled="isOptionDisabled(option.value)"
68
- :class="[
36
+ </template>
37
+ <template #append>
38
+ <Icon
39
+ @mousedown.stop.prevent="handleIconClick"
40
+ @click.stop.prevent
41
+ :name="hasSelection ? 'lucide:x' : 'lucide:chevron-down'"
42
+ size="16"
43
+ class="cursor-pointer text-black"
44
+ />
45
+ </template>
46
+ </InputTextField>
47
+ </ShadPopoverAnchor>
48
+ <ShadPopoverContent @openAutoFocus.prevent>
49
+ <ShadCommand>
50
+ <ShadCommandInput />
51
+ <ShadCommandList>
52
+ <ShadCommandGroup v-if="options && options.length > 0">
53
+ <slot name="options">
54
+ <ShadCommandItem
55
+ v-for="option in options"
56
+ :key="option.value"
57
+ :value="option.value"
58
+ :disabled="isOptionDisabled(option.value)"
59
+ :class="[
69
60
  isOptionDisabled(option.value) && 'cursor-not-allowed'
70
61
  ]"
71
- @click="() => handleSelect(option.value)"
72
- >
73
- <ShadCheckbox
74
- v-if="props.multiple"
75
- :model-value="isSelected(option.value)"
76
- :disabled="isOptionDisabled(option.value)"
77
- class="mr-2 pointer-events-none"
78
- />
62
+ @click="() => handleSelect(option.value)"
63
+ >
64
+ <ShadCheckbox
65
+ v-if="props.multiple"
66
+ :model-value="isSelected(option.value)"
67
+ :disabled="isOptionDisabled(option.value)"
68
+ class="mr-2 pointer-events-none"
69
+ />
79
70
 
80
- {{ option.label }}
81
- <Icon
82
- v-if="!props.multiple && isSelected(option.value)"
83
- name="lucide:check"
84
- class="ml-auto h-4 w-4"
85
- />
86
- </ShadCommandItem>
87
- </slot>
88
- </ShadCommandGroup>
89
- <ShadCommandEmpty
90
- :force-show="!options || options.length === 0"
91
- />
92
- </ShadCommandList>
93
- </ShadCommand>
94
- </ShadPopoverContent>
95
- </ShadPopover>
96
- <ShadFormMessage />
97
- </ShadFormItem>
98
- </ShadFormField>
71
+ {{ option.label }}
72
+ <Icon
73
+ v-if="!props.multiple && isSelected(option.value)"
74
+ name="lucide:check"
75
+ class="ml-auto h-4 w-4"
76
+ />
77
+ </ShadCommandItem>
78
+ </slot>
79
+ </ShadCommandGroup>
80
+ <ShadCommandEmpty :force-show="!options || options.length === 0" />
81
+ </ShadCommandList>
82
+ </ShadCommand>
83
+ </ShadPopoverContent>
84
+ </ShadPopover>
99
85
  </template>
100
86
 
101
87
  <script setup>
@@ -116,49 +102,80 @@ const modelValue = defineModel({ type: [String, Array], ...{
116
102
  default: () => ""
117
103
  } });
118
104
  const popoverOpen = ref(false);
119
- const fieldRef = ref();
120
- const defaultRules = (v) => {
121
- return true;
122
- };
123
- onMounted(() => {
124
- if (props.multiple && modelValue.value === "") {
125
- modelValue.value = [];
126
- }
127
- });
128
- const displayValue = computed(() => {
105
+ const inputTextFieldRef = ref();
106
+ const displayText = ref("");
107
+ const updateDisplayText = () => {
129
108
  if (props.multiple) {
130
109
  const selectedValues = modelValue.value;
131
110
  if (!selectedValues || selectedValues.length === 0) {
132
- return props.placeholder || "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23";
111
+ displayText.value = "";
112
+ return;
133
113
  }
134
114
  const selectedLabels = selectedValues.map((val) => props.options?.find((opt) => opt.value === val)?.label).filter(Boolean);
135
- return selectedLabels.length > 0 ? selectedLabels.join(", ") : props.placeholder || "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23";
115
+ displayText.value = selectedLabels.length > 0 ? selectedLabels.join(", ") : "";
136
116
  } else {
137
117
  const selectedOption = props.options?.find(
138
118
  (opt) => opt.value === modelValue.value
139
119
  );
140
- return selectedOption?.label || props.placeholder || "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23";
120
+ displayText.value = selectedOption?.label || "";
141
121
  }
122
+ };
123
+ watch([modelValue, () => props.options], updateDisplayText, {
124
+ immediate: true,
125
+ deep: true
142
126
  });
143
- const isShowingPlaceholder = computed(() => {
144
- if (props.multiple) {
145
- const selectedValues = modelValue.value;
146
- return !selectedValues || selectedValues.length === 0;
147
- } else {
148
- const selectedOption = props.options?.find(
149
- (opt) => opt.value === modelValue.value
150
- );
151
- return !selectedOption;
127
+ const computedRules = computed(() => {
128
+ if (props.rules) return props.rules;
129
+ return () => {
130
+ if (props.required) {
131
+ if (props.multiple) {
132
+ const values = modelValue.value;
133
+ return values && values.length > 0 || `\u0E01\u0E23\u0E38\u0E13\u0E32\u0E40\u0E25\u0E37\u0E2D\u0E01 ${props.label || "\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23"}`;
134
+ } else {
135
+ return !!modelValue.value || `\u0E01\u0E23\u0E38\u0E13\u0E32\u0E40\u0E25\u0E37\u0E2D\u0E01 ${props.label || "\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23"}`;
136
+ }
137
+ }
138
+ return true;
139
+ };
140
+ });
141
+ onMounted(() => {
142
+ if (props.multiple && modelValue.value === "") {
143
+ modelValue.value = [];
152
144
  }
153
145
  });
154
146
  const isLimitReached = computed(() => {
155
147
  if (!props.multiple || props.limit <= 0) return false;
156
148
  return modelValue.value.length >= props.limit;
157
149
  });
150
+ const selectedCount = computed(() => {
151
+ if (props.multiple) {
152
+ return modelValue.value.length;
153
+ }
154
+ return modelValue.value ? 1 : 0;
155
+ });
156
+ const hasSelection = computed(() => {
157
+ if (props.multiple) {
158
+ return modelValue.value.length > 0;
159
+ }
160
+ return !!modelValue.value;
161
+ });
162
+ const handleInputClick = () => {
163
+ popoverOpen.value = true;
164
+ };
165
+ const handleIconClick = () => {
166
+ if (hasSelection.value) {
167
+ if (props.multiple) {
168
+ modelValue.value = [];
169
+ } else {
170
+ modelValue.value = "";
171
+ }
172
+ displayText.value = "";
173
+ }
174
+ popoverOpen.value = true;
175
+ };
158
176
  const isSelected = (value) => {
159
177
  if (props.multiple) {
160
- const result = modelValue.value.includes(value);
161
- return result;
178
+ return modelValue.value.includes(value);
162
179
  } else {
163
180
  return modelValue.value === value;
164
181
  }
@@ -189,19 +206,11 @@ const handleSelect = (value) => {
189
206
  popoverOpen.value = false;
190
207
  }
191
208
  };
192
- const validate = async () => {
193
- if (fieldRef.value) {
194
- const result = await fieldRef.value.validate();
195
- return result.valid;
196
- }
197
- return true;
198
- };
199
209
  const setErrors = (errMsg) => {
200
- fieldRef.value?.setErrors(errMsg);
210
+ inputTextFieldRef.value?.setErrors(errMsg);
201
211
  };
202
212
  defineExpose({
203
- validate,
204
213
  setErrors,
205
- fieldRef
214
+ inputTextFieldRef
206
215
  });
207
216
  </script>
@@ -19,16 +19,15 @@ type __VLS_ModelProps = {
19
19
  modelValue?: string | string[];
20
20
  };
21
21
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
22
- declare var __VLS_21: {}, __VLS_81: {};
22
+ declare var __VLS_25: {}, __VLS_67: {};
23
23
  type __VLS_Slots = {} & {
24
- label?: (props: typeof __VLS_21) => any;
24
+ label?: (props: typeof __VLS_25) => any;
25
25
  } & {
26
- options?: (props: typeof __VLS_81) => any;
26
+ options?: (props: typeof __VLS_67) => any;
27
27
  };
28
28
  declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
29
- validate: () => Promise<any>;
30
29
  setErrors: (errMsg: string[]) => void;
31
- fieldRef: import("vue").Ref<any, any>;
30
+ inputTextFieldRef: import("vue").Ref<any, any>;
32
31
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
33
32
  "update:modelValue": (value: string | string[]) => any;
34
33
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
@@ -1,10 +1,8 @@
1
1
  export interface LoadingProps {
2
- isLoading?: boolean;
3
- text?: string;
2
+ loading?: boolean;
4
3
  }
5
4
  declare const __VLS_export: import("vue").DefineComponent<LoadingProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<LoadingProps> & Readonly<{}>, {
6
- text: string;
7
- isLoading: boolean;
5
+ loading: boolean;
8
6
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
7
  declare const _default: typeof __VLS_export;
10
8
  export default _default;
@@ -1,21 +1,17 @@
1
1
  <template>
2
2
  <Transition name="loading-fade">
3
3
  <div
4
- v-if="isLoading"
5
- class="absolute inset-0 opacity-50 z-50 flex items-center justify-center backdrop-blur-2xl"
4
+ v-if="loading"
5
+ class="absolute inset-0 bg-white/50 flex items-center justify-center z-50"
6
6
  >
7
- <div class="flex flex-col items-center gap-3">
8
- <div class="loading-spinner"></div>
9
- <span v-if="text" class="text-sm text-cloud">{{ text }}</span>
10
- </div>
7
+ <div class="loading-spinner"></div>
11
8
  </div>
12
9
  </Transition>
13
10
  </template>
14
11
 
15
12
  <script setup>
16
13
  defineProps({
17
- isLoading: { type: Boolean, required: false, default: false },
18
- text: { type: String, required: false, default: "" }
14
+ loading: { type: Boolean, required: false, default: false }
19
15
  });
20
16
  </script>
21
17
 
@@ -1,10 +1,8 @@
1
1
  export interface LoadingProps {
2
- isLoading?: boolean;
3
- text?: string;
2
+ loading?: boolean;
4
3
  }
5
4
  declare const __VLS_export: import("vue").DefineComponent<LoadingProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<LoadingProps> & Readonly<{}>, {
6
- text: string;
7
- isLoading: boolean;
5
+ loading: boolean;
8
6
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
7
  declare const _default: typeof __VLS_export;
10
8
  export default _default;
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <Modal title="แชร์" width="396" v-model="isOpen">
2
+ <Modal title="แชร์" width="396" v-model="isOpen" :loading="true">
3
3
  <div class="flex justify-between">
4
4
  <div
5
5
  v-for="(share, i) of listShares"
@@ -5,7 +5,7 @@ export interface ModalProps {
5
5
  description?: string;
6
6
  footer?: string;
7
7
  disabledCloseBtn?: boolean;
8
- isLoading?: boolean;
8
+ loading?: boolean;
9
9
  loadingText?: string;
10
10
  }
11
11
  type __VLS_Props = ModalProps;
@@ -39,6 +39,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
39
39
  "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
40
40
  onClose?: (() => any) | undefined;
41
41
  }>, {
42
+ loading: boolean;
42
43
  disabledCloseBtn: boolean;
43
44
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
44
45
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -7,7 +7,7 @@
7
7
  @submit="onSubmit"
8
8
  @close="onClose"
9
9
  >
10
- <Loading :is-loading="props.isLoading" :text="props.loadingText" />
10
+ <Loading :loading="props.loading" :text="props.loadingText" />
11
11
  <ShadDialogHeader>
12
12
  <slot name="header">
13
13
  <ShadDialogTitle>
@@ -38,7 +38,7 @@ const props = defineProps({
38
38
  description: { type: String, required: false },
39
39
  footer: { type: String, required: false },
40
40
  disabledCloseBtn: { type: Boolean, required: false, default: false },
41
- isLoading: { type: Boolean, required: false },
41
+ loading: { type: Boolean, required: false, default: false },
42
42
  loadingText: { type: String, required: false }
43
43
  });
44
44
  const isOpen = defineModel({ type: Boolean, ...{
@@ -5,7 +5,7 @@ export interface ModalProps {
5
5
  description?: string;
6
6
  footer?: string;
7
7
  disabledCloseBtn?: boolean;
8
- isLoading?: boolean;
8
+ loading?: boolean;
9
9
  loadingText?: string;
10
10
  }
11
11
  type __VLS_Props = ModalProps;
@@ -39,6 +39,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
39
39
  "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
40
40
  onClose?: (() => any) | undefined;
41
41
  }>, {
42
+ loading: boolean;
42
43
  disabledCloseBtn: boolean;
43
44
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
44
45
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pukaad-ui-lib",
3
- "version": "1.38.0",
3
+ "version": "1.40.0",
4
4
  "description": "pukaad-ui for MeMSG",
5
5
  "repository": {
6
6
  "type": "git",