@dumiflex/wp-kit 0.3.0 → 0.5.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 (130) hide show
  1. package/CHANGELOG.md +92 -51
  2. package/LICENSE +674 -674
  3. package/README.md +73 -76
  4. package/package.json +78 -77
  5. package/src/Avatar.vue +83 -83
  6. package/src/Badge.vue +97 -97
  7. package/src/Banner.vue +118 -118
  8. package/src/BookmarkButton.vue +27 -27
  9. package/src/BundleMeta.vue +51 -51
  10. package/src/Button.vue +103 -103
  11. package/src/Card.vue +68 -68
  12. package/src/Checkbox.vue +109 -107
  13. package/src/Chip.vue +163 -97
  14. package/src/ChoiceCards.vue +133 -133
  15. package/src/CloseButton.vue +69 -69
  16. package/src/CodeBlock.vue +146 -119
  17. package/src/ColorField.vue +188 -206
  18. package/src/ColorPicker.vue +201 -201
  19. package/src/Combobox.vue +331 -334
  20. package/src/ConfirmDialog.vue +165 -163
  21. package/src/ConfirmModal.vue +147 -147
  22. package/src/ConstraintMatrixPreview.vue +708 -708
  23. package/src/ContextMenu.vue +386 -379
  24. package/src/CopyButton.vue +156 -156
  25. package/src/DataTable.vue +293 -292
  26. package/src/DatePicker.vue +234 -234
  27. package/src/DescriptionList.vue +73 -73
  28. package/src/DiffView.vue +68 -68
  29. package/src/Disclosure.vue +166 -166
  30. package/src/DragHandle.vue +49 -49
  31. package/src/Drawer.vue +131 -131
  32. package/src/DropZone.vue +196 -196
  33. package/src/DropdownMenu.vue +72 -72
  34. package/src/EmptyState.vue +111 -111
  35. package/src/Field.vue +28 -28
  36. package/src/HsvPicker.vue +208 -208
  37. package/src/Icon.vue +39 -39
  38. package/src/IconPicker.vue +209 -209
  39. package/src/ImageGrid.vue +127 -125
  40. package/src/InlineEdit.vue +183 -183
  41. package/src/Input.vue +99 -99
  42. package/src/Kbd.vue +56 -56
  43. package/src/Lightbox.vue +176 -173
  44. package/src/ListFilter.vue +85 -85
  45. package/src/ListRow.vue +204 -204
  46. package/src/LoadingMark.vue +217 -161
  47. package/src/Logo.vue +60 -60
  48. package/src/Modal.vue +78 -76
  49. package/src/ModalShell.vue +109 -107
  50. package/src/Pagination.vue +106 -106
  51. package/src/Popover.vue +180 -171
  52. package/src/PostCard.vue +210 -211
  53. package/src/ProgressBar.vue +90 -90
  54. package/src/RadioGroup.vue +177 -177
  55. package/src/RangeSlider.vue +183 -183
  56. package/src/RelativeDate.vue +35 -35
  57. package/src/ResizeHandle.vue +143 -143
  58. package/src/SearchInput.vue +192 -199
  59. package/src/Select.vue +644 -641
  60. package/src/Separator.vue +54 -54
  61. package/src/Skeleton.vue +98 -82
  62. package/src/Slider.vue +198 -198
  63. package/src/SortableList.vue +260 -260
  64. package/src/SplitPane.vue +68 -68
  65. package/src/StarButton.vue +40 -40
  66. package/src/Stepper.vue +141 -141
  67. package/src/SupportLinks.vue +93 -93
  68. package/src/TagInput.vue +297 -300
  69. package/src/Textarea.vue +57 -57
  70. package/src/Timeline.vue +92 -92
  71. package/src/ToastCard.vue +119 -119
  72. package/src/ToastHost.vue +40 -40
  73. package/src/Toggle.vue +36 -36
  74. package/src/TokenizedValue.vue +310 -310
  75. package/src/Toolbar.vue +110 -110
  76. package/src/VirtualList.vue +87 -87
  77. package/src/VisuallyHidden.vue +29 -29
  78. package/src/WpButton.vue +75 -75
  79. package/src/WpCheck.vue +104 -102
  80. package/src/WpIconButton.vue +107 -107
  81. package/src/WpImageZoom.vue +544 -544
  82. package/src/WpNumber.vue +178 -183
  83. package/src/WpSegmented.vue +175 -175
  84. package/src/WpSelect.vue +93 -89
  85. package/src/WpSwitch.vue +163 -163
  86. package/src/WpTabs.vue +188 -188
  87. package/src/_modal-motion.css +33 -33
  88. package/src/_skeleton.css +50 -50
  89. package/src/axis-color.ts +42 -42
  90. package/src/button.css +169 -169
  91. package/src/calendar.ts +59 -59
  92. package/src/clipboard.ts +50 -50
  93. package/src/colour-presets.ts +12 -12
  94. package/src/diff.ts +51 -51
  95. package/src/drop-zone.ts +41 -41
  96. package/src/field.css +50 -0
  97. package/src/flip.ts +237 -237
  98. package/src/floating-layer.ts +48 -0
  99. package/src/floating.ts +99 -99
  100. package/src/highlight.ts +199 -0
  101. package/src/hsv.ts +100 -100
  102. package/src/icon-catalog.ts +52 -52
  103. package/src/image-zoom.ts +93 -93
  104. package/src/kind-colour.ts +28 -0
  105. package/src/loading-mark-motion.ts +249 -0
  106. package/src/logo-paths.ts +21 -21
  107. package/src/matrix-axis.ts +91 -91
  108. package/src/pagination.ts +27 -27
  109. package/src/platform-keys.ts +29 -29
  110. package/src/rich-tokenize.ts +357 -357
  111. package/src/select-types.ts +32 -32
  112. package/src/slider-math.ts +45 -45
  113. package/src/sortable.ts +22 -22
  114. package/src/theme/fonts/OFL-Fraunces.txt +93 -93
  115. package/src/theme/fonts/OFL-JetBrainsMono.txt +93 -93
  116. package/src/theme/fonts/OFL-Manrope.txt +93 -93
  117. package/src/theme/looks.css +428 -428
  118. package/src/theme/palettes.css +254 -254
  119. package/src/theme/tokens.css +2896 -2894
  120. package/src/tokens.ts +56 -56
  121. package/src/tooltip.css +43 -43
  122. package/src/tooltip.ts +146 -146
  123. package/src/useDialogFocus.ts +73 -73
  124. package/src/useGrowableField.ts +357 -357
  125. package/src/useModalKeyShield.ts +116 -116
  126. package/src/useToast.ts +48 -48
  127. package/src/wp-logo.svg +14 -14
  128. package/src/ChipToggle.vue +0 -92
  129. package/src/KindChip.vue +0 -53
  130. package/src/RatingBadge.vue +0 -42
@@ -1,116 +1,116 @@
1
- import { onBeforeUnmount, watch, type Ref } from "vue";
2
-
3
- /**
4
- * Stop keystrokes inside one of our modals from reaching ComfyUI's global
5
- * shortcuts.
6
- *
7
- * ComfyUI binds `useEventListener(window, 'keydown', keybindHandler)` and only
8
- * bails for text-editing targets:
9
- *
10
- * const target = event.composedPath()[0]
11
- * if (keyCombo.isReservedByTextInput &&
12
- * (target.tagName === 'TEXTAREA' || target.tagName === 'INPUT' ||
13
- * target.contentEditable === 'true' || ...)) return
14
- *
15
- * So a plain letter pressed while focus sits on a button or a div inside our
16
- * modal is not "reserved by text input" and runs the global keybinding —
17
- * pressing `r` in an edit modal fired Refresh Node Definitions and froze the
18
- * page. Its other guard, which skips global keybindings while a dialog is
19
- * open, keys off ComfyUI's OWN dialog stack, which our modals never enter.
20
- *
21
- * The listener is bound to the modal ROOT in bubble phase. Anything inside the
22
- * modal has already had its turn by then — inner `@keydown` handlers, input
23
- * defaults, IME — and the event simply stops before it can climb to window.
24
- *
25
- * That alone is not enough, because a key only traverses the overlay if it is
26
- * TARGETED inside it. Open a modal and type without clicking in it first and
27
- * `document.activeElement` is still `<body>`, so the event goes body → window
28
- * and never passes the shield at all — which is how `a` kept reaching ComfyUI
29
- * even with the root listener in place. Two additions close that:
30
- *
31
- * - focus the overlay when it opens, so keystrokes land inside the modal;
32
- * - guard at the document for keys targeted OUTSIDE the open modal, since a
33
- * modal owns the keyboard while it is up. Escape still passes, so the
34
- * modal's own window-level close handler keeps working.
35
- *
36
- * Because of that, a modal's own key handling must live on the root too, not
37
- * on `window`: a window listener sits ABOVE the shield and would never see
38
- * keys pressed inside its own modal. `onKey` is where that logic goes.
39
- */
40
- export interface ModalKeyShieldOptions {
41
- /** Runs for every keydown inside the modal, before the event is stopped. */
42
- onKey?: ((event: KeyboardEvent) => void) | undefined;
43
- /**
44
- * Let a key through to the rest of the page. Use sparingly — the point of
45
- * the shield is that a modal owns the keyboard while it is open.
46
- */
47
- passThrough?: ((event: KeyboardEvent) => boolean) | undefined;
48
- }
49
-
50
- /** Wire the shield to a root element that exists only while the modal is open. */
51
- export function useModalKeyShield(
52
- root: Ref<HTMLElement | null | undefined>,
53
- options: ModalKeyShieldOptions = {},
54
- ): void {
55
- let bound: HTMLElement | null = null;
56
-
57
- const onKeydown = (event: KeyboardEvent): void => {
58
- options.onKey?.(event);
59
- if (options.passThrough?.(event)) return;
60
- // stopPropagation only: the default action still happens, so typing,
61
- // shortcuts inside inputs and IME composition all behave normally.
62
- event.stopPropagation();
63
- };
64
-
65
- // Keyup carries no handling of ours, but extensions bind shortcuts on it
66
- // and a half-seen chord is worse than none.
67
- const onKeyup = (event: KeyboardEvent): void => {
68
- if (options.passThrough?.(event)) return;
69
- event.stopPropagation();
70
- };
71
-
72
- // Keys aimed outside the open modal — the canvas, the body — never reach the
73
- // overlay, so they are stopped here instead. Document bubble runs before
74
- // window, where ComfyUI's keybindHandler lives.
75
- const onOutside = (event: KeyboardEvent): void => {
76
- if (!bound) return;
77
- const target = event.target;
78
- if (target instanceof Node && bound.contains(target)) return;
79
- // Escape belongs to whoever wants to close something.
80
- if (event.key === "Escape") return;
81
- if (options.passThrough?.(event)) return;
82
- event.stopPropagation();
83
- };
84
-
85
- const detach = (): void => {
86
- if (!bound) return;
87
- bound.removeEventListener("keydown", onKeydown);
88
- bound.removeEventListener("keyup", onKeyup);
89
- document.removeEventListener("keydown", onOutside);
90
- document.removeEventListener("keyup", onOutside);
91
- bound = null;
92
- };
93
-
94
- watch(
95
- root,
96
- (el) => {
97
- detach();
98
- if (!el) return;
99
- bound = el;
100
- el.addEventListener("keydown", onKeydown);
101
- el.addEventListener("keyup", onKeyup);
102
- document.addEventListener("keydown", onOutside);
103
- document.addEventListener("keyup", onOutside);
104
- // Pull focus in, so typing lands inside the modal rather than on <body>.
105
- // Only when focus is not already somewhere inside — an autofocused input
106
- // must keep it.
107
- if (!el.contains(document.activeElement)) {
108
- if (!el.hasAttribute("tabindex")) el.setAttribute("tabindex", "-1");
109
- el.focus({ preventScroll: true });
110
- }
111
- },
112
- { immediate: true, flush: "post" },
113
- );
114
-
115
- onBeforeUnmount(detach);
116
- }
1
+ import { onBeforeUnmount, watch, type Ref } from "vue";
2
+
3
+ /**
4
+ * Stop keystrokes inside one of our modals from reaching ComfyUI's global
5
+ * shortcuts.
6
+ *
7
+ * ComfyUI binds `useEventListener(window, 'keydown', keybindHandler)` and only
8
+ * bails for text-editing targets:
9
+ *
10
+ * const target = event.composedPath()[0]
11
+ * if (keyCombo.isReservedByTextInput &&
12
+ * (target.tagName === 'TEXTAREA' || target.tagName === 'INPUT' ||
13
+ * target.contentEditable === 'true' || ...)) return
14
+ *
15
+ * So a plain letter pressed while focus sits on a button or a div inside our
16
+ * modal is not "reserved by text input" and runs the global keybinding —
17
+ * pressing `r` in an edit modal fired Refresh Node Definitions and froze the
18
+ * page. Its other guard, which skips global keybindings while a dialog is
19
+ * open, keys off ComfyUI's OWN dialog stack, which our modals never enter.
20
+ *
21
+ * The listener is bound to the modal ROOT in bubble phase. Anything inside the
22
+ * modal has already had its turn by then — inner `@keydown` handlers, input
23
+ * defaults, IME — and the event simply stops before it can climb to window.
24
+ *
25
+ * That alone is not enough, because a key only traverses the overlay if it is
26
+ * TARGETED inside it. Open a modal and type without clicking in it first and
27
+ * `document.activeElement` is still `<body>`, so the event goes body → window
28
+ * and never passes the shield at all — which is how `a` kept reaching ComfyUI
29
+ * even with the root listener in place. Two additions close that:
30
+ *
31
+ * - focus the overlay when it opens, so keystrokes land inside the modal;
32
+ * - guard at the document for keys targeted OUTSIDE the open modal, since a
33
+ * modal owns the keyboard while it is up. Escape still passes, so the
34
+ * modal's own window-level close handler keeps working.
35
+ *
36
+ * Because of that, a modal's own key handling must live on the root too, not
37
+ * on `window`: a window listener sits ABOVE the shield and would never see
38
+ * keys pressed inside its own modal. `onKey` is where that logic goes.
39
+ */
40
+ export interface ModalKeyShieldOptions {
41
+ /** Runs for every keydown inside the modal, before the event is stopped. */
42
+ onKey?: ((event: KeyboardEvent) => void) | undefined;
43
+ /**
44
+ * Let a key through to the rest of the page. Use sparingly — the point of
45
+ * the shield is that a modal owns the keyboard while it is open.
46
+ */
47
+ passThrough?: ((event: KeyboardEvent) => boolean) | undefined;
48
+ }
49
+
50
+ /** Wire the shield to a root element that exists only while the modal is open. */
51
+ export function useModalKeyShield(
52
+ root: Ref<HTMLElement | null | undefined>,
53
+ options: ModalKeyShieldOptions = {},
54
+ ): void {
55
+ let bound: HTMLElement | null = null;
56
+
57
+ const onKeydown = (event: KeyboardEvent): void => {
58
+ options.onKey?.(event);
59
+ if (options.passThrough?.(event)) return;
60
+ // stopPropagation only: the default action still happens, so typing,
61
+ // shortcuts inside inputs and IME composition all behave normally.
62
+ event.stopPropagation();
63
+ };
64
+
65
+ // Keyup carries no handling of ours, but extensions bind shortcuts on it
66
+ // and a half-seen chord is worse than none.
67
+ const onKeyup = (event: KeyboardEvent): void => {
68
+ if (options.passThrough?.(event)) return;
69
+ event.stopPropagation();
70
+ };
71
+
72
+ // Keys aimed outside the open modal — the canvas, the body — never reach the
73
+ // overlay, so they are stopped here instead. Document bubble runs before
74
+ // window, where ComfyUI's keybindHandler lives.
75
+ const onOutside = (event: KeyboardEvent): void => {
76
+ if (!bound) return;
77
+ const target = event.target;
78
+ if (target instanceof Node && bound.contains(target)) return;
79
+ // Escape belongs to whoever wants to close something.
80
+ if (event.key === "Escape") return;
81
+ if (options.passThrough?.(event)) return;
82
+ event.stopPropagation();
83
+ };
84
+
85
+ const detach = (): void => {
86
+ if (!bound) return;
87
+ bound.removeEventListener("keydown", onKeydown);
88
+ bound.removeEventListener("keyup", onKeyup);
89
+ document.removeEventListener("keydown", onOutside);
90
+ document.removeEventListener("keyup", onOutside);
91
+ bound = null;
92
+ };
93
+
94
+ watch(
95
+ root,
96
+ (el) => {
97
+ detach();
98
+ if (!el) return;
99
+ bound = el;
100
+ el.addEventListener("keydown", onKeydown);
101
+ el.addEventListener("keyup", onKeyup);
102
+ document.addEventListener("keydown", onOutside);
103
+ document.addEventListener("keyup", onOutside);
104
+ // Pull focus in, so typing lands inside the modal rather than on <body>.
105
+ // Only when focus is not already somewhere inside — an autofocused input
106
+ // must keep it.
107
+ if (!el.contains(document.activeElement)) {
108
+ if (!el.hasAttribute("tabindex")) el.setAttribute("tabindex", "-1");
109
+ el.focus({ preventScroll: true });
110
+ }
111
+ },
112
+ { immediate: true, flush: "post" },
113
+ );
114
+
115
+ onBeforeUnmount(detach);
116
+ }
package/src/useToast.ts CHANGED
@@ -1,48 +1,48 @@
1
- import { ref } from "vue";
2
-
3
- export type ToastSeverity = "info" | "success" | "warn" | "error";
4
-
5
- export interface ToastAction {
6
- label: string;
7
- run: () => void | Promise<void>;
8
- }
9
-
10
- export interface ToastEntry {
11
- id: string;
12
- severity: ToastSeverity;
13
- summary: string;
14
- detail?: string;
15
- life?: number;
16
- /** Optional action button rendered inline with the toast. Click runs the handler
17
- * and dismisses the toast. Reserved for Undo flows (Phase 3 bulk actions). */
18
- action?: ToastAction;
19
- }
20
-
21
- const DEFAULT_LIFE = 2400;
22
-
23
- const toasts = ref<ToastEntry[]>([]);
24
-
25
- let nextId = 0;
26
- function makeId() {
27
- nextId += 1;
28
- return `t${Date.now().toString(36)}_${nextId}`;
29
- }
30
-
31
- function dismiss(id: string) {
32
- const i = toasts.value.findIndex((t) => t.id === id);
33
- if (i >= 0) toasts.value.splice(i, 1);
34
- }
35
-
36
- function push(entry: Omit<ToastEntry, "id">): string {
37
- const id = makeId();
38
- const life = entry.life ?? DEFAULT_LIFE;
39
- toasts.value.push({ ...entry, id });
40
- if (life > 0) {
41
- setTimeout(() => dismiss(id), life);
42
- }
43
- return id;
44
- }
45
-
46
- export function useToast() {
47
- return { toasts, push, dismiss };
48
- }
1
+ import { ref } from "vue";
2
+
3
+ export type ToastSeverity = "info" | "success" | "warn" | "error";
4
+
5
+ export interface ToastAction {
6
+ label: string;
7
+ run: () => void | Promise<void>;
8
+ }
9
+
10
+ export interface ToastEntry {
11
+ id: string;
12
+ severity: ToastSeverity;
13
+ summary: string;
14
+ detail?: string;
15
+ life?: number;
16
+ /** Optional action button rendered inline with the toast. Click runs the handler
17
+ * and dismisses the toast. Reserved for Undo flows (Phase 3 bulk actions). */
18
+ action?: ToastAction;
19
+ }
20
+
21
+ const DEFAULT_LIFE = 2400;
22
+
23
+ const toasts = ref<ToastEntry[]>([]);
24
+
25
+ let nextId = 0;
26
+ function makeId() {
27
+ nextId += 1;
28
+ return `t${Date.now().toString(36)}_${nextId}`;
29
+ }
30
+
31
+ function dismiss(id: string) {
32
+ const i = toasts.value.findIndex((t) => t.id === id);
33
+ if (i >= 0) toasts.value.splice(i, 1);
34
+ }
35
+
36
+ function push(entry: Omit<ToastEntry, "id">): string {
37
+ const id = makeId();
38
+ const life = entry.life ?? DEFAULT_LIFE;
39
+ toasts.value.push({ ...entry, id });
40
+ if (life > 0) {
41
+ setTimeout(() => dismiss(id), life);
42
+ }
43
+ return id;
44
+ }
45
+
46
+ export function useToast() {
47
+ return { toasts, push, dismiss };
48
+ }
package/src/wp-logo.svg CHANGED
@@ -1,14 +1,14 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-name="Layer 1" viewBox="0 0 1024 1024" width="100%" height="100%"><defs>
2
- <linearGradient id="wp-logo-grad-a" x1="312.56" y1="897.19" x2="312.56" y2="2" gradientUnits="userSpaceOnUse">
3
- <stop offset="0" stop-color="#a970ff"></stop>
4
- <stop offset="1" stop-color="#6366f1"></stop>
5
- </linearGradient>
6
- <linearGradient id="wp-logo-grad-b" x1="711.44" y1="126.81" x2="711.44" y2="1022" gradientUnits="userSpaceOnUse">
7
- <stop offset="0" stop-color="#6366f1"></stop>
8
- <stop offset="1" stop-color="#33d6c6"></stop>
9
- </linearGradient>
10
- </defs><path fill="url(#wp-logo-grad-a)" d="m512,2c-53.6,0-98.47,38.16-108.84,88.73-75.07,19.29-143.32,58.26-198.74,113.68-55.42,55.42-94.39,123.68-113.68,198.74-50.58,10.38-88.73,55.24-88.73,108.84s38.16,98.47,88.73,108.84c13.62,53.14,37.03,102.64,69.77,147.47,37.14,50.86,85.83,93.86,140.81,124.33,5.54,3.08,11.56,4.54,17.48,4.54,12.7,0,25.02-6.72,31.61-18.61,9.68-17.44,3.38-39.42-14.07-49.09-85.32-47.32-147.22-125.71-173.38-218.21,36.3-18.29,61.27-55.92,61.27-99.28s-24.94-80.97-61.23-99.27c34.22-119.96,129.79-215.52,249.74-249.74,18.3,36.29,55.92,61.23,99.27,61.23,61.27,0,111.11-49.84,111.11-111.11S573.27,2,512,2ZM113.11,473.11c21.44,0,38.89,17.44,38.89,38.89,0,20.1-15.32,36.69-34.91,38.69h-.01c-.18.02-.36.03-.53.04-1.13.1-2.28.16-3.43.16-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89Zm398.89-321.11c-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89,38.89,17.44,38.89,38.89-17.44,38.89-38.89,38.89Z"></path><path fill="url(#wp-logo-grad-b)" d="m933.27,403.16c-13.62-53.14-37.03-102.64-69.77-147.47-37.14-50.86-85.83-93.86-140.81-124.33-17.44-9.68-39.42-3.38-49.09,14.07-9.68,17.44-3.38,39.42,14.07,49.09,85.32,47.32,147.22,125.71,173.38,218.21-36.3,18.29-61.27,55.92-61.27,99.28s24.94,80.97,61.23,99.27c-34.22,119.96-129.79,215.52-249.74,249.74-18.3-36.29-55.92-61.23-99.27-61.23-61.27,0-111.11,49.84-111.11,111.11s49.84,111.11,111.11,111.11c53.6,0,98.47-38.16,108.84-88.73,75.07-19.29,143.32-58.26,198.74-113.68,55.42-55.42,94.39-123.68,113.68-198.74,50.58-10.38,88.73-55.24,88.73-108.84s-38.16-98.47-88.73-108.84Zm-421.27,546.62c-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89,38.89,17.44,38.89,38.89-17.44,38.89-38.89,38.89Zm398.89-398.89c-21.44,0-38.89-17.44-38.89-38.89,0-20.03,15.22-36.58,34.72-38.67.38-.04.77-.08,1.16-.11h.02c.99-.08,1.99-.11,2.99-.11,21.44,0,38.89,17.44,38.89,38.89s-17.44,38.89-38.89,38.89Z"></path><g>
11
- <path fill="#a970ff" d="m735.02,517.56c-153.82,30.87-186.59,63.64-217.46,217.46-1.22,6.08-9.9,6.08-11.12,0-30.87-153.82-63.64-186.59-217.46-217.46-6.08-1.22-6.08-9.9,0-11.12,153.82-30.87,186.59-63.64,217.46-217.46,1.22-6.08,9.9-6.08,11.12,0,30.87,153.82,63.64,186.59,217.46,217.46,6.08,1.22,6.08,9.9,0,11.12Z"></path>
12
- <path fill="#6366f1" d="m434.06,665.85c-51.08,10.25-61.96,21.13-72.22,72.22-.41,2.02-3.29,2.02-3.69,0-10.25-51.08-21.13-61.96-72.22-72.22-2.02-.41-2.02-3.29,0-3.69,51.08-10.25,61.96-21.13,72.22-72.22.41-2.02,3.29-2.02,3.69,0,10.25,51.08,21.13,61.96,72.22,72.22,2.02.41,2.02,3.29,0,3.69Z"></path>
13
- <path fill="#33d6c6" d="m738.07,361.84c-51.08,10.25-61.96,21.13-72.22,72.22-.41,2.02-3.29,2.02-3.69,0-10.25-51.08-21.13-61.96-72.22-72.22-2.02-.41-2.02-3.29,0-3.69,51.08-10.25,61.96-21.13,72.22-72.22.41-2.02,3.29-2.02,3.69,0,10.25,51.08,21.13,61.96,72.22,72.22,2.02.41,2.02,3.29,0,3.69Z"></path>
14
- </g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-name="Layer 1" viewBox="0 0 1024 1024" width="100%" height="100%"><defs>
2
+ <linearGradient id="wp-logo-grad-a" x1="312.56" y1="897.19" x2="312.56" y2="2" gradientUnits="userSpaceOnUse">
3
+ <stop offset="0" stop-color="#a970ff"></stop>
4
+ <stop offset="1" stop-color="#6366f1"></stop>
5
+ </linearGradient>
6
+ <linearGradient id="wp-logo-grad-b" x1="711.44" y1="126.81" x2="711.44" y2="1022" gradientUnits="userSpaceOnUse">
7
+ <stop offset="0" stop-color="#6366f1"></stop>
8
+ <stop offset="1" stop-color="#33d6c6"></stop>
9
+ </linearGradient>
10
+ </defs><path fill="url(#wp-logo-grad-a)" d="m512,2c-53.6,0-98.47,38.16-108.84,88.73-75.07,19.29-143.32,58.26-198.74,113.68-55.42,55.42-94.39,123.68-113.68,198.74-50.58,10.38-88.73,55.24-88.73,108.84s38.16,98.47,88.73,108.84c13.62,53.14,37.03,102.64,69.77,147.47,37.14,50.86,85.83,93.86,140.81,124.33,5.54,3.08,11.56,4.54,17.48,4.54,12.7,0,25.02-6.72,31.61-18.61,9.68-17.44,3.38-39.42-14.07-49.09-85.32-47.32-147.22-125.71-173.38-218.21,36.3-18.29,61.27-55.92,61.27-99.28s-24.94-80.97-61.23-99.27c34.22-119.96,129.79-215.52,249.74-249.74,18.3,36.29,55.92,61.23,99.27,61.23,61.27,0,111.11-49.84,111.11-111.11S573.27,2,512,2ZM113.11,473.11c21.44,0,38.89,17.44,38.89,38.89,0,20.1-15.32,36.69-34.91,38.69h-.01c-.18.02-.36.03-.53.04-1.13.1-2.28.16-3.43.16-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89Zm398.89-321.11c-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89,38.89,17.44,38.89,38.89-17.44,38.89-38.89,38.89Z"></path><path fill="url(#wp-logo-grad-b)" d="m933.27,403.16c-13.62-53.14-37.03-102.64-69.77-147.47-37.14-50.86-85.83-93.86-140.81-124.33-17.44-9.68-39.42-3.38-49.09,14.07-9.68,17.44-3.38,39.42,14.07,49.09,85.32,47.32,147.22,125.71,173.38,218.21-36.3,18.29-61.27,55.92-61.27,99.28s24.94,80.97,61.23,99.27c-34.22,119.96-129.79,215.52-249.74,249.74-18.3-36.29-55.92-61.23-99.27-61.23-61.27,0-111.11,49.84-111.11,111.11s49.84,111.11,111.11,111.11c53.6,0,98.47-38.16,108.84-88.73,75.07-19.29,143.32-58.26,198.74-113.68,55.42-55.42,94.39-123.68,113.68-198.74,50.58-10.38,88.73-55.24,88.73-108.84s-38.16-98.47-88.73-108.84Zm-421.27,546.62c-21.44,0-38.89-17.44-38.89-38.89s17.44-38.89,38.89-38.89,38.89,17.44,38.89,38.89-17.44,38.89-38.89,38.89Zm398.89-398.89c-21.44,0-38.89-17.44-38.89-38.89,0-20.03,15.22-36.58,34.72-38.67.38-.04.77-.08,1.16-.11h.02c.99-.08,1.99-.11,2.99-.11,21.44,0,38.89,17.44,38.89,38.89s-17.44,38.89-38.89,38.89Z"></path><g>
11
+ <path fill="#a970ff" d="m735.02,517.56c-153.82,30.87-186.59,63.64-217.46,217.46-1.22,6.08-9.9,6.08-11.12,0-30.87-153.82-63.64-186.59-217.46-217.46-6.08-1.22-6.08-9.9,0-11.12,153.82-30.87,186.59-63.64,217.46-217.46,1.22-6.08,9.9-6.08,11.12,0,30.87,153.82,63.64,186.59,217.46,217.46,6.08,1.22,6.08,9.9,0,11.12Z"></path>
12
+ <path fill="#6366f1" d="m434.06,665.85c-51.08,10.25-61.96,21.13-72.22,72.22-.41,2.02-3.29,2.02-3.69,0-10.25-51.08-21.13-61.96-72.22-72.22-2.02-.41-2.02-3.29,0-3.69,51.08-10.25,61.96-21.13,72.22-72.22.41-2.02,3.29-2.02,3.69,0,10.25,51.08,21.13,61.96,72.22,72.22,2.02.41,2.02,3.29,0,3.69Z"></path>
13
+ <path fill="#33d6c6" d="m738.07,361.84c-51.08,10.25-61.96,21.13-72.22,72.22-.41,2.02-3.29,2.02-3.69,0-10.25-51.08-21.13-61.96-72.22-72.22-2.02-.41-2.02-3.29,0-3.69,51.08-10.25,61.96-21.13,72.22-72.22.41-2.02,3.29-2.02,3.69,0,10.25,51.08,21.13,61.96,72.22,72.22,2.02.41,2.02,3.29,0,3.69Z"></path>
14
+ </g></svg>
@@ -1,92 +0,0 @@
1
- <script setup lang="ts">
2
- /**
3
- * A pill you press to turn a filter or flag on and off: a tag filter, a
4
- * module-type filter, "Recommended" on a version, a category you pick.
5
- * `pressed` lights it in the accent, or in `color` (a kind colour, a
6
- * category's colour) when given; with `color` the icon wears it even when
7
- * off, so a row of type filters reads at a glance. `count` shows a small
8
- * number after the label.
9
- *
10
- * <ChipToggle :pressed="tag === t.slug" icon="pi-tag" :count="t.usage" @click="pick(t)">{{ t.name }}</ChipToggle>
11
- * <ChipToggle :pressed="kind === 'wildcard'" color="var(--wp-kind-wildcard)" icon="pi-sparkles">Wildcard</ChipToggle>
12
- *
13
- * A real `<button>` with `aria-pressed`; it reads only the shared tokens, so
14
- * it works on a page and on the canvas.
15
- */
16
- import { computed } from "vue";
17
- import Icon from "./Icon.vue";
18
-
19
- const props = withDefaults(defineProps<{
20
- pressed?: boolean | undefined;
21
- /** PrimeIcons name (`tag` or `pi-tag`) before the label. */
22
- icon?: string | undefined;
23
- /** Tint when pressed (and of the icon): any CSS colour. Default the accent. */
24
- color?: string | null | undefined;
25
- count?: number | string | undefined;
26
- size?: "sm" | "md" | undefined;
27
- disabled?: boolean | undefined;
28
- }>(), { pressed: false, size: "sm" });
29
-
30
- const style = computed(() => (props.color ? { "--chip-toggle-c": props.color } : undefined));
31
- </script>
32
-
33
- <template>
34
- <button
35
- type="button"
36
- class="wp-chip-toggle"
37
- :class="[`wp-chip-toggle--${size}`, { 'wp-chip-toggle--tinted': !!color }]"
38
- :style="style"
39
- :aria-pressed="pressed"
40
- :disabled="disabled"
41
- >
42
- <Icon v-if="icon" class="wp-chip-toggle__icon" :name="icon" :size="11" />
43
- <span class="wp-chip-toggle__label"><slot /></span>
44
- <span v-if="count !== undefined" class="wp-chip-toggle__count">{{ count }}</span>
45
- </button>
46
- </template>
47
-
48
- <style scoped>
49
- .wp-chip-toggle {
50
- --chip-toggle-c: var(--wp-accent-500);
51
- display: inline-flex;
52
- align-items: center;
53
- gap: var(--wp-space-2);
54
- height: var(--wp-control-h-sm);
55
- padding: 0 var(--wp-space-4);
56
- border: 1px solid var(--wp-border);
57
- border-radius: var(--wp-radius-cap, 999px);
58
- background: var(--wp-bg-3);
59
- color: var(--wp-text-muted);
60
- font: inherit;
61
- font-family: var(--wp-font-sans);
62
- font-size: var(--wp-text-sm);
63
- font-weight: 500;
64
- line-height: 1;
65
- white-space: nowrap;
66
- cursor: pointer;
67
- transition: background var(--wp-motion-quick) ease, border-color var(--wp-motion-quick) ease, color var(--wp-motion-quick) ease;
68
- }
69
- .wp-chip-toggle--md { height: var(--wp-control-h-md); padding: 0 var(--wp-space-5); font-size: var(--wp-text-base); }
70
- .wp-chip-toggle:hover:not(:disabled) { border-color: var(--wp-border-strong); color: var(--wp-text); }
71
- .wp-chip-toggle:focus-visible { outline: none; box-shadow: var(--wp-focus-ring); }
72
- .wp-chip-toggle:disabled { opacity: 0.45; cursor: not-allowed; }
73
- .wp-chip-toggle--tinted .wp-chip-toggle__icon { color: var(--chip-toggle-c); }
74
- .wp-chip-toggle[aria-pressed="true"] {
75
- border-color: color-mix(in oklab, var(--chip-toggle-c) 45%, transparent);
76
- background: color-mix(in oklab, var(--chip-toggle-c) 16%, transparent);
77
- color: var(--wp-accent-text-strong);
78
- }
79
- .wp-chip-toggle--tinted[aria-pressed="true"] { color: var(--chip-toggle-c); }
80
- .wp-chip-toggle__count {
81
- padding: 1px var(--wp-space-3);
82
- border-radius: var(--wp-radius-cap, 999px);
83
- background: var(--wp-bg-4, var(--wp-bg-2));
84
- color: var(--wp-text-dim);
85
- font-family: var(--wp-font-mono);
86
- font-size: var(--wp-text-xs);
87
- }
88
- .wp-chip-toggle[aria-pressed="true"] .wp-chip-toggle__count {
89
- background: color-mix(in oklab, var(--chip-toggle-c) 22%, transparent);
90
- color: inherit;
91
- }
92
- </style>
package/src/KindChip.vue DELETED
@@ -1,53 +0,0 @@
1
- <script setup lang="ts">
2
- /**
3
- * The module-type chip: `wildcard`, `fixed_values`, `combine`, `constraint`,
4
- * `derivation` or `bundle`, in the kind colour the extension uses for that
5
- * type everywhere (`--wp-kind-*`; bundles are slate, `--wp-bundle-default`).
6
- *
7
- * <KindChip kind="module" subtype="wildcard" />
8
- * <KindChip kind="bundle" />
9
- *
10
- * A module with no subtype reads as `fixed_values`, the oldest type.
11
- */
12
- import { computed } from "vue";
13
-
14
- export type KindChipSubtype = "fixed_values" | "combine" | "wildcard" | "constraint" | "derivation";
15
-
16
- const props = defineProps<{
17
- kind: "module" | "bundle";
18
- /** A `"bundle"` subtype reads as a bundle too (some APIs report it so). */
19
- subtype?: KindChipSubtype | "bundle" | null | undefined;
20
- }>();
21
-
22
- const type = computed(() => (props.kind === "bundle" ? "bundle" : (props.subtype ?? "fixed_values")));
23
- </script>
24
-
25
- <template>
26
- <span class="wp-kind-chip" :data-type="type">{{ type }}</span>
27
- </template>
28
-
29
- <style scoped>
30
- .wp-kind-chip {
31
- --kind-chip-c: var(--wp-text-muted);
32
- display: inline-flex;
33
- align-items: center;
34
- gap: 5px;
35
- padding: 3px 9px;
36
- border-radius: var(--wp-radius-cap, 999px);
37
- font-family: var(--wp-font-mono);
38
- font-size: 11px;
39
- font-weight: 500;
40
- line-height: 14px;
41
- letter-spacing: -0.01em;
42
- white-space: nowrap;
43
- color: var(--kind-chip-c);
44
- background: color-mix(in oklab, var(--kind-chip-c) 14%, transparent);
45
- border: 1px solid color-mix(in oklab, var(--kind-chip-c) 38%, transparent);
46
- }
47
- .wp-kind-chip[data-type="fixed_values"] { --kind-chip-c: var(--wp-kind-fixed); }
48
- .wp-kind-chip[data-type="combine"] { --kind-chip-c: var(--wp-kind-combine); }
49
- .wp-kind-chip[data-type="wildcard"] { --kind-chip-c: var(--wp-kind-wildcard); }
50
- .wp-kind-chip[data-type="constraint"] { --kind-chip-c: var(--wp-kind-constraint); }
51
- .wp-kind-chip[data-type="derivation"] { --kind-chip-c: var(--wp-kind-derivation); }
52
- .wp-kind-chip[data-type="bundle"] { --kind-chip-c: var(--wp-bundle-default); }
53
- </style>
@@ -1,42 +0,0 @@
1
- <script setup lang="ts">
2
- /**
3
- * The NSFW and Featured badges on a post: small uppercase pills. Renders
4
- * nothing for a safe, unfeatured post.
5
- *
6
- * <RatingBadge rating="nsfw" featured />
7
- */
8
- import Icon from "./Icon.vue";
9
-
10
- defineProps<{
11
- rating?: "sfw" | "nsfw" | undefined;
12
- featured?: boolean | undefined;
13
- }>();
14
- </script>
15
-
16
- <template>
17
- <span v-if="rating === 'nsfw'" class="wp-rating-badge wp-rating-badge--nsfw">NSFW</span>
18
- <span v-if="featured" class="wp-rating-badge wp-rating-badge--featured">
19
- <Icon name="star-fill" :size="9" />Featured
20
- </span>
21
- </template>
22
-
23
- <style scoped>
24
- .wp-rating-badge {
25
- --rating-badge-c: var(--wp-warn);
26
- display: inline-flex;
27
- align-items: center;
28
- gap: 4px;
29
- padding: 3px 8px;
30
- border-radius: var(--wp-radius-cap, 999px);
31
- font-size: 10px;
32
- font-weight: 600;
33
- letter-spacing: 0.08em;
34
- line-height: 14px;
35
- text-transform: uppercase;
36
- white-space: nowrap;
37
- color: var(--rating-badge-c);
38
- background: color-mix(in oklab, var(--rating-badge-c) 16%, transparent);
39
- border: 1px solid color-mix(in oklab, var(--rating-badge-c) 42%, transparent);
40
- }
41
- .wp-rating-badge--nsfw { --rating-badge-c: var(--wp-danger); color: var(--wp-danger-text); }
42
- </style>