@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
package/src/button.css CHANGED
@@ -1,169 +1,169 @@
1
- /* Button — the one .wp-btn stylesheet, shared by the manager Button
2
- * (manager/components/ui/Button.vue, loaded globally by manager/main.ts) and
3
- * the canvas twin WpButton (components/shared/WpButton.vue, component
4
- * standard rule 8), which pulls it in scoped. It reads only the
5
- * SHARED_TOKENS names, so it wears the manager colours on a page and the
6
- * canvas colours in a node.
7
- *
8
- * Unlayered on purpose: the manager loads it beside tokens.css, where an
9
- * unlayered reset would otherwise beat it. The canvas never loads it
10
- * globally: WpButton pulls in a scoped copy, so its classes never meet
11
- * another extension's `.wp-btn`.
12
- *
13
- * Heights follow the control scale: sm 24 / md 32 / lg 40.
14
- *
15
- * States:
16
- * disabled — 45% opacity, not-allowed cursor, no hover change; the filled
17
- * secondary drops its fill so it no longer reads as a live
18
- * secondary button.
19
- * loading — keeps its own colours (aria-busy), progress cursor, so the
20
- * label stays readable while it works.
21
- */
22
-
23
- .wp-btn {
24
- height: var(--wp-control-h-md);
25
- padding: 0 12px;
26
- border-radius: min(7px, var(--wp-radius-cap, 999px));
27
- display: inline-flex; align-items: center; justify-content: center; gap: 6px;
28
- font-family: var(--wp-font-sans);
29
- font-size: 12.5px;
30
- font-weight: 500;
31
- /* Normal, not 1: a line box shorter than the font's ascent + descent
32
- gets its negative leading rounded unevenly and the label rides high. */
33
- line-height: normal;
34
- cursor: pointer;
35
- background: var(--wp-bg-3);
36
- color: var(--wp-text);
37
- border: 1px solid var(--wp-border);
38
- transition: background .12s, border-color .12s, color .12s, opacity .12s, transform .04s;
39
- white-space: nowrap;
40
- user-select: none;
41
- box-sizing: border-box;
42
- }
43
- .wp-btn:hover { background: var(--wp-bg-4); border-color: var(--wp-border-strong); }
44
- .wp-btn:active { transform: translateY(0.5px); }
45
- .wp-btn:focus-visible {
46
- outline: none;
47
- box-shadow: var(--wp-focus-ring);
48
- }
49
- .wp-btn .pi, .wp-btn svg { font-size: 13px; }
50
- /* The label box (not on a link, which has no fixed height) is trimmed to
51
- * the cap height where the browser can, so the
52
- * flex centring puts the capitals, not the ascender-to-descender box, on the
53
- * button's middle line, level with the icon. */
54
- @supports (text-box: trim-both cap alphabetic) {
55
- .wp-btn:not(.wp-btn--link) .wp-btn__label { text-box: trim-both cap alphabetic; }
56
- }
57
-
58
- /* Sizes */
59
- .wp-btn--sm { height: var(--wp-control-h-sm); padding: 0 8px; gap: 4px; font-size: 12px; border-radius: min(6px, var(--wp-radius-cap, 999px)); }
60
- .wp-btn--sm .pi, .wp-btn--sm svg { font-size: 12px; }
61
- .wp-btn--md { /* the default size */ }
62
- .wp-btn--lg { height: var(--wp-control-h-lg); padding: 0 var(--wp-space-6); font-size: 13.5px; border-radius: var(--wp-radius); }
63
- .wp-btn--icon { width: var(--wp-control-h-md); padding: 0; }
64
- .wp-btn--icon.wp-btn--sm { width: var(--wp-control-h-sm); }
65
- .wp-btn--icon.wp-btn--lg { width: var(--wp-control-h-lg); }
66
-
67
- /* Variants */
68
- .wp-btn--secondary { /* the default look */ }
69
-
70
- .wp-btn--primary {
71
- background: var(--wp-accent-600);
72
- border-color: var(--wp-accent-600);
73
- color: #fff; /* audit-exempt: white on accent-600 is 6.3:1 */
74
- }
75
- .wp-btn--primary:hover { background: var(--wp-accent-700); border-color: var(--wp-accent-700); }
76
- .wp-btn--primary:active { background: var(--wp-accent-800); border-color: var(--wp-accent-800); }
77
- .wp-btn--primary:focus-visible { box-shadow: var(--wp-focus-ring); }
78
-
79
- .wp-btn--ghost { background: transparent; border-color: transparent; color: var(--wp-text-muted); }
80
- .wp-btn--ghost:hover { background: var(--wp-bg-3); border-color: transparent; color: var(--wp-text); }
81
- .wp-btn--ghost:active { background: var(--wp-bg-4); }
82
-
83
- /* Outline is neutral: a bordered ghost. An accent outline read as a second
84
- * primary next to the real one. */
85
- .wp-btn--outline { background: transparent; border-color: var(--wp-border-strong); color: var(--wp-text); }
86
- .wp-btn--outline:hover { background: var(--wp-bg-3); border-color: var(--wp-border-strong); }
87
- .wp-btn--outline:active { background: var(--wp-bg-4); }
88
-
89
- .wp-btn--link {
90
- background: transparent;
91
- border-color: transparent;
92
- color: var(--wp-accent-text);
93
- height: auto;
94
- padding: 0;
95
- line-height: 1;
96
- }
97
- .wp-btn--link:hover {
98
- background: transparent;
99
- border-color: transparent;
100
- color: var(--wp-accent-text-strong);
101
- text-decoration: underline;
102
- }
103
- .wp-btn--link:focus-visible { box-shadow: none; text-decoration: underline; }
104
-
105
- .wp-btn--danger { color: var(--wp-danger); }
106
- .wp-btn--danger:hover {
107
- background: color-mix(in oklab, var(--wp-danger) 14%, transparent);
108
- border-color: color-mix(in oklab, var(--wp-danger) 40%, transparent);
109
- color: var(--wp-danger-text);
110
- }
111
- .wp-btn--danger:active { background: color-mix(in oklab, var(--wp-danger) 22%, transparent); }
112
- .wp-btn--danger:focus-visible {
113
- box-shadow: 0 0 0 3px color-mix(in oklab, var(--wp-danger) 35%, transparent); /* danger uses its own ring color */
114
- }
115
-
116
- /* Add: the quiet dashed "Add a row" button at the end of a list (Injector
117
- * template rows, Model Info rules, fixed values, constraint exceptions). */
118
- .wp-btn--add {
119
- background: transparent;
120
- border-style: dashed;
121
- border-color: var(--wp-border-strong);
122
- color: var(--wp-text-dim);
123
- font-weight: 600;
124
- }
125
- .wp-btn--add:hover {
126
- background: color-mix(in srgb, var(--wp-accent) 8%, transparent);
127
- border-color: color-mix(in srgb, var(--wp-accent) 45%, var(--wp-border-strong));
128
- color: var(--wp-text);
129
- }
130
- .wp-btn--add:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { background: transparent; border-color: var(--wp-border-strong); color: var(--wp-text-dim); }
131
-
132
- .wp-btn--block { display: flex; width: 100%; }
133
-
134
- /* Pressed: a toggle that is on (favourites only, filters open). */
135
- .wp-btn[aria-pressed="true"] {
136
- background: color-mix(in srgb, var(--wp-accent) 14%, transparent);
137
- border-color: color-mix(in srgb, var(--wp-accent) 55%, var(--wp-border));
138
- color: var(--wp-accent-text);
139
- }
140
- .wp-btn[aria-pressed="true"]:hover {
141
- background: color-mix(in srgb, var(--wp-accent) 22%, transparent);
142
- border-color: var(--wp-accent);
143
- }
144
-
145
- /* Disabled (not loading): faded, flat, and hover does nothing. */
146
- .wp-btn:disabled:not([aria-busy="true"]),
147
- .wp-btn[aria-disabled="true"] {
148
- opacity: 0.45;
149
- cursor: not-allowed;
150
- transform: none;
151
- }
152
- .wp-btn--secondary:disabled:not([aria-busy="true"]),
153
- .wp-btn--secondary[aria-disabled="true"] {
154
- background: transparent;
155
- border-color: var(--wp-border);
156
- color: var(--wp-text-muted);
157
- }
158
- .wp-btn--secondary:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { background: transparent; border-color: var(--wp-border); }
159
- .wp-btn--primary:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover {
160
- background: var(--wp-accent-600);
161
- border-color: var(--wp-accent-600);
162
- }
163
- .wp-btn--ghost:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { background: transparent; color: var(--wp-text-muted); }
164
- .wp-btn--outline:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { background: transparent; border-color: var(--wp-border-strong); }
165
- .wp-btn--danger:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { background: var(--wp-bg-3); border-color: var(--wp-border); color: var(--wp-danger); }
166
- .wp-btn--link:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { color: var(--wp-accent-text); text-decoration: none; }
167
-
168
- /* Loading keeps the button's own colours; only the cursor says "wait". */
169
- .wp-btn[aria-busy="true"] { cursor: progress; transform: none; }
1
+ /* Button — the one .wp-btn stylesheet, shared by the manager Button
2
+ * (manager/components/ui/Button.vue, loaded globally by manager/main.ts) and
3
+ * the canvas twin WpButton (components/shared/WpButton.vue, component
4
+ * standard rule 8), which pulls it in scoped. It reads only the
5
+ * SHARED_TOKENS names, so it wears the manager colours on a page and the
6
+ * canvas colours in a node.
7
+ *
8
+ * Unlayered on purpose: the manager loads it beside tokens.css, where an
9
+ * unlayered reset would otherwise beat it. The canvas never loads it
10
+ * globally: WpButton pulls in a scoped copy, so its classes never meet
11
+ * another extension's `.wp-btn`.
12
+ *
13
+ * Heights follow the control scale: sm 24 / md 32 / lg 40.
14
+ *
15
+ * States:
16
+ * disabled — 45% opacity, not-allowed cursor, no hover change; the filled
17
+ * secondary drops its fill so it no longer reads as a live
18
+ * secondary button.
19
+ * loading — keeps its own colours (aria-busy), progress cursor, so the
20
+ * label stays readable while it works.
21
+ */
22
+
23
+ .wp-btn {
24
+ height: var(--wp-control-h-md);
25
+ padding: 0 12px;
26
+ border-radius: min(7px, var(--wp-radius-cap, 999px));
27
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
28
+ font-family: var(--wp-font-sans);
29
+ font-size: 12.5px;
30
+ font-weight: 500;
31
+ /* Normal, not 1: a line box shorter than the font's ascent + descent
32
+ gets its negative leading rounded unevenly and the label rides high. */
33
+ line-height: normal;
34
+ cursor: pointer;
35
+ background: var(--wp-bg-3);
36
+ color: var(--wp-text);
37
+ border: 1px solid var(--wp-border);
38
+ transition: background .12s, border-color .12s, color .12s, opacity .12s, transform .04s;
39
+ white-space: nowrap;
40
+ user-select: none;
41
+ box-sizing: border-box;
42
+ }
43
+ .wp-btn:hover { background: var(--wp-bg-4); border-color: var(--wp-border-strong); }
44
+ .wp-btn:active { transform: translateY(0.5px); }
45
+ .wp-btn:focus-visible {
46
+ outline: none;
47
+ box-shadow: var(--wp-focus-ring);
48
+ }
49
+ .wp-btn .pi, .wp-btn svg { font-size: 13px; }
50
+ /* The label box (not on a link, which has no fixed height) is trimmed to
51
+ * the cap height where the browser can, so the
52
+ * flex centring puts the capitals, not the ascender-to-descender box, on the
53
+ * button's middle line, level with the icon. */
54
+ @supports (text-box: trim-both cap alphabetic) {
55
+ .wp-btn:not(.wp-btn--link) .wp-btn__label { text-box: trim-both cap alphabetic; }
56
+ }
57
+
58
+ /* Sizes */
59
+ .wp-btn--sm { height: var(--wp-control-h-sm); padding: 0 8px; gap: 4px; font-size: 12px; border-radius: min(6px, var(--wp-radius-cap, 999px)); }
60
+ .wp-btn--sm .pi, .wp-btn--sm svg { font-size: 12px; }
61
+ .wp-btn--md { /* the default size */ }
62
+ .wp-btn--lg { height: var(--wp-control-h-lg); padding: 0 var(--wp-space-6); font-size: 13.5px; border-radius: var(--wp-radius); }
63
+ .wp-btn--icon { width: var(--wp-control-h-md); padding: 0; }
64
+ .wp-btn--icon.wp-btn--sm { width: var(--wp-control-h-sm); }
65
+ .wp-btn--icon.wp-btn--lg { width: var(--wp-control-h-lg); }
66
+
67
+ /* Variants */
68
+ .wp-btn--secondary { /* the default look */ }
69
+
70
+ .wp-btn--primary {
71
+ background: var(--wp-accent-600);
72
+ border-color: var(--wp-accent-600);
73
+ color: #fff; /* audit-exempt: white on accent-600 is 6.3:1 */
74
+ }
75
+ .wp-btn--primary:hover { background: var(--wp-accent-700); border-color: var(--wp-accent-700); }
76
+ .wp-btn--primary:active { background: var(--wp-accent-800); border-color: var(--wp-accent-800); }
77
+ .wp-btn--primary:focus-visible { box-shadow: var(--wp-focus-ring); }
78
+
79
+ .wp-btn--ghost { background: transparent; border-color: transparent; color: var(--wp-text-muted); }
80
+ .wp-btn--ghost:hover { background: var(--wp-bg-3); border-color: transparent; color: var(--wp-text); }
81
+ .wp-btn--ghost:active { background: var(--wp-bg-4); }
82
+
83
+ /* Outline is neutral: a bordered ghost. An accent outline read as a second
84
+ * primary next to the real one. */
85
+ .wp-btn--outline { background: transparent; border-color: var(--wp-border-strong); color: var(--wp-text); }
86
+ .wp-btn--outline:hover { background: var(--wp-bg-3); border-color: var(--wp-border-strong); }
87
+ .wp-btn--outline:active { background: var(--wp-bg-4); }
88
+
89
+ .wp-btn--link {
90
+ background: transparent;
91
+ border-color: transparent;
92
+ color: var(--wp-accent-text);
93
+ height: auto;
94
+ padding: 0;
95
+ line-height: 1;
96
+ }
97
+ .wp-btn--link:hover {
98
+ background: transparent;
99
+ border-color: transparent;
100
+ color: var(--wp-accent-text-strong);
101
+ text-decoration: underline;
102
+ }
103
+ .wp-btn--link:focus-visible { box-shadow: none; text-decoration: underline; }
104
+
105
+ .wp-btn--danger { color: var(--wp-danger); }
106
+ .wp-btn--danger:hover {
107
+ background: color-mix(in oklab, var(--wp-danger) 14%, transparent);
108
+ border-color: color-mix(in oklab, var(--wp-danger) 40%, transparent);
109
+ color: var(--wp-danger-text);
110
+ }
111
+ .wp-btn--danger:active { background: color-mix(in oklab, var(--wp-danger) 22%, transparent); }
112
+ .wp-btn--danger:focus-visible {
113
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--wp-danger) 35%, transparent); /* danger uses its own ring color */
114
+ }
115
+
116
+ /* Add: the quiet dashed "Add a row" button at the end of a list (Injector
117
+ * template rows, Model Info rules, fixed values, constraint exceptions). */
118
+ .wp-btn--add {
119
+ background: transparent;
120
+ border-style: dashed;
121
+ border-color: var(--wp-border-strong);
122
+ color: var(--wp-text-dim);
123
+ font-weight: 600;
124
+ }
125
+ .wp-btn--add:hover {
126
+ background: color-mix(in srgb, var(--wp-accent) 8%, transparent);
127
+ border-color: color-mix(in srgb, var(--wp-accent) 45%, var(--wp-border-strong));
128
+ color: var(--wp-text);
129
+ }
130
+ .wp-btn--add:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { background: transparent; border-color: var(--wp-border-strong); color: var(--wp-text-dim); }
131
+
132
+ .wp-btn--block { display: flex; width: 100%; }
133
+
134
+ /* Pressed: a toggle that is on (favourites only, filters open). */
135
+ .wp-btn[aria-pressed="true"] {
136
+ background: color-mix(in srgb, var(--wp-accent) 14%, transparent);
137
+ border-color: color-mix(in srgb, var(--wp-accent) 55%, var(--wp-border));
138
+ color: var(--wp-accent-text);
139
+ }
140
+ .wp-btn[aria-pressed="true"]:hover {
141
+ background: color-mix(in srgb, var(--wp-accent) 22%, transparent);
142
+ border-color: var(--wp-accent);
143
+ }
144
+
145
+ /* Disabled (not loading): faded, flat, and hover does nothing. */
146
+ .wp-btn:disabled:not([aria-busy="true"]),
147
+ .wp-btn[aria-disabled="true"] {
148
+ opacity: 0.45;
149
+ cursor: not-allowed;
150
+ transform: none;
151
+ }
152
+ .wp-btn--secondary:disabled:not([aria-busy="true"]),
153
+ .wp-btn--secondary[aria-disabled="true"] {
154
+ background: transparent;
155
+ border-color: var(--wp-border);
156
+ color: var(--wp-text-muted);
157
+ }
158
+ .wp-btn--secondary:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { background: transparent; border-color: var(--wp-border); }
159
+ .wp-btn--primary:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover {
160
+ background: var(--wp-accent-600);
161
+ border-color: var(--wp-accent-600);
162
+ }
163
+ .wp-btn--ghost:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { background: transparent; color: var(--wp-text-muted); }
164
+ .wp-btn--outline:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { background: transparent; border-color: var(--wp-border-strong); }
165
+ .wp-btn--danger:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { background: var(--wp-bg-3); border-color: var(--wp-border); color: var(--wp-danger); }
166
+ .wp-btn--link:is(:disabled:not([aria-busy="true"]), [aria-disabled="true"]):hover { color: var(--wp-accent-text); text-decoration: none; }
167
+
168
+ /* Loading keeps the button's own colours; only the cursor says "wait". */
169
+ .wp-btn[aria-busy="true"] { cursor: progress; transform: none; }
package/src/calendar.ts CHANGED
@@ -1,59 +1,59 @@
1
- /**
2
- * Date helpers for DatePicker. Dates travel as "YYYY-MM-DD" strings (local
3
- * calendar days, no time zone), so a saved filter means the same day
4
- * everywhere.
5
- */
6
- export interface Ymd { y: number; m: number; d: number } // m is 0-11
7
-
8
- export function parseYmd(s: string | null | undefined): Ymd | null {
9
- const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s ?? "");
10
- if (!m) return null;
11
- const y = Number(m[1]); const mo = Number(m[2]) - 1; const d = Number(m[3]);
12
- const dt = new Date(y, mo, d);
13
- return dt.getFullYear() === y && dt.getMonth() === mo && dt.getDate() === d ? { y, m: mo, d } : null;
14
- }
15
-
16
- export function toYmd(v: Ymd): string {
17
- return `${String(v.y).padStart(4, "0")}-${String(v.m + 1).padStart(2, "0")}-${String(v.d).padStart(2, "0")}`;
18
- }
19
-
20
- export function fromDate(dt: Date): Ymd {
21
- return { y: dt.getFullYear(), m: dt.getMonth(), d: dt.getDate() };
22
- }
23
-
24
- export function addDays(v: Ymd, n: number): Ymd {
25
- return fromDate(new Date(v.y, v.m, v.d + n));
26
- }
27
-
28
- /** The same day n months on, kept inside the shorter month (31 Jan + 1 → 28/29 Feb). */
29
- export function addMonths(v: Ymd, n: number): Ymd {
30
- const first = new Date(v.y, v.m + n, 1);
31
- const last = new Date(first.getFullYear(), first.getMonth() + 1, 0).getDate();
32
- return { y: first.getFullYear(), m: first.getMonth(), d: Math.min(v.d, last) };
33
- }
34
-
35
- export function compareYmd(a: Ymd, b: Ymd): number {
36
- return a.y - b.y || a.m - b.m || a.d - b.d;
37
- }
38
-
39
- /**
40
- * The 6×7 grid of days shown for a month, starting on `weekStart`
41
- * (0 = Sunday, 1 = Monday), with days of the months either side.
42
- */
43
- export function monthGrid(y: number, m: number, weekStart = 1): Ymd[] {
44
- const first = new Date(y, m, 1);
45
- const lead = (first.getDay() - weekStart + 7) % 7;
46
- return Array.from({ length: 42 }, (_, i) => fromDate(new Date(y, m, 1 - lead + i)));
47
- }
48
-
49
- /** Short weekday names from `weekStart`, in the browser's language. */
50
- export function weekdayNames(weekStart = 1, locale?: string): { short: string; long: string }[] {
51
- // 2024-01-07 was a Sunday.
52
- return Array.from({ length: 7 }, (_, i) => {
53
- const dt = new Date(2024, 0, 7 + ((weekStart + i) % 7));
54
- return {
55
- short: dt.toLocaleDateString(locale, { weekday: "narrow" }),
56
- long: dt.toLocaleDateString(locale, { weekday: "long" }),
57
- };
58
- });
59
- }
1
+ /**
2
+ * Date helpers for DatePicker. Dates travel as "YYYY-MM-DD" strings (local
3
+ * calendar days, no time zone), so a saved filter means the same day
4
+ * everywhere.
5
+ */
6
+ export interface Ymd { y: number; m: number; d: number } // m is 0-11
7
+
8
+ export function parseYmd(s: string | null | undefined): Ymd | null {
9
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s ?? "");
10
+ if (!m) return null;
11
+ const y = Number(m[1]); const mo = Number(m[2]) - 1; const d = Number(m[3]);
12
+ const dt = new Date(y, mo, d);
13
+ return dt.getFullYear() === y && dt.getMonth() === mo && dt.getDate() === d ? { y, m: mo, d } : null;
14
+ }
15
+
16
+ export function toYmd(v: Ymd): string {
17
+ return `${String(v.y).padStart(4, "0")}-${String(v.m + 1).padStart(2, "0")}-${String(v.d).padStart(2, "0")}`;
18
+ }
19
+
20
+ export function fromDate(dt: Date): Ymd {
21
+ return { y: dt.getFullYear(), m: dt.getMonth(), d: dt.getDate() };
22
+ }
23
+
24
+ export function addDays(v: Ymd, n: number): Ymd {
25
+ return fromDate(new Date(v.y, v.m, v.d + n));
26
+ }
27
+
28
+ /** The same day n months on, kept inside the shorter month (31 Jan + 1 → 28/29 Feb). */
29
+ export function addMonths(v: Ymd, n: number): Ymd {
30
+ const first = new Date(v.y, v.m + n, 1);
31
+ const last = new Date(first.getFullYear(), first.getMonth() + 1, 0).getDate();
32
+ return { y: first.getFullYear(), m: first.getMonth(), d: Math.min(v.d, last) };
33
+ }
34
+
35
+ export function compareYmd(a: Ymd, b: Ymd): number {
36
+ return a.y - b.y || a.m - b.m || a.d - b.d;
37
+ }
38
+
39
+ /**
40
+ * The 6×7 grid of days shown for a month, starting on `weekStart`
41
+ * (0 = Sunday, 1 = Monday), with days of the months either side.
42
+ */
43
+ export function monthGrid(y: number, m: number, weekStart = 1): Ymd[] {
44
+ const first = new Date(y, m, 1);
45
+ const lead = (first.getDay() - weekStart + 7) % 7;
46
+ return Array.from({ length: 42 }, (_, i) => fromDate(new Date(y, m, 1 - lead + i)));
47
+ }
48
+
49
+ /** Short weekday names from `weekStart`, in the browser's language. */
50
+ export function weekdayNames(weekStart = 1, locale?: string): { short: string; long: string }[] {
51
+ // 2024-01-07 was a Sunday.
52
+ return Array.from({ length: 7 }, (_, i) => {
53
+ const dt = new Date(2024, 0, 7 + ((weekStart + i) % 7));
54
+ return {
55
+ short: dt.toLocaleDateString(locale, { weekday: "narrow" }),
56
+ long: dt.toLocaleDateString(locale, { weekday: "long" }),
57
+ };
58
+ });
59
+ }
package/src/clipboard.ts CHANGED
@@ -1,50 +1,50 @@
1
- /**
2
- * clipboard — copy text, also where `navigator.clipboard` is missing.
3
- *
4
- * Browsers only give `navigator.clipboard` to secure pages (https or
5
- * localhost), so ComfyUI opened over plain http on the LAN has none. There a
6
- * hidden textarea is selected and copied with `execCommand("copy")`, which
7
- * still works inside a click.
8
- */
9
-
10
- /** Copy `text`; true when it reached the clipboard. */
11
- export async function copyText(text: string): Promise<boolean> {
12
- const api = typeof navigator === "undefined" ? undefined : navigator.clipboard;
13
- if (api?.writeText) {
14
- try {
15
- await api.writeText(text);
16
- return true;
17
- } catch {
18
- // Refused (no permission, page not focused): try the old way.
19
- }
20
- }
21
- return copyByTextarea(text);
22
- }
23
-
24
- function copyByTextarea(text: string): boolean {
25
- if (typeof document === "undefined" || !document.body) return false;
26
- const area = document.createElement("textarea");
27
- area.value = text;
28
- area.setAttribute("readonly", "");
29
- area.setAttribute("aria-hidden", "true");
30
- area.style.cssText = "position:fixed;top:0;left:0;width:1px;height:1px;opacity:0;pointer-events:none;";
31
- const focused = document.activeElement;
32
- const selection = document.getSelection();
33
- const range = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
34
- document.body.appendChild(area);
35
- area.focus({ preventScroll: true });
36
- area.select();
37
- let ok = false;
38
- try {
39
- ok = typeof document.execCommand === "function" && document.execCommand("copy");
40
- } catch {
41
- ok = false;
42
- }
43
- area.remove();
44
- if (range && selection) {
45
- selection.removeAllRanges();
46
- selection.addRange(range);
47
- }
48
- if (focused instanceof HTMLElement) focused.focus();
49
- return ok;
50
- }
1
+ /**
2
+ * clipboard — copy text, also where `navigator.clipboard` is missing.
3
+ *
4
+ * Browsers only give `navigator.clipboard` to secure pages (https or
5
+ * localhost), so ComfyUI opened over plain http on the LAN has none. There a
6
+ * hidden textarea is selected and copied with `execCommand("copy")`, which
7
+ * still works inside a click.
8
+ */
9
+
10
+ /** Copy `text`; true when it reached the clipboard. */
11
+ export async function copyText(text: string): Promise<boolean> {
12
+ const api = typeof navigator === "undefined" ? undefined : navigator.clipboard;
13
+ if (api?.writeText) {
14
+ try {
15
+ await api.writeText(text);
16
+ return true;
17
+ } catch {
18
+ // Refused (no permission, page not focused): try the old way.
19
+ }
20
+ }
21
+ return copyByTextarea(text);
22
+ }
23
+
24
+ function copyByTextarea(text: string): boolean {
25
+ if (typeof document === "undefined" || !document.body) return false;
26
+ const area = document.createElement("textarea");
27
+ area.value = text;
28
+ area.setAttribute("readonly", "");
29
+ area.setAttribute("aria-hidden", "true");
30
+ area.style.cssText = "position:fixed;top:0;left:0;width:1px;height:1px;opacity:0;pointer-events:none;";
31
+ const focused = document.activeElement;
32
+ const selection = document.getSelection();
33
+ const range = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
34
+ document.body.appendChild(area);
35
+ area.focus({ preventScroll: true });
36
+ area.select();
37
+ let ok = false;
38
+ try {
39
+ ok = typeof document.execCommand === "function" && document.execCommand("copy");
40
+ } catch {
41
+ ok = false;
42
+ }
43
+ area.remove();
44
+ if (range && selection) {
45
+ selection.removeAllRanges();
46
+ selection.addRange(range);
47
+ }
48
+ if (focused instanceof HTMLElement) focused.focus();
49
+ return ok;
50
+ }
@@ -1,12 +1,12 @@
1
- /** The preset colours the colour pickers offer (bundle frames, categories),
2
- * and the default a new one starts with. One list, so every picker and the
3
- * canvas offer the same swatches. Brand colours, not theme tokens: a picked
4
- * colour is stored as hex and looks the same in light and dark. */
5
- export const COLOUR_PRESETS: readonly string[] = [
6
- "#7c3aed", "#a78bfa", "#22d3ee", "#34d399",
7
- "#fbbf24", "#f472b6", "#fb7185", "#ef4444",
8
- "#6366f1", "#10b981", "#f59e0b", "#8b5cf6",
9
- ];
10
-
11
- /** The colour a new bundle or category starts with. */
12
- export const DEFAULT_COLOUR = "#6366f1";
1
+ /** The preset colours the colour pickers offer (bundle frames, categories),
2
+ * and the default a new one starts with. One list, so every picker and the
3
+ * canvas offer the same swatches. Brand colours, not theme tokens: a picked
4
+ * colour is stored as hex and looks the same in light and dark. */
5
+ export const COLOUR_PRESETS: readonly string[] = [
6
+ "#7c3aed", "#a78bfa", "#22d3ee", "#34d399",
7
+ "#fbbf24", "#f472b6", "#fb7185", "#ef4444",
8
+ "#6366f1", "#10b981", "#f59e0b", "#8b5cf6",
9
+ ];
10
+
11
+ /** The colour a new bundle or category starts with. */
12
+ export const DEFAULT_COLOUR = "#6366f1";