@nuxt/ui 2.8.1 → 2.10.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.
Files changed (120) hide show
  1. package/README.md +1 -0
  2. package/dist/module.d.mts +1411 -1092
  3. package/dist/module.d.ts +1411 -1092
  4. package/dist/module.json +1 -1
  5. package/dist/module.mjs +55 -11
  6. package/dist/runtime/components/data/Table.vue +47 -32
  7. package/dist/runtime/components/data/Table.vue.d.ts +22 -14
  8. package/dist/runtime/components/elements/Accordion.vue +33 -28
  9. package/dist/runtime/components/elements/Accordion.vue.d.ts +25 -18
  10. package/dist/runtime/components/elements/Alert.vue +22 -18
  11. package/dist/runtime/components/elements/Alert.vue.d.ts +26 -19
  12. package/dist/runtime/components/elements/Avatar.vue +22 -16
  13. package/dist/runtime/components/elements/Avatar.vue.d.ts +28 -19
  14. package/dist/runtime/components/elements/AvatarGroup.d.ts +17 -8
  15. package/dist/runtime/components/elements/AvatarGroup.mjs +18 -13
  16. package/dist/runtime/components/elements/Badge.vue +21 -17
  17. package/dist/runtime/components/elements/Badge.vue.d.ts +28 -19
  18. package/dist/runtime/components/elements/Button.vue +22 -18
  19. package/dist/runtime/components/elements/Button.vue.d.ts +28 -19
  20. package/dist/runtime/components/elements/ButtonGroup.d.ts +17 -8
  21. package/dist/runtime/components/elements/ButtonGroup.mjs +22 -15
  22. package/dist/runtime/components/elements/Dropdown.vue +45 -37
  23. package/dist/runtime/components/elements/Dropdown.vue.d.ts +22 -17
  24. package/dist/runtime/components/elements/Kbd.vue +16 -12
  25. package/dist/runtime/components/elements/Kbd.vue.d.ts +20 -12
  26. package/dist/runtime/components/elements/Link.vue +18 -5
  27. package/dist/runtime/components/elements/Meter.vue +166 -0
  28. package/dist/runtime/components/elements/Meter.vue.d.ts +123 -0
  29. package/dist/runtime/components/elements/MeterGroup.d.ts +78 -0
  30. package/dist/runtime/components/elements/MeterGroup.mjs +172 -0
  31. package/dist/runtime/components/elements/Progress.vue +284 -0
  32. package/dist/runtime/components/elements/Progress.vue.d.ts +2 -0
  33. package/dist/runtime/components/forms/Checkbox.vue +27 -17
  34. package/dist/runtime/components/forms/Checkbox.vue.d.ts +33 -15
  35. package/dist/runtime/components/forms/Form.vue +34 -9
  36. package/dist/runtime/components/forms/Form.vue.d.ts +2 -1
  37. package/dist/runtime/components/forms/FormGroup.vue +51 -27
  38. package/dist/runtime/components/forms/FormGroup.vue.d.ts +23 -15
  39. package/dist/runtime/components/forms/Input.vue +41 -29
  40. package/dist/runtime/components/forms/Input.vue.d.ts +51 -24
  41. package/dist/runtime/components/forms/Radio.vue +32 -21
  42. package/dist/runtime/components/forms/Radio.vue.d.ts +36 -18
  43. package/dist/runtime/components/forms/RadioGroup.vue +136 -0
  44. package/dist/runtime/components/forms/RadioGroup.vue.d.ts +118 -0
  45. package/dist/runtime/components/forms/Range.vue +26 -16
  46. package/dist/runtime/components/forms/Range.vue.d.ts +37 -18
  47. package/dist/runtime/components/forms/Select.vue +34 -26
  48. package/dist/runtime/components/forms/Select.vue.d.ts +42 -24
  49. package/dist/runtime/components/forms/SelectMenu.vue +104 -89
  50. package/dist/runtime/components/forms/SelectMenu.vue.d.ts +53 -33
  51. package/dist/runtime/components/forms/Textarea.vue +43 -31
  52. package/dist/runtime/components/forms/Textarea.vue.d.ts +50 -23
  53. package/dist/runtime/components/forms/Toggle.vue +26 -15
  54. package/dist/runtime/components/forms/Toggle.vue.d.ts +31 -12
  55. package/dist/runtime/components/layout/Card.vue +15 -10
  56. package/dist/runtime/components/layout/Card.vue.d.ts +17 -9
  57. package/dist/runtime/components/layout/Container.vue +15 -10
  58. package/dist/runtime/components/layout/Container.vue.d.ts +17 -9
  59. package/dist/runtime/components/layout/Divider.vue +107 -0
  60. package/dist/runtime/components/layout/Divider.vue.d.ts +82 -0
  61. package/dist/runtime/components/layout/Skeleton.vue +15 -10
  62. package/dist/runtime/components/layout/Skeleton.vue.d.ts +17 -9
  63. package/dist/runtime/components/navigation/CommandPalette.vue +57 -38
  64. package/dist/runtime/components/navigation/CommandPalette.vue.d.ts +24 -23
  65. package/dist/runtime/components/navigation/CommandPaletteGroup.vue +1 -1
  66. package/dist/runtime/components/navigation/CommandPaletteGroup.vue.d.ts +3 -4
  67. package/dist/runtime/components/navigation/Pagination.vue +129 -64
  68. package/dist/runtime/components/navigation/Pagination.vue.d.ts +70 -25
  69. package/dist/runtime/components/navigation/Tabs.vue +17 -14
  70. package/dist/runtime/components/navigation/Tabs.vue.d.ts +18 -11
  71. package/dist/runtime/components/navigation/VerticalNavigation.vue +15 -13
  72. package/dist/runtime/components/navigation/VerticalNavigation.vue.d.ts +20 -16
  73. package/dist/runtime/components/overlays/ContextMenu.vue +21 -11
  74. package/dist/runtime/components/overlays/ContextMenu.vue.d.ts +20 -11
  75. package/dist/runtime/components/overlays/Modal.vue +16 -14
  76. package/dist/runtime/components/overlays/Modal.vue.d.ts +18 -11
  77. package/dist/runtime/components/overlays/Notification.vue +25 -15
  78. package/dist/runtime/components/overlays/Notification.vue.d.ts +25 -19
  79. package/dist/runtime/components/overlays/Notifications.vue +19 -13
  80. package/dist/runtime/components/overlays/Notifications.vue.d.ts +22 -14
  81. package/dist/runtime/components/overlays/Popover.vue +39 -22
  82. package/dist/runtime/components/overlays/Popover.vue.d.ts +32 -11
  83. package/dist/runtime/components/overlays/Slideover.vue +15 -13
  84. package/dist/runtime/components/overlays/Slideover.vue.d.ts +18 -11
  85. package/dist/runtime/components/overlays/Tooltip.vue +31 -23
  86. package/dist/runtime/components/overlays/Tooltip.vue.d.ts +19 -11
  87. package/dist/runtime/composables/useFormGroup.d.ts +12 -2
  88. package/dist/runtime/composables/useFormGroup.mjs +21 -4
  89. package/dist/runtime/composables/usePopper.d.ts +3 -3
  90. package/dist/runtime/composables/usePopper.mjs +47 -31
  91. package/dist/runtime/composables/useToast.d.ts +3 -3
  92. package/dist/runtime/composables/useUI.d.ts +10 -0
  93. package/dist/runtime/composables/useUI.mjs +24 -0
  94. package/dist/runtime/types/avatar.d.ts +10 -4
  95. package/dist/runtime/types/badge.d.ts +14 -0
  96. package/dist/runtime/types/button.d.ts +10 -3
  97. package/dist/runtime/types/command-palette.d.ts +3 -1
  98. package/dist/runtime/types/form.d.ts +18 -4
  99. package/dist/runtime/types/index.d.ts +4 -0
  100. package/dist/runtime/types/link.d.ts +1 -0
  101. package/dist/runtime/types/meter.d.ts +5 -0
  102. package/dist/runtime/types/notification.d.ts +5 -3
  103. package/dist/runtime/types/popper.d.ts +1 -0
  104. package/dist/runtime/types/progress.d.ts +4 -0
  105. package/dist/runtime/types/tabs.d.ts +1 -0
  106. package/dist/runtime/types/utils.d.ts +11 -0
  107. package/dist/runtime/ui.config.d.ts +1470 -0
  108. package/dist/runtime/{app.config.mjs → ui.config.mjs} +313 -97
  109. package/dist/runtime/utils/index.d.ts +5 -11
  110. package/dist/runtime/utils/index.mjs +21 -9
  111. package/dist/runtime/utils/lodash.d.ts +2 -0
  112. package/dist/runtime/utils/lodash.mjs +23 -0
  113. package/dist/runtime/utils/uid.d.ts +1 -0
  114. package/dist/runtime/utils/uid.mjs +4 -0
  115. package/dist/types.d.mts +1 -1
  116. package/dist/types.d.ts +1 -1
  117. package/package.json +27 -29
  118. package/dist/runtime/app.config.d.ts +0 -1203
  119. package/dist/runtime/utils/StateEmitter.d.ts +0 -17
  120. package/dist/runtime/utils/StateEmitter.mjs +0 -21
@@ -4,7 +4,7 @@
4
4
  :model-value="modelValue"
5
5
  :multiple="multiple"
6
6
  :nullable="nullable"
7
- :class="wrapperClass"
7
+ :class="ui.wrapper"
8
8
  v-bind="attrs"
9
9
  as="div"
10
10
  @update:model-value="onSelect"
@@ -16,17 +16,12 @@
16
16
  :value="query"
17
17
  :class="[ui.input.base, ui.input.size, ui.input.height, ui.input.padding, icon && ui.input.icon.padding]"
18
18
  :placeholder="placeholder"
19
+ :aria-label="placeholder"
19
20
  autocomplete="off"
20
21
  @change="query = $event.target.value"
21
22
  />
22
23
 
23
- <UButton
24
- v-if="closeButton"
25
- v-bind="{ ...ui.default.closeButton, ...closeButton }"
26
- :class="ui.input.closeButton"
27
- aria-label="Close"
28
- @click="onClear"
29
- />
24
+ <UButton v-if="closeButton" aria-label="Close" v-bind="{ ...ui.default.closeButton, ...closeButton }" :class="ui.input.closeButton" @click="onClear" />
30
25
  </div>
31
26
 
32
27
  <HComboboxOptions
@@ -67,19 +62,20 @@
67
62
  </template>
68
63
 
69
64
  <script>
70
- import { ref, computed, watch, onMounted, defineComponent } from "vue";
65
+ import { ref, computed, watch, toRef, onMounted, defineComponent } from "vue";
71
66
  import { Combobox as HCombobox, ComboboxInput as HComboboxInput, ComboboxOptions as HComboboxOptions } from "@headlessui/vue";
72
67
  import { useDebounceFn } from "@vueuse/core";
73
68
  import { useFuse } from "@vueuse/integrations/useFuse";
74
- import { twMerge, twJoin } from "tailwind-merge";
75
- import { groupBy, map, omit } from "lodash-es";
69
+ import { twJoin } from "tailwind-merge";
76
70
  import { defu } from "defu";
77
71
  import UIcon from "../elements/Icon.vue";
78
72
  import UButton from "../elements/Button.vue";
79
73
  import CommandPaletteGroup from "./CommandPaletteGroup.vue";
80
- import { defuTwMerge } from "../../utils";
81
- import { useAppConfig } from "#imports";
74
+ import { useUI } from "../../composables/useUI";
75
+ import { mergeConfig } from "../../utils";
82
76
  import appConfig from "#build/app.config";
77
+ import { commandPalette } from "#ui/ui.config";
78
+ const config = mergeConfig(appConfig.ui.strategy, appConfig.ui.commandPalette, commandPalette);
83
79
  export default defineComponent({
84
80
  components: {
85
81
  HCombobox,
@@ -121,23 +117,23 @@ export default defineComponent({
121
117
  },
122
118
  icon: {
123
119
  type: String,
124
- default: () => appConfig.ui.commandPalette.default.icon
120
+ default: () => config.default.icon
125
121
  },
126
122
  loadingIcon: {
127
123
  type: String,
128
- default: () => appConfig.ui.commandPalette.default.loadingIcon
124
+ default: () => config.default.loadingIcon
129
125
  },
130
126
  selectedIcon: {
131
127
  type: String,
132
- default: () => appConfig.ui.commandPalette.default.selectedIcon
128
+ default: () => config.default.selectedIcon
133
129
  },
134
130
  closeButton: {
135
131
  type: Object,
136
- default: () => appConfig.ui.commandPalette.default.closeButton
132
+ default: () => config.default.closeButton
137
133
  },
138
134
  emptyState: {
139
135
  type: Object,
140
- default: () => appConfig.ui.commandPalette.default.emptyState
136
+ default: () => config.default.emptyState
141
137
  },
142
138
  placeholder: {
143
139
  type: String,
@@ -167,15 +163,18 @@ export default defineComponent({
167
163
  type: Object,
168
164
  default: () => ({})
169
165
  },
166
+ class: {
167
+ type: [String, Object, Array],
168
+ default: void 0
169
+ },
170
170
  ui: {
171
171
  type: Object,
172
- default: () => ({})
172
+ default: void 0
173
173
  }
174
174
  },
175
175
  emits: ["update:modelValue", "close"],
176
- setup(props, { emit, attrs, expose }) {
177
- const appConfig2 = useAppConfig();
178
- const ui = computed(() => defuTwMerge({}, props.ui, appConfig2.ui.commandPalette));
176
+ setup(props, { emit, expose }) {
177
+ const { ui, attrs } = useUI("commandPalette", toRef(props, "ui"), config, toRef(props, "class"));
179
178
  const query = ref("");
180
179
  const comboboxInput = ref();
181
180
  const comboboxApi = ref(null);
@@ -202,27 +201,49 @@ export default defineComponent({
202
201
  resultLimit: 12,
203
202
  matchAllWhenSearchEmpty: true
204
203
  }));
205
- const commands = computed(() => props.groups.filter((group) => !group.search).reduce((acc, group) => {
206
- return acc.concat(group.commands.map((command) => ({ ...command, group: group.key })));
207
- }, []));
204
+ const commands = computed(() => {
205
+ const commands2 = [];
206
+ for (const group of props.groups) {
207
+ if (!group.search) {
208
+ commands2.push(...group.commands.map((command) => ({ ...command, group: group.key })));
209
+ }
210
+ }
211
+ return commands2;
212
+ });
208
213
  const searchResults = ref({});
209
214
  const { results } = useFuse(query, commands, options);
210
- const groups = computed(() => [
211
- ...map(groupBy(results.value, (command) => command.item.group), (results2, key) => {
212
- const commands2 = results2.map((result) => {
215
+ const groups = computed(() => {
216
+ const groups2 = [];
217
+ const groupedCommands = {};
218
+ for (const command of results.value) {
219
+ groupedCommands[command.item.group] ||= [];
220
+ groupedCommands[command.item.group].push(command);
221
+ }
222
+ for (const key in groupedCommands) {
223
+ const group = props.groups.find((group2) => group2.key === key);
224
+ let commands2 = groupedCommands[key].map((result) => {
213
225
  const { item, ...data } = result;
214
226
  return {
215
227
  ...item,
216
228
  ...data
217
229
  };
218
230
  });
219
- return {
220
- ...props.groups.find((group) => group.key === key),
221
- commands: commands2.slice(0, options.value.resultLimit)
222
- };
223
- }),
224
- ...props.groups.filter((group) => !!group.search).map((group) => ({ ...group, commands: (searchResults.value[group.key] || []).slice(0, options.value.resultLimit) })).filter((group) => group.commands.length)
225
- ]);
231
+ if (group.filter && typeof group.filter === "function") {
232
+ commands2 = group.filter(query.value, commands2);
233
+ }
234
+ groups2.push({ ...group, commands: commands2.slice(0, options.value.resultLimit) });
235
+ }
236
+ for (const group of props.groups) {
237
+ if (group.search && searchResults.value[group.key]?.length) {
238
+ let commands2 = searchResults.value[group.key] || [];
239
+ if (group.filter && typeof group.filter === "function") {
240
+ commands2 = group.filter(query.value, commands2);
241
+ }
242
+ groups2.push({ ...group, commands: commands2.slice(0, options.value.resultLimit) });
243
+ }
244
+ }
245
+ return groups2;
246
+ });
226
247
  const debouncedSearch = useDebounceFn(async () => {
227
248
  const searchableGroups = props.groups.filter((group) => !!group.search);
228
249
  if (!searchableGroups.length) {
@@ -240,7 +261,6 @@ export default defineComponent({
240
261
  comboboxInput.value?.$el.dispatchEvent(new KeyboardEvent("keydown", { key: "PageUp" }));
241
262
  }, 0);
242
263
  });
243
- const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class));
244
264
  const iconName = computed(() => {
245
265
  if ((props.loading || isLoading.value) && props.loadingIcon) {
246
266
  return props.loadingIcon;
@@ -284,14 +304,13 @@ export default defineComponent({
284
304
  results
285
305
  });
286
306
  return {
287
- attrs: omit(attrs, ["class"]),
288
307
  // eslint-disable-next-line vue/no-dupe-keys
289
308
  ui,
309
+ attrs,
290
310
  // eslint-disable-next-line vue/no-dupe-keys
291
311
  groups,
292
312
  comboboxInput,
293
313
  query,
294
- wrapperClass,
295
314
  iconName,
296
315
  iconClass,
297
316
  // eslint-disable-next-line vue/no-dupe-keys
@@ -1,11 +1,9 @@
1
- /// <reference types="lodash" />
2
1
  import type { ComputedRef, PropType, ComponentPublicInstance } from 'vue';
3
2
  import type { UseFuseOptions } from '@vueuse/integrations/useFuse';
4
- import type { Group, Command } from '../../types/command-palette';
5
- import type { Button } from '../../types/button';
3
+ import type { Group, Command, Button } from '../../types';
6
4
  declare const _default: import("vue").DefineComponent<{
7
5
  modelValue: {
8
- type: (ObjectConstructor | ArrayConstructor | StringConstructor | NumberConstructor)[];
6
+ type: (ObjectConstructor | NumberConstructor | ArrayConstructor | StringConstructor)[];
9
7
  default: null;
10
8
  };
11
9
  by: {
@@ -46,7 +44,7 @@ declare const _default: import("vue").DefineComponent<{
46
44
  };
47
45
  closeButton: {
48
46
  type: PropType<Button>;
49
- default: () => any;
47
+ default: () => Button;
50
48
  };
51
49
  emptyState: {
52
50
  type: PropType<{
@@ -84,24 +82,22 @@ declare const _default: import("vue").DefineComponent<{
84
82
  type: PropType<UseFuseOptions<Command>>;
85
83
  default: () => {};
86
84
  };
87
- ui: {
85
+ class: {
88
86
  type: PropType<any>;
89
- default: () => {};
87
+ default: undefined;
88
+ };
89
+ ui: {
90
+ type: PropType<Partial<any>>;
91
+ default: undefined;
90
92
  };
91
93
  }, {
92
- attrs: import("lodash").Omit<{
93
- [x: string]: unknown;
94
- }, "class">;
95
94
  ui: ComputedRef<any>;
96
- groups: ComputedRef<{
97
- commands: any;
98
- key: string;
99
- active?: string | undefined;
100
- inactive?: string | undefined;
101
- }[]>;
95
+ attrs: ComputedRef<Pick<{
96
+ [x: string]: unknown;
97
+ }, string>>;
98
+ groups: ComputedRef<Group[]>;
102
99
  comboboxInput: import("vue").Ref<ComponentPublicInstance<HTMLInputElement> | undefined>;
103
100
  query: import("vue").Ref<string>;
104
- wrapperClass: ComputedRef<string>;
105
101
  iconName: ComputedRef<string | undefined>;
106
102
  iconClass: ComputedRef<string>;
107
103
  emptyState: ComputedRef<any>;
@@ -109,7 +105,7 @@ declare const _default: import("vue").DefineComponent<{
109
105
  onClear: () => void;
110
106
  }, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("close" | "update:modelValue")[], "close" | "update:modelValue", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
111
107
  modelValue: {
112
- type: (ObjectConstructor | ArrayConstructor | StringConstructor | NumberConstructor)[];
108
+ type: (ObjectConstructor | NumberConstructor | ArrayConstructor | StringConstructor)[];
113
109
  default: null;
114
110
  };
115
111
  by: {
@@ -150,7 +146,7 @@ declare const _default: import("vue").DefineComponent<{
150
146
  };
151
147
  closeButton: {
152
148
  type: PropType<Button>;
153
- default: () => any;
149
+ default: () => Button;
154
150
  };
155
151
  emptyState: {
156
152
  type: PropType<{
@@ -188,17 +184,22 @@ declare const _default: import("vue").DefineComponent<{
188
184
  type: PropType<UseFuseOptions<Command>>;
189
185
  default: () => {};
190
186
  };
191
- ui: {
187
+ class: {
192
188
  type: PropType<any>;
193
- default: () => {};
189
+ default: undefined;
190
+ };
191
+ ui: {
192
+ type: PropType<Partial<any>>;
193
+ default: undefined;
194
194
  };
195
195
  }>> & {
196
- onClose?: ((...args: any[]) => any) | undefined;
197
196
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
197
+ onClose?: ((...args: any[]) => any) | undefined;
198
198
  }, {
199
199
  icon: string;
200
200
  closeButton: Button;
201
- ui: any;
201
+ ui: Partial<any>;
202
+ class: any;
202
203
  modelValue: string | number | unknown[] | Record<string, any>;
203
204
  by: string;
204
205
  loading: boolean;
@@ -105,7 +105,7 @@ export default defineComponent({
105
105
  },
106
106
  ui: {
107
107
  type: Object,
108
- default: () => ({})
108
+ required: true
109
109
  }
110
110
  },
111
111
  setup(props) {
@@ -1,5 +1,5 @@
1
1
  import type { PropType } from 'vue';
2
- import type { Group } from '../../types/command-palette';
2
+ import type { Group } from '../../types';
3
3
  declare const _default: import("vue").DefineComponent<{
4
4
  group: {
5
5
  type: PropType<Group>;
@@ -23,7 +23,7 @@ declare const _default: import("vue").DefineComponent<{
23
23
  };
24
24
  ui: {
25
25
  type: PropType<any>;
26
- default: () => {};
26
+ required: true;
27
27
  };
28
28
  }, {
29
29
  label: import("vue").ComputedRef<any>;
@@ -54,10 +54,9 @@ declare const _default: import("vue").DefineComponent<{
54
54
  };
55
55
  ui: {
56
56
  type: PropType<any>;
57
- default: () => {};
57
+ required: true;
58
58
  };
59
59
  }>>, {
60
- ui: any;
61
60
  query: string;
62
61
  }, {}>;
63
62
  export default _default;
@@ -1,13 +1,27 @@
1
1
  <template>
2
- <div :class="wrapperClass" v-bind="attrs">
2
+ <div :class="ui.wrapper" v-bind="attrs">
3
+ <slot name="first" :on-click="onClickFirst">
4
+ <UButton
5
+ v-if="firstButton && showFirst"
6
+ :size="size"
7
+ :disabled="!canGoFirstOrPrev"
8
+ :class="[ui.base, ui.rounded]"
9
+ v-bind="{ ...ui.default.firstButton, ...firstButton }"
10
+ :ui="{ rounded: '' }"
11
+ aria-label="First"
12
+ @click="onClickFirst"
13
+ />
14
+ </slot>
15
+
3
16
  <slot name="prev" :on-click="onClickPrev">
4
17
  <UButton
5
18
  v-if="prevButton"
6
19
  :size="size"
7
- :disabled="!canGoPrev"
20
+ :disabled="!canGoFirstOrPrev"
8
21
  :class="[ui.base, ui.rounded]"
9
22
  v-bind="{ ...ui.default.prevButton, ...prevButton }"
10
23
  :ui="{ rounded: '' }"
24
+ aria-label="Prev"
11
25
  @click="onClickPrev"
12
26
  />
13
27
  </slot>
@@ -27,24 +41,39 @@
27
41
  <UButton
28
42
  v-if="nextButton"
29
43
  :size="size"
30
- :disabled="!canGoNext"
44
+ :disabled="!canGoLastOrNext"
31
45
  :class="[ui.base, ui.rounded]"
32
46
  v-bind="{ ...ui.default.nextButton, ...nextButton }"
33
47
  :ui="{ rounded: '' }"
48
+ aria-label="Next"
34
49
  @click="onClickNext"
35
50
  />
36
51
  </slot>
52
+
53
+ <slot name="last" :on-click="onClickLast">
54
+ <UButton
55
+ v-if="lastButton && showLast"
56
+ :size="size"
57
+ :disabled="!canGoLastOrNext"
58
+ :class="[ui.base, ui.rounded]"
59
+ v-bind="{ ...ui.default.lastButton, ...lastButton }"
60
+ :ui="{ rounded: '' }"
61
+ aria-label="Last"
62
+ @click="onClickLast"
63
+ />
64
+ </slot>
37
65
  </div>
38
66
  </template>
39
67
 
40
68
  <script>
41
- import { computed, defineComponent } from "vue";
42
- import { omit } from "lodash-es";
43
- import { twMerge } from "tailwind-merge";
69
+ import { computed, toRef, defineComponent } from "vue";
44
70
  import UButton from "../elements/Button.vue";
45
- import { defuTwMerge } from "../../utils";
46
- import { useAppConfig } from "#imports";
71
+ import { useUI } from "../../composables/useUI";
72
+ import { mergeConfig } from "../../utils";
47
73
  import appConfig from "#build/app.config";
74
+ import { pagination, button } from "#ui/ui.config";
75
+ const config = mergeConfig(appConfig.ui.strategy, appConfig.ui.pagination, pagination);
76
+ const buttonConfig = mergeConfig(appConfig.ui.strategy, appConfig.ui.button, button);
48
77
  export default defineComponent({
49
78
  components: {
50
79
  UButton
@@ -67,45 +96,64 @@ export default defineComponent({
67
96
  type: Number,
68
97
  default: 7,
69
98
  validate(value) {
70
- return value >= 7 && value < Number.MAX_VALUE;
99
+ return value >= 5 && value < Number.MAX_VALUE;
71
100
  }
72
101
  },
73
102
  size: {
74
103
  type: String,
75
- default: () => appConfig.ui.pagination.default.size,
104
+ default: () => config.default.size,
76
105
  validator(value) {
77
- return Object.keys(appConfig.ui.button.size).includes(value);
106
+ return Object.keys(buttonConfig.size).includes(value);
78
107
  }
79
108
  },
80
109
  activeButton: {
81
110
  type: Object,
82
- default: () => appConfig.ui.pagination.default.activeButton
111
+ default: () => config.default.activeButton
83
112
  },
84
113
  inactiveButton: {
85
114
  type: Object,
86
- default: () => appConfig.ui.pagination.default.inactiveButton
115
+ default: () => config.default.inactiveButton
116
+ },
117
+ showFirst: {
118
+ type: Boolean,
119
+ default: false
120
+ },
121
+ showLast: {
122
+ type: Boolean,
123
+ default: false
124
+ },
125
+ firstButton: {
126
+ type: Object,
127
+ default: () => config.default.firstButton
128
+ },
129
+ lastButton: {
130
+ type: Object,
131
+ default: () => config.default.lastButton
87
132
  },
88
133
  prevButton: {
89
134
  type: Object,
90
- default: () => appConfig.ui.pagination.default.prevButton
135
+ default: () => config.default.prevButton
91
136
  },
92
137
  nextButton: {
93
138
  type: Object,
94
- default: () => appConfig.ui.pagination.default.nextButton
139
+ default: () => config.default.nextButton
95
140
  },
96
141
  divider: {
97
142
  type: String,
98
143
  default: "\u2026"
99
144
  },
145
+ class: {
146
+ type: [String, Object, Array],
147
+ default: void 0
148
+ },
100
149
  ui: {
101
150
  type: Object,
102
- default: () => ({})
151
+ default: void 0
103
152
  }
104
153
  },
105
154
  emits: ["update:modelValue"],
106
- setup(props, { attrs, emit }) {
107
- const appConfig2 = useAppConfig();
108
- const ui = computed(() => defuTwMerge({}, props.ui, appConfig2.ui.pagination));
155
+ setup(props, { emit }) {
156
+ const { ui, attrs } = useUI("pagination", toRef(props, "ui"), config, toRef(props, "class"));
109
157
  const currentPage = computed({
110
158
  get() {
111
159
  return props.modelValue;
@@ -116,50 +164,66 @@ export default defineComponent({
116
164
  });
117
165
  const pages = computed(() => Array.from({ length: Math.ceil(props.total / props.pageCount) }, (_, i) => i + 1));
118
166
  const displayedPages = computed(() => {
119
- if (!props.max || pages.value.length <= 5) {
120
- return pages.value;
121
- } else {
122
- const current = currentPage.value;
123
- const max = pages.value.length;
124
- const r = Math.floor((Math.min(props.max, max) - 5) / 2);
125
- const r1 = current - r;
126
- const r2 = current + r;
127
- const beforeWrapped = r1 - 1 > 1;
128
- const afterWrapped = r2 + 1 < max;
129
- const items = [1];
130
- if (beforeWrapped)
131
- items.push(props.divider);
132
- if (!afterWrapped) {
133
- const addedItems = current + r + 2 - max;
134
- for (let i = current - r - addedItems; i <= current - r - 1; i++) {
135
- items.push(i);
136
- }
137
- }
138
- for (let i = r1 > 2 ? r1 : 2; i <= Math.min(max, r2); i++) {
167
+ const totalPages = pages.value.length;
168
+ const current = currentPage.value;
169
+ const maxDisplayedPages = Math.max(props.max, 5);
170
+ const r = Math.floor((Math.min(maxDisplayedPages, totalPages) - 5) / 2);
171
+ const r1 = current - r;
172
+ const r2 = current + r;
173
+ const beforeWrapped = r1 - 1 > 1;
174
+ const afterWrapped = r2 + 1 < totalPages;
175
+ const items = [];
176
+ if (totalPages <= maxDisplayedPages) {
177
+ for (let i = 1; i <= totalPages; i++) {
139
178
  items.push(i);
140
179
  }
141
- if (!beforeWrapped) {
142
- const addedItems = 1 - (current - r - 2);
143
- for (let i = current + r + 1; i <= current + r + addedItems; i++) {
144
- items.push(i);
145
- }
146
- }
147
- if (afterWrapped)
148
- items.push(props.divider);
149
- if (r2 < max)
150
- items.push(max);
151
- if (items.length >= 3 && items[1] === props.divider && items[2] === 3) {
152
- items[1] = 2;
180
+ return items;
181
+ }
182
+ items.push(1);
183
+ if (beforeWrapped)
184
+ items.push(props.divider);
185
+ if (!afterWrapped) {
186
+ const addedItems = current + r + 2 - totalPages;
187
+ for (let i = current - r - addedItems; i <= current - r - 1; i++) {
188
+ items.push(i);
153
189
  }
154
- if (items.length >= 3 && items[items.length - 2] === props.divider && items[items.length - 1] === items.length) {
155
- items[items.length - 2] = items.length - 1;
190
+ }
191
+ for (let i = Math.max(2, r1); i <= Math.min(totalPages, r2); i++) {
192
+ items.push(i);
193
+ }
194
+ if (!beforeWrapped) {
195
+ const addedItems = 1 - (current - r - 2);
196
+ for (let i = current + r + 1; i <= current + r + addedItems; i++) {
197
+ items.push(i);
156
198
  }
157
- return items;
158
199
  }
200
+ if (afterWrapped)
201
+ items.push(props.divider);
202
+ if (r2 < totalPages) {
203
+ items.push(totalPages);
204
+ }
205
+ if (items.length >= 3 && items[1] === props.divider && items[2] === 3) {
206
+ items[1] = 2;
207
+ }
208
+ if (items.length >= 3 && items[items.length - 2] === props.divider && items[items.length - 1] === items.length) {
209
+ items[items.length - 2] = items.length - 1;
210
+ }
211
+ return items;
159
212
  });
160
- const canGoPrev = computed(() => currentPage.value > 1);
161
- const canGoNext = computed(() => currentPage.value < pages.value.length);
162
- const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class));
213
+ const canGoFirstOrPrev = computed(() => currentPage.value > 1);
214
+ const canGoLastOrNext = computed(() => currentPage.value < pages.value.length);
215
+ function onClickFirst() {
216
+ if (!canGoFirstOrPrev.value) {
217
+ return;
218
+ }
219
+ currentPage.value = 1;
220
+ }
221
+ function onClickLast() {
222
+ if (!canGoLastOrNext.value) {
223
+ return;
224
+ }
225
+ currentPage.value = pages.value.length;
226
+ }
163
227
  function onClickPage(page) {
164
228
  if (typeof page === "string") {
165
229
  return;
@@ -167,30 +231,31 @@ export default defineComponent({
167
231
  currentPage.value = page;
168
232
  }
169
233
  function onClickPrev() {
170
- if (!canGoPrev.value) {
234
+ if (!canGoFirstOrPrev.value) {
171
235
  return;
172
236
  }
173
237
  currentPage.value--;
174
238
  }
175
239
  function onClickNext() {
176
- if (!canGoNext.value) {
240
+ if (!canGoLastOrNext.value) {
177
241
  return;
178
242
  }
179
243
  currentPage.value++;
180
244
  }
181
245
  return {
182
- attrs: omit(attrs, ["class"]),
183
246
  // eslint-disable-next-line vue/no-dupe-keys
184
247
  ui,
248
+ attrs,
185
249
  currentPage,
186
250
  pages,
187
251
  displayedPages,
188
- canGoPrev,
189
- canGoNext,
190
- wrapperClass,
252
+ canGoLastOrNext,
253
+ canGoFirstOrPrev,
191
254
  onClickPrev,
192
255
  onClickNext,
193
- onClickPage
256
+ onClickPage,
257
+ onClickFirst,
258
+ onClickLast
194
259
  };
195
260
  }
196
261
  });