@dumiflex/wp-kit 0.5.0 → 0.7.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 +47 -0
- package/README.md +5 -0
- package/package.json +4 -3
- package/src/Avatar.vue +1 -1
- package/src/Banner.vue +4 -4
- package/src/BundlePreview.vue +254 -0
- package/src/Button.vue +73 -44
- package/src/Checkbox.vue +201 -63
- package/src/Chip.vue +5 -2
- package/src/CodeBlock.vue +1 -1
- package/src/ColorField.vue +60 -6
- package/src/CombinePreview.vue +150 -0
- package/src/Combobox.vue +10 -14
- package/src/ConfirmModal.vue +12 -22
- package/src/ConstraintMatrixPreview.vue +6 -22
- package/src/ConstraintPreview.vue +259 -0
- package/src/ContextMenu.vue +11 -30
- package/src/CopyButton.vue +1 -1
- package/src/DataTable.vue +0 -2
- package/src/DatePicker.vue +1 -1
- package/src/DerivationCondition.vue +68 -0
- package/src/DerivationPreview.vue +345 -0
- package/src/Disclosure.vue +2 -1
- package/src/Drawer.vue +2 -2
- package/src/DropZone.vue +2 -2
- package/src/FixedValuesPreview.vue +184 -0
- package/src/HsvPicker.vue +2 -2
- package/src/IconPicker.vue +32 -103
- package/src/IconPickerPanel.vue +132 -0
- package/src/ImageGrid.vue +2 -2
- package/src/Lightbox.vue +2 -2
- package/src/LoadingMark.vue +6 -61
- package/src/Modal.vue +109 -44
- package/src/ModalShell.vue +26 -5
- package/src/Pagination.vue +4 -4
- package/src/PayloadPreview.vue +154 -0
- package/src/Popover.vue +68 -12
- package/src/PostDetailSkeleton.vue +65 -0
- package/src/PostGridSkeleton.vue +61 -0
- package/src/PreviewGroup.vue +75 -0
- package/src/ProfileSkeleton.vue +73 -0
- package/src/RadioGroup.vue +5 -4
- package/src/RichText.vue +61 -0
- package/src/SearchInput.vue +1 -1
- package/src/Select.vue +53 -158
- package/src/SelectionBar.vue +90 -0
- package/src/SettingGroup.vue +57 -0
- package/src/SettingRow.vue +127 -0
- package/src/Skeleton.vue +1 -1
- package/src/Slider.vue +166 -55
- package/src/SortableList.vue +1 -1
- package/src/TagInput.vue +1 -1
- package/src/Timeline.vue +2 -2
- package/src/WildcardPreview.vue +607 -0
- package/src/WorkingLine.vue +234 -0
- package/src/WpIconButton.vue +3 -2
- package/src/WpImageZoom.vue +7 -7
- package/src/WpSegmented.vue +3 -1
- package/src/WpSwitch.vue +1 -1
- package/src/WpTabs.vue +1 -1
- package/src/_modal-motion.css +1 -1
- package/src/button.css +8 -6
- package/src/canvas-motion.ts +251 -0
- package/src/floating.ts +5 -0
- package/src/hsv.ts +1 -1
- package/src/kind-colour.ts +24 -1
- package/src/linkify.ts +142 -0
- package/src/loading-mark-motion.ts +12 -155
- package/src/matrix-axis.ts +28 -0
- package/src/menu.css +51 -0
- package/src/motion.css +73 -0
- package/src/pi-icons.ts +51 -0
- package/src/preview-readers.ts +401 -0
- package/src/preview.css +194 -0
- package/src/slider-math.ts +45 -1
- package/src/theme/looks.css +6 -19
- package/src/theme/tokens.css +12 -1108
- package/src/tokens.ts +15 -4
- package/src/tooltip.css +8 -5
- package/src/useDialogFocus.ts +11 -2
- package/src/working-line-motion.ts +135 -0
- package/src/CloseButton.vue +0 -69
- package/src/ColorPicker.vue +0 -201
- package/src/ConfirmDialog.vue +0 -165
- package/src/RangeSlider.vue +0 -183
- package/src/Toggle.vue +0 -36
- package/src/WpButton.vue +0 -75
- package/src/WpCheck.vue +0 -104
- package/src/_skeleton.css +0 -50
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.0](https://github.com/DumiFlex/wp-kit/compare/v0.6.0...v0.7.0) (2026-11-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* IconPicker all (every PrimeIcons glyph, searchable), inline, color and allow-none ([#70](https://github.com/DumiFlex/wp-kit/issues/70)) ([a0dc837](https://github.com/DumiFlex/wp-kit/commit/a0dc83716441c8061428e69faf751ab7b52ed8ae))
|
|
9
|
+
* RichText, the module payload previews and the post loading shapes move into the kit ([#71](https://github.com/DumiFlex/wp-kit/issues/71)) ([b92d62f](https://github.com/DumiFlex/wp-kit/commit/b92d62f87ecd44cb205d2265d7d56c9018e29144))
|
|
10
|
+
* SelectionBar, the one bulk-action bar over a list of ticked rows ([#69](https://github.com/DumiFlex/wp-kit/issues/69)) ([13a51e2](https://github.com/DumiFlex/wp-kit/commit/13a51e22b0e27b404700dc6f3fb2f34fd912d10a))
|
|
11
|
+
* SettingGroup and SettingRow, the settings page rows both apps use ([#68](https://github.com/DumiFlex/wp-kit/issues/68)) ([20f4ff8](https://github.com/DumiFlex/wp-kit/commit/20f4ff8a8c28da63fcabfd9f2c0c2bf28a4f60dd))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* draw WorkingLine's marks on a canvas so they glide in Firefox ([#67](https://github.com/DumiFlex/wp-kit/issues/67)) ([6565f15](https://github.com/DumiFlex/wp-kit/commit/6565f1575ecb224ad9e94efdef611f5c31b12849))
|
|
17
|
+
|
|
18
|
+
## [0.6.0](https://github.com/DumiFlex/wp-kit/compare/v0.5.0...v0.6.0) (2026-11-02)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### ⚠ BREAKING CHANGES
|
|
22
|
+
|
|
23
|
+
* WpButton, CloseButton and ConfirmDialog are removed. Use Button (size="sm" on the canvas; variant="ghost" icon="times" aria-label="Close" for a close button) and ConfirmModal (visible -> open, variant="danger" -> tone="danger"). Button shows its aria-label as a kit tooltip instead of a title attribute. Modal's panel classes are scoped; a ModalShell backdrop close needs mousedown and click on the backdrop.
|
|
24
|
+
* RangeSlider is removed (use Slider with a [low, high] v-model); _skeleton.css and .wp-skel-bar are removed (use Skeleton); unused global classes are removed from theme/tokens.css.
|
|
25
|
+
* WpCheck is removed; use Checkbox (same v-model, size, disabled and --check-on). Toggle is removed; use <WpSwitch size="md"> (error becomes invalid). ColorPicker is removed; use <ColorField presets size="md"> (test hooks are <dataTest>, <dataTest>-preset, <dataTest>-hex). The global .wp-check, .wp-checkbox* and .wp-radio classes are removed.
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* **site:** API tables under every piece, read from the components' own types ([#58](https://github.com/DumiFlex/wp-kit/issues/58)) ([e23973a](https://github.com/DumiFlex/wp-kit/commit/e23973acf57f158068594dc0d63f8172084ab945))
|
|
30
|
+
* **site:** search bar that finds pieces and sections ([#52](https://github.com/DumiFlex/wp-kit/issues/52)) ([8989669](https://github.com/DumiFlex/wp-kit/commit/8989669c43bc8f1511266525ee6a8b9007ee2891))
|
|
31
|
+
* Slider snap settles the thumb on its marks (all the time, or within a distance) ([#60](https://github.com/DumiFlex/wp-kit/issues/60)) ([9b1b5ac](https://github.com/DumiFlex/wp-kit/commit/9b1b5acf1f0d604814b300d7158691fbab007ee0))
|
|
32
|
+
* WorkingLine and entrance classes; AI animations on the kit site ([#56](https://github.com/DumiFlex/wp-kit/issues/56)) ([430df62](https://github.com/DumiFlex/wp-kit/commit/430df624e27e9181d174a7572f70369c7a7b428c))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Bug Fixes
|
|
36
|
+
|
|
37
|
+
* centre icons on whole pixels; no link underline on a link drawn as a button ([#59](https://github.com/DumiFlex/wp-kit/issues/59)) ([9de9468](https://github.com/DumiFlex/wp-kit/commit/9de94684688879cdd396f948b54650c43918a9fc))
|
|
38
|
+
* chip text clipped in Firefox, refresh icon wobbling as it spins ([#53](https://github.com/DumiFlex/wp-kit/issues/53)) ([02e0c84](https://github.com/DumiFlex/wp-kit/commit/02e0c8405293e39206ea8c810822b00029ee2f44))
|
|
39
|
+
* circles follow the look's corner cap (square in Terminal and TUI) ([#61](https://github.com/DumiFlex/wp-kit/issues/61)) ([7ae84ba](https://github.com/DumiFlex/wp-kit/commit/7ae84baae4024e20b4e67f7b5fb4ec314d3a55b4))
|
|
40
|
+
* tooltip and popover outlines survive a host's CSS reset ([#63](https://github.com/DumiFlex/wp-kit/issues/63)) ([fd8a334](https://github.com/DumiFlex/wp-kit/commit/fd8a334ac86cca52f83f9e0ac7b3bc3ed3b92a98))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Refactoring
|
|
44
|
+
|
|
45
|
+
* fold RangeSlider into Slider, drop dead global CSS; write the one-piece rule ([#57](https://github.com/DumiFlex/wp-kit/issues/57)) ([2e5d024](https://github.com/DumiFlex/wp-kit/commit/2e5d0240bbb78b467940f19ee50f4de0623ef69d))
|
|
46
|
+
* one Button and one Modal (WpButton, CloseButton and ConfirmDialog folded in) ([#62](https://github.com/DumiFlex/wp-kit/issues/62)) ([591546d](https://github.com/DumiFlex/wp-kit/commit/591546db46a1d6f3fa6ccef10c484bbe40689e94))
|
|
47
|
+
* one Checkbox, one switch, one colour field ([#54](https://github.com/DumiFlex/wp-kit/issues/54)) ([06dba67](https://github.com/DumiFlex/wp-kit/commit/06dba67d9495773bcb1088c09763b30654972bff))
|
|
48
|
+
* one menu (menu.css, Popover variant="menu", Select on Popover) ([#55](https://github.com/DumiFlex/wp-kit/issues/55)) ([e39dc1f](https://github.com/DumiFlex/wp-kit/commit/e39dc1f27f8d0d1efec5bae609b0bf19543d015b))
|
|
49
|
+
|
|
3
50
|
## [0.5.0](https://github.com/DumiFlex/wp-kit/compare/v0.4.0...v0.5.0) (2026-11-02)
|
|
4
51
|
|
|
5
52
|
|
package/README.md
CHANGED
|
@@ -57,6 +57,11 @@ If you already import the kit as `@/kit/...`, alias `@/kit` to `node_modules/@du
|
|
|
57
57
|
|
|
58
58
|
- **Vue 3.5** and **PrimeIcons 7** (peer dependencies). Icons are `pi pi-*` classes. The kit imports nothing else at runtime.
|
|
59
59
|
- **A theme:** the kit's own sheets above. A host with its own colour sheet can define the names in [`SHARED_TOKENS`](https://github.com/DumiFlex/wp-kit/blob/main/src/tokens.ts) instead and use the pieces that read only those.
|
|
60
|
+
- **A layer order, if your app uses cascade layers** (Tailwind 4 does). A few pieces (the tooltip, Popover, DropdownMenu, the entrance classes) put their rules in `@layer wp-extension`, so a host page's unlayered CSS wins a clash (that keeps the kit polite on the ComfyUI canvas). Their outlines are drawn as rings, so a reset like Tailwind's preflight (`border-width: 0`) can't strip them. If your own CSS is layered, declare the order in your page's `<head>`, before any stylesheet, with the kit above your reset and below your utilities:
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<style>@layer theme, base, wp-extension, components, utilities;</style>
|
|
64
|
+
```
|
|
60
65
|
|
|
61
66
|
## Versions
|
|
62
67
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dumiflex/wp-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Vue 3 component kit shared by the Wildcard Pipeline manager, canvas and community site.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vue",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"npm-readme": "node scripts/npm-readme.mjs"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"
|
|
57
|
-
"
|
|
56
|
+
"primeicons": "^7.0.0 || ^8.0.0",
|
|
57
|
+
"vue": "^3.5.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "^22.10.0",
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
"vite": "5.4.21",
|
|
73
73
|
"vitest": "2.1.9",
|
|
74
74
|
"vue": "3.5.44",
|
|
75
|
+
"vue-component-meta": "2.2.12",
|
|
75
76
|
"vue-eslint-parser": "10.4.0",
|
|
76
77
|
"vue-tsc": "2.2.12"
|
|
77
78
|
}
|
package/src/Avatar.vue
CHANGED
|
@@ -68,7 +68,7 @@ const hue = computed(() => {
|
|
|
68
68
|
.wp-avatar {
|
|
69
69
|
--_s: var(--wp-control-h-md);
|
|
70
70
|
position: relative; display: inline-grid; place-items: center; flex: none; overflow: hidden;
|
|
71
|
-
width: var(--_s); height: var(--_s); border-radius: 50
|
|
71
|
+
width: var(--_s); height: var(--_s); border-radius: min(50%, var(--wp-radius-cap, 999px));
|
|
72
72
|
background: color-mix(in srgb, var(--_hue) 22%, var(--wp-bg-2));
|
|
73
73
|
color: color-mix(in srgb, var(--_hue) 70%, var(--wp-text));
|
|
74
74
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--_hue) 30%, transparent);
|
package/src/Banner.vue
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* that is part of the page (a docs tip): `role="note"`, never announced.
|
|
15
15
|
*/
|
|
16
16
|
import { computed, useSlots } from "vue";
|
|
17
|
-
import
|
|
17
|
+
import Button from "./Button.vue";
|
|
18
18
|
|
|
19
19
|
export type BannerTone = "neutral" | "info" | "success" | "warn" | "danger" | "accent";
|
|
20
20
|
|
|
@@ -60,11 +60,11 @@ const iconClass = computed(() => props.icon ?? TONE_ICON[props.tone]);
|
|
|
60
60
|
<div v-if="slots.default" class="wp-banner__text"><slot /></div>
|
|
61
61
|
</div>
|
|
62
62
|
<div v-if="slots.actions" class="wp-banner__actions"><slot name="actions" /></div>
|
|
63
|
-
<
|
|
64
|
-
|
|
63
|
+
<Button v-if="dismissible" variant="ghost"
|
|
64
|
+
icon="times"
|
|
65
65
|
class="wp-banner__close"
|
|
66
66
|
size="sm"
|
|
67
|
-
:label="dismissLabel"
|
|
67
|
+
:aria-label="dismissLabel"
|
|
68
68
|
@click="emit('dismiss')"
|
|
69
69
|
/>
|
|
70
70
|
</div>
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Bundle: what the bundle needs from outside vs what it sets (folded from
|
|
4
|
+
* its children), then a child list beside the selected child's own full
|
|
5
|
+
* preview. Narrow hosts stack the list above the preview.
|
|
6
|
+
*/
|
|
7
|
+
import { computed, ref } from "vue";
|
|
8
|
+
import PayloadPreview from "./PayloadPreview.vue";
|
|
9
|
+
import ListRow from "./ListRow.vue";
|
|
10
|
+
import Chip from "./Chip.vue";
|
|
11
|
+
import { itemType, kindColour, kindIcon, moduleSubtype } from "./kind-colour";
|
|
12
|
+
import {
|
|
13
|
+
derivationRules,
|
|
14
|
+
fixedValues,
|
|
15
|
+
innerOf,
|
|
16
|
+
readArray,
|
|
17
|
+
readGroups,
|
|
18
|
+
readObject,
|
|
19
|
+
readString,
|
|
20
|
+
varFlow,
|
|
21
|
+
wildcardOptions,
|
|
22
|
+
type Obj,
|
|
23
|
+
} from "./preview-readers";
|
|
24
|
+
|
|
25
|
+
const props = defineProps<{
|
|
26
|
+
payload: Obj;
|
|
27
|
+
refNames: Record<string, string>;
|
|
28
|
+
refGroups: Record<string, Record<string, string[]>>;
|
|
29
|
+
}>();
|
|
30
|
+
|
|
31
|
+
interface Child {
|
|
32
|
+
key: string;
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
type: string;
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
raw: Obj;
|
|
38
|
+
}
|
|
39
|
+
function childName(c: unknown): string | null {
|
|
40
|
+
const meta = readObject(c, "meta");
|
|
41
|
+
return (
|
|
42
|
+
readString(meta, "name") ??
|
|
43
|
+
readString(meta, "library_name") ??
|
|
44
|
+
readString(c, "name") ??
|
|
45
|
+
readString(readObject(c, "payload"), "var_binding")
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const children = computed<Child[]>(() =>
|
|
49
|
+
readArray(props.payload, "children").map((c, i) => ({
|
|
50
|
+
key: readString(c, "id") ?? String(i),
|
|
51
|
+
id: readString(c, "id") ?? "",
|
|
52
|
+
name: childName(c) ?? "—",
|
|
53
|
+
type: readString(c, "type") ?? "—",
|
|
54
|
+
enabled: (c as Obj | null)?.["enabled"] !== false,
|
|
55
|
+
raw: (c && typeof c === "object" ? c : {}) as Obj,
|
|
56
|
+
})),
|
|
57
|
+
);
|
|
58
|
+
/** Sibling names + wildcard axes so a child constraint resolves its
|
|
59
|
+
* wildcards the same way the list does. */
|
|
60
|
+
const childRefNames = computed(() => {
|
|
61
|
+
const out: Record<string, string> = { ...props.refNames };
|
|
62
|
+
for (const c of children.value) if (c.id) out[c.id] = c.name;
|
|
63
|
+
return out;
|
|
64
|
+
});
|
|
65
|
+
const childRefGroups = computed(() => {
|
|
66
|
+
const out: Record<string, Record<string, string[]>> = { ...props.refGroups };
|
|
67
|
+
for (const c of children.value) {
|
|
68
|
+
const g = readGroups(innerOf(c.raw), "tag_groups");
|
|
69
|
+
if (c.id && g) out[c.id] = g;
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
});
|
|
73
|
+
function summary(c: Child): string {
|
|
74
|
+
const inner = innerOf(c.raw);
|
|
75
|
+
const label = c.type.replace(/_/g, " ");
|
|
76
|
+
switch (c.type) {
|
|
77
|
+
case "wildcard": {
|
|
78
|
+
const n = wildcardOptions(inner).length;
|
|
79
|
+
const b = readString(inner, "var_binding");
|
|
80
|
+
return `${label} · ${n} option${n === 1 ? "" : "s"}${b ? ` · $${b}` : ""}`;
|
|
81
|
+
}
|
|
82
|
+
case "fixed_values": {
|
|
83
|
+
const n = fixedValues(inner).length;
|
|
84
|
+
return `${label} · ${n} value${n === 1 ? "" : "s"}`;
|
|
85
|
+
}
|
|
86
|
+
case "derivation": {
|
|
87
|
+
const n = derivationRules(inner).length;
|
|
88
|
+
return `${label} · ${n} rule${n === 1 ? "" : "s"}`;
|
|
89
|
+
}
|
|
90
|
+
case "combine": {
|
|
91
|
+
const out = readString(inner, "output_var");
|
|
92
|
+
return out ? `${label} · $${out}` : label;
|
|
93
|
+
}
|
|
94
|
+
case "constraint": {
|
|
95
|
+
const s = readString(inner, "source_wildcard_id");
|
|
96
|
+
const t = readString(inner, "target_wildcard_id");
|
|
97
|
+
const name = (id: string | null): string => (id ? (childRefNames.value[id] ?? id) : "?");
|
|
98
|
+
return `${label} · ${name(s)} → ${name(t)}`;
|
|
99
|
+
}
|
|
100
|
+
default:
|
|
101
|
+
return label;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const flow = computed(() => varFlow(props.payload));
|
|
105
|
+
const provide = computed(() => flow.value.reads.filter((v) => !flow.value.sets.includes(v)));
|
|
106
|
+
|
|
107
|
+
const selected = ref<string | null>(null);
|
|
108
|
+
const current = computed(
|
|
109
|
+
() => children.value.find((c) => c.key === selected.value) ?? children.value[0] ?? null,
|
|
110
|
+
);
|
|
111
|
+
const counts = computed(() => {
|
|
112
|
+
const out: Record<string, number> = {};
|
|
113
|
+
for (const c of children.value) out[c.type] = (out[c.type] ?? 0) + 1;
|
|
114
|
+
return out;
|
|
115
|
+
});
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<template>
|
|
119
|
+
<div class="wp-pv wp-bundle">
|
|
120
|
+
<div class="wp-pv-stats">
|
|
121
|
+
<div class="wp-pv-stat">
|
|
122
|
+
<b>{{ children.length }}</b><span>module{{ children.length === 1 ? "" : "s" }}</span>
|
|
123
|
+
</div>
|
|
124
|
+
<div
|
|
125
|
+
v-for="(n, t) in counts"
|
|
126
|
+
:key="t"
|
|
127
|
+
class="wp-pv-stat wp-pv-hide-narrow"
|
|
128
|
+
>
|
|
129
|
+
<b>{{ n }}</b><span>{{ String(t).replace(/_/g, " ") }}</span>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div
|
|
133
|
+
v-if="provide.length || flow.sets.length"
|
|
134
|
+
class="wp-bd-flow"
|
|
135
|
+
>
|
|
136
|
+
<template v-if="provide.length">
|
|
137
|
+
<span class="wp-pv-label">You provide</span>
|
|
138
|
+
<div>
|
|
139
|
+
<code
|
|
140
|
+
v-for="v in provide"
|
|
141
|
+
:key="`p-${v}`"
|
|
142
|
+
class="wp-bd-var"
|
|
143
|
+
>${{ v }}</code>
|
|
144
|
+
<span class="wp-bd-hint">read inside, not set by any module here</span>
|
|
145
|
+
</div>
|
|
146
|
+
</template>
|
|
147
|
+
<template v-if="flow.sets.length">
|
|
148
|
+
<span class="wp-pv-label">It sets</span>
|
|
149
|
+
<div>
|
|
150
|
+
<code
|
|
151
|
+
v-for="v in flow.sets"
|
|
152
|
+
:key="`s-${v}`"
|
|
153
|
+
class="wp-bd-var"
|
|
154
|
+
>${{ v }}</code>
|
|
155
|
+
</div>
|
|
156
|
+
</template>
|
|
157
|
+
</div>
|
|
158
|
+
<div class="wp-bd-split">
|
|
159
|
+
<nav
|
|
160
|
+
class="wp-bd-kids wp-bundle-children"
|
|
161
|
+
data-wp-rows
|
|
162
|
+
>
|
|
163
|
+
<ListRow
|
|
164
|
+
v-for="c in children"
|
|
165
|
+
:key="c.key"
|
|
166
|
+
class="wp-bd-kid wp-bundle-child"
|
|
167
|
+
:selected="current?.key === c.key"
|
|
168
|
+
:icon="kindIcon(c.type)"
|
|
169
|
+
:description="`${summary(c)}${c.enabled ? '' : ' · disabled'}`"
|
|
170
|
+
:muted="!c.enabled"
|
|
171
|
+
data-test="bundle-child"
|
|
172
|
+
@click="selected = c.key"
|
|
173
|
+
>
|
|
174
|
+
{{ c.name }}
|
|
175
|
+
</ListRow>
|
|
176
|
+
</nav>
|
|
177
|
+
<div
|
|
178
|
+
v-if="current"
|
|
179
|
+
class="wp-bd-pane"
|
|
180
|
+
data-test="bundle-child-body"
|
|
181
|
+
>
|
|
182
|
+
<div class="wp-bd-pane__head">
|
|
183
|
+
<Chip v-if="moduleSubtype(current.type)" :color="kindColour(itemType('module', moduleSubtype(current.type)))">{{ itemType('module', moduleSubtype(current.type)) }}</Chip>
|
|
184
|
+
<Chip v-else>{{ current.type }}</Chip>
|
|
185
|
+
<b>{{ current.name }}</b>
|
|
186
|
+
</div>
|
|
187
|
+
<PayloadPreview
|
|
188
|
+
:payload="current.raw"
|
|
189
|
+
:ref-names="childRefNames"
|
|
190
|
+
:ref-groups="childRefGroups"
|
|
191
|
+
nested
|
|
192
|
+
/>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</template>
|
|
197
|
+
|
|
198
|
+
<style scoped src="./preview.css"></style>
|
|
199
|
+
<style scoped>
|
|
200
|
+
.wp-bd-flow {
|
|
201
|
+
display: grid;
|
|
202
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
203
|
+
gap: 6px 14px;
|
|
204
|
+
align-items: baseline;
|
|
205
|
+
padding: 12px 16px;
|
|
206
|
+
border-bottom: 1px solid var(--wp-border);
|
|
207
|
+
}
|
|
208
|
+
.wp-bd-flow > div {
|
|
209
|
+
display: flex;
|
|
210
|
+
flex-wrap: wrap;
|
|
211
|
+
gap: 6px;
|
|
212
|
+
align-items: baseline;
|
|
213
|
+
}
|
|
214
|
+
.wp-bd-var {
|
|
215
|
+
font-family: var(--wp-font-mono);
|
|
216
|
+
font-size: 12px;
|
|
217
|
+
color: var(--wp-success);
|
|
218
|
+
}
|
|
219
|
+
.wp-bd-hint {
|
|
220
|
+
font-size: 11.5px;
|
|
221
|
+
color: var(--wp-text-dim);
|
|
222
|
+
}
|
|
223
|
+
.wp-bd-split {
|
|
224
|
+
display: grid;
|
|
225
|
+
grid-template-columns: 240px minmax(0, 1fr);
|
|
226
|
+
}
|
|
227
|
+
@container (max-width: 700px) {
|
|
228
|
+
.wp-bd-split {
|
|
229
|
+
grid-template-columns: minmax(0, 1fr);
|
|
230
|
+
}
|
|
231
|
+
.wp-bd-kids {
|
|
232
|
+
border-right: 0;
|
|
233
|
+
border-bottom: 1px solid var(--wp-border);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
.wp-bd-kids {
|
|
237
|
+
display: flex;
|
|
238
|
+
flex-direction: column;
|
|
239
|
+
gap: 2px;
|
|
240
|
+
padding: 8px;
|
|
241
|
+
border-right: 1px solid var(--wp-border);
|
|
242
|
+
}
|
|
243
|
+
.wp-bd-pane {
|
|
244
|
+
min-width: 0;
|
|
245
|
+
}
|
|
246
|
+
.wp-bd-pane__head {
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
gap: 10px;
|
|
250
|
+
padding: 12px 16px;
|
|
251
|
+
border-bottom: 1px solid var(--wp-border);
|
|
252
|
+
font-size: 13px;
|
|
253
|
+
}
|
|
254
|
+
</style>
|
package/src/Button.vue
CHANGED
|
@@ -1,103 +1,132 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
* Button
|
|
3
|
+
* Button: the one button, in the manager, on the canvas and on the
|
|
4
|
+
* community site (WpButton and CloseButton were folded in).
|
|
5
|
+
*
|
|
6
|
+
* <Button variant="primary" icon="plus">Add module</Button>
|
|
7
|
+
* <Button variant="ghost" icon="download" aria-label="Download snapshot" />
|
|
8
|
+
* <Button variant="ghost" icon="times" aria-label="Close" kbd="Esc" @click="close" />
|
|
9
|
+
* <Button variant="add" icon="plus" size="sm" block>Add rule</Button>
|
|
10
|
+
* <Button as="a" href="/docs" variant="link">Read the docs</Button>
|
|
4
11
|
*
|
|
5
12
|
* Variants:
|
|
6
|
-
* primary
|
|
7
|
-
* secondary
|
|
8
|
-
* ghost
|
|
9
|
-
* danger
|
|
10
|
-
* link
|
|
11
|
-
* outline
|
|
12
|
-
*
|
|
13
|
-
* add — dashed, quiet: the "Add a row" button at the end of a list
|
|
13
|
+
* primary solid accent; the main action of a surface
|
|
14
|
+
* secondary subtle filled; the default
|
|
15
|
+
* ghost transparent with a hover fill; tertiary actions, close (×)
|
|
16
|
+
* danger red-tinted; destructive actions
|
|
17
|
+
* link text only; inline actions
|
|
18
|
+
* outline neutral bordered ghost (an accent outline read as a second primary)
|
|
19
|
+
* add dashed and quiet: the "Add a row" button at the end of a list
|
|
14
20
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
21
|
+
* Sizes follow the control scale: sm 24 (the canvas) / md 32 (default) / lg 40.
|
|
22
|
+
* `icon` / `iconRight` take a PrimeIcons name, with or without `pi-`.
|
|
17
23
|
*
|
|
18
|
-
*
|
|
24
|
+
* With no slot content it is an icon button (square, `.wp-btn--icon`); give
|
|
25
|
+
* it an `aria-label`, which is also its tooltip (`hint` sets another, `kbd`
|
|
26
|
+
* adds a key chip). Use `hint`, not `v-tooltip` on the component, which would
|
|
27
|
+
* fight the button's own. `pressed` makes it a toggle (aria-pressed, accent
|
|
28
|
+
* tint while on). `block` stretches it to its container.
|
|
19
29
|
*
|
|
20
|
-
* States: default · hover · active · focus-visible · disabled · loading.
|
|
21
30
|
* Disabled fades to 45% with a not-allowed cursor and no hover change.
|
|
22
|
-
* Loading keeps the variant's
|
|
23
|
-
*
|
|
31
|
+
* Loading keeps the variant's colours, swaps the lead icon for the sparkle,
|
|
32
|
+
* sets aria-busy and ignores clicks.
|
|
24
33
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
34
|
+
* `as="a"` draws a link that looks like a button (pass `href`, `target`…);
|
|
35
|
+
* disabled then sets aria-disabled and takes it out of the tab order.
|
|
27
36
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
37
|
+
* Its stylesheet (`button.css`) is pulled in scoped, so the canvas never
|
|
38
|
+
* carries the `.wp-btn` classes globally; the manager also loads it globally
|
|
39
|
+
* for the few hand-written `.wp-btn` links. Row icon buttons on the canvas
|
|
40
|
+
* are WpIconButton.
|
|
30
41
|
*/
|
|
31
42
|
import { computed, useSlots } from "vue";
|
|
32
43
|
import Icon from "./Icon.vue";
|
|
33
44
|
import LoadingMark from "./LoadingMark.vue";
|
|
45
|
+
import { vTooltip } from "./tooltip";
|
|
34
46
|
|
|
35
47
|
type Variant = "primary" | "secondary" | "ghost" | "danger" | "link" | "outline" | "add";
|
|
36
48
|
type Size = "sm" | "md" | "lg";
|
|
37
49
|
|
|
38
|
-
|
|
50
|
+
const props = withDefaults(defineProps<{
|
|
39
51
|
variant?: Variant | undefined;
|
|
40
52
|
size?: Size | undefined;
|
|
53
|
+
/** PrimeIcons name ("plus" or "pi-plus"), drawn before the label. */
|
|
41
54
|
icon?: string | undefined;
|
|
55
|
+
/** PrimeIcons name drawn after the label (a chevron, an external link). */
|
|
42
56
|
iconRight?: string | undefined;
|
|
43
57
|
loading?: boolean | undefined;
|
|
44
58
|
disabled?: boolean | undefined;
|
|
59
|
+
/** Stretch to the container's full width. */
|
|
45
60
|
block?: boolean | undefined;
|
|
46
61
|
/** Makes it a toggle (`aria-pressed`): on, it wears the accent tint. */
|
|
47
62
|
pressed?: boolean | undefined;
|
|
48
63
|
type?: "button" | "submit" | "reset" | undefined;
|
|
49
64
|
ariaLabel?: string | undefined;
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
/** Tooltip text; an icon button shows its aria-label when this is unset. */
|
|
66
|
+
hint?: string | undefined;
|
|
67
|
+
/** A shortcut shown in the tooltip's key chip, e.g. "Esc". */
|
|
68
|
+
kbd?: string | undefined;
|
|
69
|
+
/** `a` draws a link that looks like a button. */
|
|
70
|
+
as?: "button" | "a" | undefined;
|
|
71
|
+
}>(), {
|
|
52
72
|
variant: "secondary",
|
|
53
73
|
size: "md",
|
|
54
74
|
type: "button",
|
|
55
75
|
pressed: undefined,
|
|
76
|
+
icon: undefined,
|
|
77
|
+
iconRight: undefined,
|
|
78
|
+
ariaLabel: undefined,
|
|
79
|
+
hint: undefined,
|
|
80
|
+
kbd: undefined,
|
|
81
|
+
as: "button",
|
|
56
82
|
});
|
|
57
83
|
|
|
58
|
-
const emit = defineEmits<{
|
|
59
|
-
(e: "click", evt: MouseEvent): void;
|
|
60
|
-
}>();
|
|
84
|
+
const emit = defineEmits<{ click: [evt: MouseEvent] }>();
|
|
61
85
|
|
|
62
86
|
const slots = useSlots();
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
/** Native hover tooltip for icon-only buttons. Their `aria-label` names them
|
|
67
|
-
* for screen readers, but a sighted user hovering a bare icon gets nothing —
|
|
68
|
-
* so mirror the label into `title` when there's no visible text. A button with
|
|
69
|
-
* text already says what it does, so it stays untitled. */
|
|
70
|
-
const titleAttr = computed(() =>
|
|
71
|
-
!hasContent.value && props.ariaLabel ? props.ariaLabel : undefined);
|
|
87
|
+
const iconOnly = computed(() => !slots.default);
|
|
88
|
+
const isLink = computed(() => props.as === "a");
|
|
89
|
+
const off = computed(() => Boolean(props.disabled || props.loading));
|
|
72
90
|
|
|
73
91
|
const classes = computed(() => [
|
|
74
92
|
"wp-btn",
|
|
75
93
|
`wp-btn--${props.variant}`,
|
|
76
94
|
`wp-btn--${props.size}`,
|
|
77
|
-
|
|
95
|
+
iconOnly.value && "wp-btn--icon",
|
|
78
96
|
props.block && "wp-btn--block",
|
|
79
97
|
]);
|
|
80
98
|
|
|
81
|
-
|
|
82
|
-
|
|
99
|
+
const markSize = computed(() => (props.size === "lg" ? 16 : props.size === "md" ? 14 : 12));
|
|
100
|
+
|
|
101
|
+
function onClick(e: MouseEvent): void {
|
|
102
|
+
if (off.value) {
|
|
103
|
+
// A link can't be disabled natively; stop it following its href.
|
|
104
|
+
if (isLink.value) e.preventDefault();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
83
107
|
emit("click", e);
|
|
84
108
|
}
|
|
85
109
|
</script>
|
|
86
110
|
|
|
87
111
|
<template>
|
|
88
|
-
<
|
|
89
|
-
:
|
|
112
|
+
<component
|
|
113
|
+
:is="as"
|
|
114
|
+
v-tooltip="{ text: hint ?? (iconOnly ? ariaLabel : undefined), kbd }"
|
|
115
|
+
:type="isLink ? undefined : type"
|
|
90
116
|
:class="classes"
|
|
91
|
-
:disabled="
|
|
117
|
+
:disabled="isLink ? undefined : off"
|
|
118
|
+
:aria-disabled="isLink && off ? 'true' : undefined"
|
|
119
|
+
:tabindex="isLink && off ? -1 : undefined"
|
|
92
120
|
:aria-label="ariaLabel"
|
|
93
|
-
:title="titleAttr"
|
|
94
121
|
:aria-busy="loading || undefined"
|
|
95
122
|
:aria-pressed="pressed === undefined ? undefined : pressed"
|
|
96
123
|
@click="onClick"
|
|
97
124
|
>
|
|
98
|
-
<LoadingMark v-if="loading" variant="sparkle" mono :size="
|
|
125
|
+
<LoadingMark v-if="loading" variant="sparkle" mono :size="markSize" decorative />
|
|
99
126
|
<Icon v-else-if="icon" :name="icon" />
|
|
100
|
-
<span v-if="
|
|
127
|
+
<span v-if="!iconOnly" class="wp-btn__label"><slot /></span>
|
|
101
128
|
<Icon v-if="iconRight" :name="iconRight" />
|
|
102
|
-
</
|
|
129
|
+
</component>
|
|
103
130
|
</template>
|
|
131
|
+
|
|
132
|
+
<style scoped src="./button.css"></style>
|