pxd 0.0.33 → 0.0.35

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 (57) hide show
  1. package/dist/components/active-graph/index.vue +7 -7
  2. package/dist/components/badge/index.vue +1 -1
  3. package/dist/components/browser/index.vue +1 -1
  4. package/dist/components/button/index.vue +2 -2
  5. package/dist/components/carousel-group/index.vue +1 -1
  6. package/dist/components/choicebox/index.vue +1 -1
  7. package/dist/components/choicebox-group/index.vue +1 -1
  8. package/dist/components/config-provider/index.vue +1 -1
  9. package/dist/components/countdown/index.vue +2 -2
  10. package/dist/components/drawer/index.vue +84 -63
  11. package/dist/components/drawer/index.vue.d.ts +9 -8
  12. package/dist/components/list/index.vue +18 -20
  13. package/dist/components/list-item/index.vue +28 -47
  14. package/dist/components/list-item/index.vue.d.ts +11 -9
  15. package/dist/components/material/index.vue +1 -1
  16. package/dist/components/message/index.vue +144 -23
  17. package/dist/components/message/index.vue.d.ts +62 -1
  18. package/dist/components/modal/index.vue +12 -10
  19. package/dist/components/modal/index.vue.d.ts +7 -6
  20. package/dist/components/note/index.vue +1 -1
  21. package/dist/components/overlay/index.vue +32 -13
  22. package/dist/components/overlay/index.vue.d.ts +1 -0
  23. package/dist/components/popover/index.vue +47 -61
  24. package/dist/components/popover/index.vue.d.ts +2 -0
  25. package/dist/components/resizable/index.vue +2 -2
  26. package/dist/components/scrollable/index.vue +8 -5
  27. package/dist/components/snippet/index.vue +2 -2
  28. package/dist/components/stack/index.vue +1 -1
  29. package/dist/components/status-dot/index.vue +5 -5
  30. package/dist/components/teleport/index.vue +36 -34
  31. package/dist/components/text/index.vue +1 -1
  32. package/dist/components/theme-switcher/index.vue +1 -1
  33. package/dist/components/tooltip/index.vue +1 -1
  34. package/dist/composables/use-color-scheme.js +5 -1
  35. package/dist/composables/use-countdown.d.ts +1 -1
  36. package/dist/composables/use-countdown.js +2 -2
  37. package/dist/composables/use-delay-change.d.ts +3 -3
  38. package/dist/composables/use-delay-change.js +8 -8
  39. package/dist/composables/use-focus-trap.d.ts +2 -3
  40. package/dist/composables/use-focus-trap.js +7 -8
  41. package/dist/composables/use-media-query.js +4 -1
  42. package/dist/composables/use-message.d.ts +4 -37
  43. package/dist/composables/use-message.js +8 -140
  44. package/dist/composables/use-pointer-gesture.d.ts +9 -3
  45. package/dist/composables/use-pointer-gesture.js +25 -21
  46. package/dist/composables/use-virtual-list.d.ts +1 -0
  47. package/dist/composables/use-virtual-list.js +28 -21
  48. package/dist/contexts/list.d.ts +1 -0
  49. package/dist/contexts/list.js +4 -0
  50. package/dist/index.d.ts +1 -1
  51. package/dist/index.js +1 -1
  52. package/dist/styles/styles.css +1 -1
  53. package/dist/styles/tw.css +8 -3
  54. package/dist/types/components/list.d.ts +2 -1
  55. package/dist/types/shared/utils.d.ts +3 -0
  56. package/dist/utils/format.js +1 -1
  57. package/package.json +1 -1
@@ -206,8 +206,8 @@ let tbodyRect;
206
206
  const tbodyRef = shallowRef();
207
207
  const {
208
208
  value: showTooltip,
209
- set: setShowTooltip,
210
- setImmediate: setShowTooltipImmediate
209
+ setValue: setShowTooltip,
210
+ setValueDelay: setShowTooltipDelay
211
211
  } = useDelayChange(false, 500);
212
212
  const tooltipInfo = shallowRef({});
213
213
  const formatTooltipText = computed(() => {
@@ -218,25 +218,25 @@ const formatTooltipText = computed(() => {
218
218
  return "";
219
219
  });
220
220
  function onMouseLeave() {
221
- setShowTooltipImmediate(false);
221
+ setShowTooltip(false);
222
222
  tooltipInfo.value = {};
223
223
  tbodyRect = null;
224
224
  }
225
225
  async function onMouseOver(ev) {
226
226
  const targetEl = ev.target;
227
227
  if (targetEl.tagName !== "TD") {
228
- setShowTooltip(false);
228
+ setShowTooltipDelay(false);
229
229
  return;
230
230
  }
231
231
  const date = targetEl.dataset.date;
232
232
  if (!date) {
233
- setShowTooltipImmediate(false);
233
+ setShowTooltip(false);
234
234
  return;
235
235
  }
236
236
  if (!tbodyRect) {
237
237
  tbodyRect = tbodyRef.value.getBoundingClientRect();
238
238
  }
239
- setShowTooltipImmediate(true);
239
+ setShowTooltip(true);
240
240
  const rect = targetEl.getBoundingClientRect();
241
241
  let top = rect.top - tbodyRect.top - CELL_SIZE;
242
242
  if (props.graphOnly) {
@@ -322,7 +322,7 @@ onBeforeUnmount(() => {
322
322
  </tbody>
323
323
  </table>
324
324
 
325
- <Transition name="pxd-transition--fade">
325
+ <Transition name="pxd-transition--fade" mode="out-in" appear>
326
326
  <div
327
327
  v-if="showTooltip"
328
328
  class="pxd-active-graph--tooltip left-0 top-0 px-2 py-1 pointer-events-none absolute z-1 w-max rounded-sm bg-gray-1000 text-[13px] text-gray-100 duration-50 will-change-transform motion-safe:transition-transform"
@@ -64,7 +64,7 @@ const badgeAttrs = computed(() => {
64
64
  </script>
65
65
 
66
66
  <template>
67
- <component :is="as" :class="computedClass" v-bind="badgeAttrs">
67
+ <Component :is="as" :class="computedClass" v-bind="badgeAttrs">
68
68
  <slot />
69
69
  </component>
70
70
  </template>
@@ -42,7 +42,7 @@ const { isCopied, copyText } = useCopyClick();
42
42
 
43
43
  <PButton variant="ghost" size="xs" shape="rounded" class="size-6" icon @click="copyText(address)">
44
44
  <Transition name="pxd-transition--fade-scale" mode="out-in">
45
- <component :is="isCopied ? CheckIcon : CopyIcon" class="text-sm text-foreground-secondary" />
45
+ <Component :is="isCopied ? CheckIcon : CopyIcon" class="text-sm text-foreground-secondary" />
46
46
  </Transition>
47
47
  </PButton>
48
48
  </div>
@@ -54,7 +54,7 @@ const ALIGNMENTS = {
54
54
  center: "justify-center",
55
55
  right: "justify-end"
56
56
  };
57
- const DISABLED_CLASS_NAMES = "is-disabled bg-gray-100 hover:bg-gray-100 active:bg-gray-100 disabled:cursor-not-allowed text-gray-700 border-gray-300";
57
+ const DISABLED_CLASS_NAMES = "is-disabled disabled:bg-gray-100 hover:bg-gray-100 active:bg-gray-100 disabled:cursor-not-allowed disabled:text-gray-700 disabled:border-gray-300";
58
58
  const config = useConfigProvider();
59
59
  const computedDisabled = computed(() => isTruthyProp(props.disabled) || isTruthyProp(props.loading));
60
60
  const computedClass = computed(() => {
@@ -89,7 +89,7 @@ function onButtonDblClick(event) {
89
89
  </script>
90
90
 
91
91
  <template>
92
- <component
92
+ <Component
93
93
  :is="as"
94
94
  role="button"
95
95
  :class="computedClass"
@@ -29,7 +29,7 @@ let autoPlayRafId = null;
29
29
  let autoPlaySession = 0;
30
30
  let isPointerEntering = false;
31
31
  const carousels = ref([]);
32
- const sliderRef = shallowRef(null);
32
+ const sliderRef = shallowRef();
33
33
  const virtualIndex = shallowRef(props.index);
34
34
  const correctIndex = computed(() => {
35
35
  const index = virtualIndex.value;
@@ -28,7 +28,7 @@ const computedAttrs = computed(() => {
28
28
  </script>
29
29
 
30
30
  <template>
31
- <component :is="renderComponent" v-model="choiceboxModelValue" v-bind="computedAttrs">
31
+ <Component :is="renderComponent" v-model="choiceboxModelValue" v-bind="computedAttrs">
32
32
  <div class="gap-1 flex flex-col">
33
33
  <span class="pxd-choicebox--label font-medium">
34
34
  <slot name="label">
@@ -52,7 +52,7 @@ provideChoiceboxGroupModelValue(modelValue);
52
52
  </slot>
53
53
  </div>
54
54
 
55
- <component
55
+ <Component
56
56
  :is="renderComponent"
57
57
  v-model="modelValue"
58
58
  v-bind="computedAttrs"
@@ -13,7 +13,7 @@ provideConfigProvider(props);
13
13
  </script>
14
14
 
15
15
  <template>
16
- <component :is="as" class="pxd-config-provider">
16
+ <Component :is="as" class="pxd-config-provider">
17
17
  <slot />
18
18
  </component>
19
19
  </template>
@@ -21,7 +21,7 @@ const emits = defineEmits(["change", "reset", "finish"]);
21
21
  dayjs.extend(durationPlugin);
22
22
  dayjs.extend(millisecondTokenPlugin);
23
23
  const {
24
- clean,
24
+ stop,
25
25
  reset,
26
26
  timestamp
27
27
  } = useCountdown(props, emits);
@@ -40,7 +40,7 @@ const displayTimes = computed(() => {
40
40
  return time;
41
41
  });
42
42
  onBeforeUnmount(() => {
43
- clean();
43
+ stop();
44
44
  });
45
45
  defineExpose({
46
46
  reset,
@@ -14,65 +14,50 @@ defineOptions({
14
14
  }
15
15
  });
16
16
  const props = defineProps({
17
- size: { type: [Number, String], required: false, default: "30%" },
18
17
  title: { type: [String, Number, Array, null], required: false },
19
18
  subtitle: { type: [String, Number, Array, null], required: false },
19
+ size: { type: [Number, String], required: false },
20
+ pending: { type: Boolean, required: false },
21
+ position: { type: String, required: false, default: "right" },
20
22
  modelValue: { type: Boolean, required: false, default: false },
21
- headerStyle: { type: Boolean, required: false, default: false },
22
- footerStyle: { type: Boolean, required: false, default: true },
23
23
  appendToBody: { type: Boolean, required: false, default: true },
24
+ headerStylize: { type: Boolean, required: false, default: false },
25
+ footerStylize: { type: Boolean, required: false, default: true },
26
+ drawerClass: { type: [String, Array, Object], required: false },
24
27
  closeOnPressEscape: { type: Boolean, required: false, default: true },
25
- closeOnClickOverlay: { type: Boolean, required: false, default: true },
26
- position: { type: String, required: false, default: "right" }
28
+ closeOnClickOverlay: { type: Boolean, required: false, default: true }
27
29
  });
28
30
  const emits = defineEmits(["open", "close", "click-outside", "update:modelValue"]);
29
31
  const drawerRef = shallowRef();
30
32
  const isVisible = useModelValue(props, emits);
31
33
  useFocusTrap(drawerRef);
32
- const ensureCorrectPosition = computed(() => {
34
+ const ensurePosition = computed(() => {
33
35
  const { position } = props;
34
36
  if (["top", "bottom", "left", "right"].includes(position)) {
35
37
  return position;
36
38
  }
37
39
  return "right";
38
40
  });
39
- const transitionName = computed(() => {
40
- return `pxd-transition--drawer-slide-${ensureCorrectPosition.value}`;
41
- });
42
- const computedClass = computed(() => {
43
- const classes = ["pxd-drawer translate-z-0 fixed z-10 flex flex-col bg-background-100 shadow-border-modal outline-none"];
44
- switch (ensureCorrectPosition.value) {
45
- case "top":
46
- classes.push("top-0", "left-0", "right-0");
47
- break;
48
- case "right":
49
- classes.push("top-0", "right-0", "bottom-0");
50
- break;
51
- case "bottom":
52
- classes.push("bottom-0", "left-0", "right-0");
53
- break;
54
- case "left":
55
- classes.push("top-0", "left-0", "bottom-0");
56
- break;
57
- }
58
- return classes.join(" ");
59
- });
41
+ const transitionName = computed(() => `pxd-transition--drawer-${ensurePosition.value}`);
60
42
  const computedStyle = computed(() => {
61
- const style = {};
62
- const sizeField = ["left", "right"].includes(ensureCorrectPosition.value) ? "width" : "height";
63
- style[sizeField] = getCssUnitValue(props.size);
64
- return style;
43
+ const styles = {};
44
+ if (props.size) {
45
+ styles["--size"] = getCssUnitValue(props.size);
46
+ }
47
+ return styles;
65
48
  });
66
49
  function onOverlayClick(ev) {
67
50
  emits("click-outside", ev);
68
- if (!props.closeOnClickOverlay) {
51
+ if (!props.closeOnClickOverlay || props.pending) {
69
52
  return;
70
53
  }
71
- closeDrawer();
72
- }
73
- function closeDrawer() {
74
54
  isVisible.value = false;
75
- emits("close");
55
+ }
56
+ function onUpdateModelValue(visible) {
57
+ if (!visible && props.pending) {
58
+ return;
59
+ }
60
+ isVisible.value = visible;
76
61
  }
77
62
  watch(() => isVisible.value, (visible) => {
78
63
  if (visible) {
@@ -85,24 +70,27 @@ watch(() => isVisible.value, (visible) => {
85
70
 
86
71
  <template>
87
72
  <POverlay
88
- v-model="isVisible"
73
+ :model-value="isVisible"
89
74
  :append-to-body="appendToBody"
90
75
  :close-on-press-escape="closeOnPressEscape"
76
+ :update:model-value="onUpdateModelValue"
91
77
  @click="onOverlayClick"
92
78
  >
93
- <Transition :name="transitionName" mode="out-in">
79
+ <Transition :name="transitionName" mode="out-in" appear>
94
80
  <div
95
81
  v-if="isVisible"
96
82
  ref="drawerRef"
97
83
  aria-modal="true"
98
84
  role="dialog"
99
85
  tabindex="-1"
100
- :class="computedClass"
86
+ class="pxd-drawer translate-z-0 sm:[--w:30vw] sm:[--h:30vw] fixed z-10 flex max-h-full max-w-full flex-col bg-background-100 shadow-border-modal outline-none"
87
+ :class="drawerClass"
101
88
  :style="computedStyle"
89
+ :data-position="ensurePosition"
102
90
  >
103
91
  <header
104
- class="pxd-drawer--header p-6 sm:py-4 relative shrink-0 empty:py-3"
105
- :class="{ 'border-b bg-background-200 dark:bg-background-100': headerStyle }"
92
+ class="pxd-drawer--header p-6 sm:pb-4 relative shrink-0 empty:py-3"
93
+ :class="{ 'sm:pt-4 border-b bg-background-200 dark:bg-background-100': headerStylize }"
106
94
  >
107
95
  <h3 v-if="$slots.title || title" class="text-xl font-semibold tracking-tight">
108
96
  <slot name="title">
@@ -119,17 +107,18 @@ watch(() => isVisible.value, (visible) => {
119
107
 
120
108
  <PScrollable
121
109
  v-if="$slots.default"
122
- :data-header="headerStyle"
110
+ :data-header="headerStylize"
123
111
  class="pxd-drawer--content group flex-1"
124
112
  content-class="group-data-[header=true]:pt-5 px-6 pb-5"
125
113
  >
126
114
  <slot />
127
115
  </PScrollable>
116
+ <div v-else class="flex-1" />
128
117
 
129
118
  <footer
130
119
  v-if="$slots.footer"
131
120
  class="pxd-drawer--footer p-4 gap-2 relative flex shrink-0 items-center justify-between"
132
- :class="{ 'border-t bg-background-200 dark:bg-background-100': footerStyle }"
121
+ :class="{ 'border-t bg-background-200 dark:bg-background-100': footerStylize }"
133
122
  >
134
123
  <slot name="footer" />
135
124
  </footer>
@@ -139,38 +128,70 @@ watch(() => isVisible.value, (visible) => {
139
128
  </template>
140
129
 
141
130
  <style>
142
- /* 右侧滑入滑出动画 */
143
- .pxd-transition--drawer-slide-right-enter-active,
144
- .pxd-transition--drawer-slide-right-leave-active,
145
- /* 左侧滑入滑出动画 */
146
- .pxd-transition--drawer-slide-left-enter-active,
147
- .pxd-transition--drawer-slide-left-leave-active,
148
- /* 顶部滑入滑出动画 */
149
- .pxd-transition--drawer-slide-top-enter-active,
150
- .pxd-transition--drawer-slide-top-leave-active,
151
- /* 底部滑入滑出动画 */
152
- .pxd-transition--drawer-slide-bottom-enter-active,
153
- .pxd-transition--drawer-slide-bottom-leave-active {
131
+ .pxd-drawer {
132
+ &[data-position="left"] {
133
+ left: 0;
134
+ top: 0;
135
+ bottom: 0;
136
+ }
137
+
138
+ &[data-position="top"] {
139
+ left: 0;
140
+ top: 0;
141
+ right: 0;
142
+ }
143
+
144
+ &[data-position="right"] {
145
+ right: 0;
146
+ top: 0;
147
+ bottom: 0;
148
+ }
149
+
150
+ &[data-position="bottom"] {
151
+ left: 0;
152
+ bottom: 0;
153
+ right: 0;
154
+ }
155
+ }
156
+
157
+ .pxd-drawer[data-position="left"],
158
+ .pxd-drawer[data-position="right"] {
159
+ width: var(--size, var(--w, 80vw));
160
+ }
161
+
162
+ .pxd-drawer[data-position="top"],
163
+ .pxd-drawer[data-position="bottom"] {
164
+ height: var(--size, var(--h, 80vw));
165
+ }
166
+
167
+ .pxd-transition--drawer-right-enter-active,
168
+ .pxd-transition--drawer-right-leave-active,
169
+ .pxd-transition--drawer-left-enter-active,
170
+ .pxd-transition--drawer-left-leave-active,
171
+ .pxd-transition--drawer-top-enter-active,
172
+ .pxd-transition--drawer-top-leave-active,
173
+ .pxd-transition--drawer-bottom-enter-active,
174
+ .pxd-transition--drawer-bottom-leave-active {
154
175
  transition: transform var(--default-transition-duration, 0.3s) var(--default-transition-timing-function);
155
176
  }
156
177
 
157
- .pxd-transition--drawer-slide-right-leave-to,
158
- .pxd-transition--drawer-slide-right-enter-from {
178
+ .pxd-transition--drawer-right-leave-to,
179
+ .pxd-transition--drawer-right-enter-from {
159
180
  transform: translateX(100%);
160
181
  }
161
182
 
162
- .pxd-transition--drawer-slide-left-leave-to,
163
- .pxd-transition--drawer-slide-left-enter-from {
183
+ .pxd-transition--drawer-left-leave-to,
184
+ .pxd-transition--drawer-left-enter-from {
164
185
  transform: translateX(-100%);
165
186
  }
166
187
 
167
- .pxd-transition--drawer-slide-top-leave-to,
168
- .pxd-transition--drawer-slide-top-enter-from {
188
+ .pxd-transition--drawer-top-leave-to,
189
+ .pxd-transition--drawer-top-enter-from {
169
190
  transform: translateY(-100%);
170
191
  }
171
192
 
172
- .pxd-transition--drawer-slide-bottom-leave-to,
173
- .pxd-transition--drawer-slide-bottom-enter-from {
193
+ .pxd-transition--drawer-bottom-leave-to,
194
+ .pxd-transition--drawer-bottom-enter-from {
174
195
  transform: translateY(100%);
175
196
  }
176
197
  </style>
@@ -1,15 +1,17 @@
1
- import type { BasePosition, ComponentLabel } from '../../types/shared';
1
+ import type { BasePosition, ComponentClass, ComponentLabel } from '../../types/shared';
2
2
  interface Props {
3
- size?: number | string;
4
3
  title?: ComponentLabel;
5
4
  subtitle?: ComponentLabel;
5
+ size?: number | string;
6
+ pending?: boolean;
7
+ position?: BasePosition;
6
8
  modelValue?: boolean;
7
- headerStyle?: boolean;
8
- footerStyle?: boolean;
9
9
  appendToBody?: boolean;
10
+ headerStylize?: boolean;
11
+ footerStylize?: boolean;
12
+ drawerClass?: ComponentClass;
10
13
  closeOnPressEscape?: boolean;
11
14
  closeOnClickOverlay?: boolean;
12
- position?: BasePosition;
13
15
  }
14
16
  declare var __VLS_14: {}, __VLS_16: {}, __VLS_22: {}, __VLS_24: {};
15
17
  type __VLS_Slots = {} & {
@@ -33,13 +35,12 @@ declare const __VLS_component: import("vue").DefineComponent<Props, void, {}, {}
33
35
  "onClick-outside"?: (args_0: MouseEvent) => any;
34
36
  }>, {
35
37
  position: BasePosition;
36
- size: number | string;
37
38
  modelValue: boolean;
38
39
  closeOnPressEscape: boolean;
39
40
  appendToBody: boolean;
40
- headerStyle: boolean;
41
- footerStyle: boolean;
42
41
  closeOnClickOverlay: boolean;
42
+ headerStylize: boolean;
43
+ footerStylize: boolean;
43
44
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
44
45
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
45
46
  export default _default;
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import { computed, onBeforeUnmount, onMounted, shallowRef } from "vue";
3
- import { provideListContext } from "../../contexts/list";
3
+ import { provideListContext, provideListItemIndexContext } from "../../contexts/list";
4
4
  import { off, on } from "../../utils/events";
5
5
  import { getCssUnitValue } from "../../utils/format";
6
6
  import { isServer } from "../../utils/is";
@@ -18,29 +18,24 @@ const props = defineProps({
18
18
  const emits = defineEmits(["close", "toggle", "selected"]);
19
19
  const ITEM_CLASS = "pxd-list-item";
20
20
  const ITEM_SELECTOR = `.${ITEM_CLASS}`;
21
- const activeIndex = shallowRef(-1);
21
+ const initialIndex = Number.NaN;
22
+ const activeIndex = shallowRef(initialIndex);
23
+ const increaseIndex = shallowRef(0);
22
24
  const allItems = shallowRef([]);
23
25
  const computedStyle = computed(() => {
24
26
  return {
25
27
  width: getCssUnitValue(props.width)
26
28
  };
27
29
  });
28
- function updateAllItemsIndex() {
29
- allItems.value.forEach((item, index) => {
30
- item.dataset.index = String(index);
31
- });
32
- }
33
30
  function registerListItem(el) {
34
31
  if (!allItems.value.includes(el)) {
35
32
  allItems.value.push(el);
36
- updateAllItemsIndex();
37
33
  }
38
34
  }
39
35
  function unregisterListItem(el) {
40
36
  const index = allItems.value.indexOf(el);
41
- if (index > -1) {
37
+ if (index >= 0) {
42
38
  allItems.value.splice(index, 1);
43
- updateAllItemsIndex();
44
39
  }
45
40
  }
46
41
  function getItemData(index) {
@@ -48,9 +43,10 @@ function getItemData(index) {
48
43
  if (!element) {
49
44
  return null;
50
45
  }
46
+ const { disabled, type = "default" } = element.dataset;
51
47
  return {
52
- disabled: element.classList.contains("text-gray-700") || element.hasAttribute("disabled"),
53
- type: element.classList.contains("text-red-900") ? "error" : void 0
48
+ disabled: disabled === "true",
49
+ type
54
50
  };
55
51
  }
56
52
  function getCorrectIndex(dir, index) {
@@ -70,6 +66,8 @@ function getCorrectIndex(dir, index) {
70
66
  }
71
67
  const PREV_KEYS = ["ArrowUp", "ArrowLeft"];
72
68
  const NEXT_KEYS = ["ArrowDown", "ArrowRight"];
69
+ const FUNCTION_KEYS = ["Enter", "Escape", "Tab"];
70
+ const PREVENT_DEFAULT_KEYS = [...FUNCTION_KEYS, ...PREV_KEYS, ...NEXT_KEYS];
73
71
  const THROTTLE_INTERVALS = 255;
74
72
  const containerKeydownThrottled = throttle((ev) => {
75
73
  const count = allItems.value.length;
@@ -89,10 +87,10 @@ const containerKeydownThrottled = throttle((ev) => {
89
87
  return;
90
88
  }
91
89
  if (PREV_KEYS.includes(key)) {
92
- activeIndex.value = activeIndex.value === -1 ? count - 1 : getCorrectIndex("prev", activeIndex.value);
90
+ activeIndex.value = Object.is(activeIndex.value, initialIndex) ? count - 1 : getCorrectIndex("prev", activeIndex.value);
93
91
  emits("toggle", activeIndex.value);
94
92
  } else if (NEXT_KEYS.includes(key)) {
95
- activeIndex.value = activeIndex.value === -1 ? 0 : getCorrectIndex("next", activeIndex.value);
93
+ activeIndex.value = Object.is(activeIndex.value, initialIndex) ? 0 : getCorrectIndex("next", activeIndex.value);
96
94
  emits("toggle", activeIndex.value);
97
95
  }
98
96
  if (allItems.value.length <= 0 || activeIndex.value < 0) {
@@ -103,7 +101,10 @@ const containerKeydownThrottled = throttle((ev) => {
103
101
  });
104
102
  }, THROTTLE_INTERVALS, { edges: ["leading"] });
105
103
  function onContainerKeydown(ev) {
106
- ev.preventDefault();
104
+ if (PREVENT_DEFAULT_KEYS.includes(ev.key)) {
105
+ ev.preventDefault();
106
+ }
107
+ ev.stopPropagation();
107
108
  containerKeydownThrottled(ev);
108
109
  }
109
110
  function onPointerOver(ev) {
@@ -119,6 +120,7 @@ function onOptionClick(ev, index) {
119
120
  emits("selected", ev, index);
120
121
  emits("close");
121
122
  }
123
+ provideListItemIndexContext(increaseIndex);
122
124
  provideListContext({
123
125
  activeIndex,
124
126
  onOptionClick,
@@ -132,9 +134,6 @@ onMounted(() => {
132
134
  on(document, "keydown", onContainerKeydown);
133
135
  });
134
136
  onBeforeUnmount(() => {
135
- if (isServer) {
136
- return;
137
- }
138
137
  off(document, "keydown", onContainerKeydown);
139
138
  allItems.value = [];
140
139
  });
@@ -144,10 +143,9 @@ onBeforeUnmount(() => {
144
143
  <ul
145
144
  role="list"
146
145
  tabindex="-1"
147
- class="pxd-list"
146
+ class="pxd-list max-w-full"
148
147
  :style="computedStyle"
149
148
  @pointerover="onPointerOver"
150
- @keydown="onContainerKeydown"
151
149
  >
152
150
  <PScrollable class="max-h-68" content-class="pr-2">
153
151
  <slot>
@@ -1,14 +1,15 @@
1
1
  <script setup>
2
- import { computed, onMounted, onUnmounted, shallowRef } from "vue";
3
- import { useListContext } from "../../contexts/list";
2
+ import { computed, onMounted, onUnmounted, shallowRef, useAttrs } from "vue";
3
+ import { useListContext, useListItemIndexContext } from "../../contexts/list";
4
4
  defineOptions({
5
5
  name: "PListItem"
6
6
  });
7
7
  const props = defineProps({
8
8
  as: { type: null, required: false, default: "li" },
9
- type: { type: String, required: false },
10
- label: { type: [String, Number, Array, null], required: false },
11
- disabled: { type: Boolean, required: false, default: false }
9
+ type: { type: null, required: false, default: "default" },
10
+ label: { type: null, required: false },
11
+ disabled: { type: null, required: false, default: false },
12
+ description: { type: null, required: false }
12
13
  });
13
14
  const emits = defineEmits(["click"]);
14
15
  const {
@@ -17,76 +18,56 @@ const {
17
18
  registerListItem,
18
19
  unregisterListItem
19
20
  } = useListContext();
21
+ const listItemIndex = useListItemIndexContext();
22
+ const attrs = useAttrs();
20
23
  const itemRef = shallowRef();
21
- const currentIndex = shallowRef(-1);
24
+ const currentIndex = shallowRef(listItemIndex.value++);
25
+ const itemTypeMap = {
26
+ error: "text-red-900 data-[selected=true]:bg-red-100",
27
+ warning: "text-amber-900 data-[selected=true]:bg-amber-100",
28
+ default: "text-foreground data-[selected=true]:bg-gray-alpha-100"
29
+ };
30
+ const isSelected = computed(() => activeIndex.value === currentIndex.value);
22
31
  const computedClass = computed(() => {
23
- const classes = [];
24
- if (props.type === "error") {
25
- classes.push("text-red-900");
26
- }
27
- if (props.disabled) {
28
- classes.push("pointer-events-none text-gray-700");
29
- } else {
30
- classes.push("cursor-pointer");
32
+ const classes = ["cursor-pointer data-[disabled=true]:pointer-events-none data-[disabled=true]:text-gray-700", attrs.class];
33
+ if (props.type) {
34
+ classes.push(itemTypeMap[props.type]);
31
35
  }
32
36
  return classes.join(" ");
33
37
  });
34
- function setRef(el) {
35
- if (!el) {
36
- return;
37
- }
38
- itemRef.value = el instanceof HTMLElement ? el : el.$el;
39
- if (registerListItem) {
40
- registerListItem(itemRef.value);
41
- }
42
- updateCurrentIndex();
43
- }
44
- function updateCurrentIndex() {
45
- if (itemRef.value && itemRef.value.dataset.index) {
46
- currentIndex.value = Number(itemRef.value.dataset.index);
47
- }
48
- }
49
38
  function onItemClick(ev) {
50
39
  emits("click", ev, currentIndex.value);
51
40
  onOptionClick?.(ev, currentIndex.value);
52
41
  }
53
42
  onMounted(() => {
54
- updateCurrentIndex();
55
- const observer = new MutationObserver(() => {
56
- updateCurrentIndex();
57
- });
58
- if (itemRef.value) {
59
- observer.observe(itemRef.value, {
60
- attributes: true,
61
- attributeFilter: ["data-index"]
62
- });
43
+ if (registerListItem) {
44
+ registerListItem(itemRef.value);
63
45
  }
64
- onUnmounted(() => {
65
- observer.disconnect();
66
- });
67
46
  });
68
47
  onUnmounted(() => {
69
- if (itemRef.value && unregisterListItem) {
48
+ if (unregisterListItem) {
70
49
  unregisterListItem(itemRef.value);
71
50
  }
72
51
  });
73
52
  </script>
74
53
 
75
54
  <template>
76
- <component
55
+ <Component
77
56
  :is="as"
78
- :ref="setRef"
57
+ ref="itemRef"
79
58
  tabindex="-1"
80
59
  role="listitem"
60
+ :data-type="type"
81
61
  :data-index="currentIndex"
82
62
  :data-disabled="disabled"
83
- :data-selected="activeIndex === currentIndex"
84
- class="pxd-list-item h-10 px-2 text-sm flex w-full items-center rounded-md outline-none data-[selected=true]:bg-gray-alpha-100 motion-safe:transition-colors"
63
+ :data-selected="isSelected"
64
+ class="pxd-list-item h-10 gap-1 px-2 text-sm flex w-full items-center rounded-md outline-none motion-safe:transition-colors"
85
65
  :class="computedClass"
86
66
  @click="onItemClick"
87
67
  >
88
68
  <slot>
89
- {{ label }}
69
+ <span class="gap-2 flex items-center">{{ label }}</span>
70
+ <span v-if="description" class="text-sm text-foreground-secondary">{{ description }}</span>
90
71
  </slot>
91
72
  </component>
92
73
  </template>