@dumiflex/wp-kit 0.0.0-stage → 0.3.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/CHANGELOG.md +51 -0
- package/LICENSE +674 -0
- package/README.md +76 -3
- package/package.json +77 -6
- package/src/Avatar.vue +83 -0
- package/src/Badge.vue +97 -0
- package/src/Banner.vue +118 -0
- package/src/BookmarkButton.vue +27 -0
- package/src/BundleMeta.vue +51 -0
- package/src/Button.vue +103 -0
- package/src/Card.vue +68 -0
- package/src/Checkbox.vue +107 -0
- package/src/Chip.vue +97 -0
- package/src/ChipToggle.vue +92 -0
- package/src/ChoiceCards.vue +133 -0
- package/src/CloseButton.vue +69 -0
- package/src/CodeBlock.vue +119 -0
- package/src/ColorField.vue +206 -0
- package/src/ColorPicker.vue +201 -0
- package/src/Combobox.vue +334 -0
- package/src/ConfirmDialog.vue +163 -0
- package/src/ConfirmModal.vue +147 -0
- package/src/ConstraintMatrixPreview.vue +708 -0
- package/src/ContextMenu.vue +379 -0
- package/src/CopyButton.vue +156 -0
- package/src/DataTable.vue +292 -0
- package/src/DatePicker.vue +234 -0
- package/src/DescriptionList.vue +73 -0
- package/src/DiffView.vue +68 -0
- package/src/Disclosure.vue +166 -0
- package/src/DragHandle.vue +49 -0
- package/src/Drawer.vue +131 -0
- package/src/DropZone.vue +196 -0
- package/src/DropdownMenu.vue +72 -0
- package/src/EmptyState.vue +111 -0
- package/src/Field.vue +28 -0
- package/src/HsvPicker.vue +208 -0
- package/src/Icon.vue +39 -0
- package/src/IconPicker.vue +209 -0
- package/src/ImageGrid.vue +125 -0
- package/src/InlineEdit.vue +183 -0
- package/src/Input.vue +99 -0
- package/src/Kbd.vue +56 -0
- package/src/KindChip.vue +53 -0
- package/src/Lightbox.vue +173 -0
- package/src/ListFilter.vue +85 -0
- package/src/ListRow.vue +204 -0
- package/src/LoadingMark.vue +161 -0
- package/src/Logo.vue +60 -0
- package/src/Modal.vue +76 -0
- package/src/ModalShell.vue +107 -0
- package/src/Pagination.vue +106 -0
- package/src/Popover.vue +171 -0
- package/src/PostCard.vue +211 -0
- package/src/ProgressBar.vue +90 -0
- package/src/RadioGroup.vue +177 -0
- package/src/RangeSlider.vue +183 -0
- package/src/RatingBadge.vue +42 -0
- package/src/RelativeDate.vue +35 -0
- package/src/ResizeHandle.vue +143 -0
- package/src/SearchInput.vue +199 -0
- package/src/Select.vue +641 -0
- package/src/Separator.vue +54 -0
- package/src/Skeleton.vue +82 -0
- package/src/Slider.vue +198 -0
- package/src/SortableList.vue +260 -0
- package/src/SplitPane.vue +68 -0
- package/src/StarButton.vue +40 -0
- package/src/Stepper.vue +141 -0
- package/src/SupportLinks.vue +93 -0
- package/src/TagInput.vue +300 -0
- package/src/Textarea.vue +57 -0
- package/src/Timeline.vue +92 -0
- package/src/ToastCard.vue +119 -0
- package/src/ToastHost.vue +40 -0
- package/src/Toggle.vue +36 -0
- package/src/TokenizedValue.vue +310 -0
- package/src/Toolbar.vue +110 -0
- package/src/VirtualList.vue +87 -0
- package/src/VisuallyHidden.vue +29 -0
- package/src/WpButton.vue +75 -0
- package/src/WpCheck.vue +102 -0
- package/src/WpIconButton.vue +107 -0
- package/src/WpImageZoom.vue +544 -0
- package/src/WpNumber.vue +183 -0
- package/src/WpSegmented.vue +175 -0
- package/src/WpSelect.vue +89 -0
- package/src/WpSwitch.vue +163 -0
- package/src/WpTabs.vue +188 -0
- package/src/_modal-motion.css +33 -0
- package/src/_skeleton.css +50 -0
- package/src/axis-color.ts +42 -0
- package/src/button.css +169 -0
- package/src/calendar.ts +59 -0
- package/src/clipboard.ts +50 -0
- package/src/colour-presets.ts +12 -0
- package/src/diff.ts +51 -0
- package/src/drop-zone.ts +41 -0
- package/src/flip.ts +237 -0
- package/src/floating.ts +99 -0
- package/src/hsv.ts +100 -0
- package/src/icon-catalog.ts +52 -0
- package/src/image-zoom.ts +93 -0
- package/src/logo-paths.ts +21 -0
- package/src/matrix-axis.ts +91 -0
- package/src/pagination.ts +27 -0
- package/src/platform-keys.ts +29 -0
- package/src/rich-tokenize.ts +357 -0
- package/src/select-types.ts +32 -0
- package/src/slider-math.ts +45 -0
- package/src/sortable.ts +22 -0
- package/src/theme/fonts/OFL-Fraunces.txt +93 -0
- package/src/theme/fonts/OFL-JetBrainsMono.txt +93 -0
- package/src/theme/fonts/OFL-Manrope.txt +93 -0
- package/src/theme/fonts/fraunces-latin-wght-normal.woff2 +0 -0
- package/src/theme/fonts/jetbrains-mono-latin.woff2 +0 -0
- package/src/theme/fonts/manrope-latin-wght-normal.woff2 +0 -0
- package/src/theme/looks.css +428 -0
- package/src/theme/palettes.css +254 -0
- package/src/theme/tokens.css +2894 -0
- package/src/tokens.ts +56 -0
- package/src/tooltip.css +43 -0
- package/src/tooltip.ts +146 -0
- package/src/useDialogFocus.ts +73 -0
- package/src/useGrowableField.ts +357 -0
- package/src/useModalKeyShield.ts +116 -0
- package/src/useToast.ts +48 -0
- package/src/wp-logo.svg +14 -0
|
@@ -0,0 +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
|
+
}
|
package/src/useToast.ts
ADDED
|
@@ -0,0 +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
|
+
}
|
package/src/wp-logo.svg
ADDED
|
@@ -0,0 +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>
|