@celestia-island/hikari 0.55.288 → 0.55.290
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/package.json +1 -1
- package/src/components/HkConfirmDialog.scss +10 -7
- package/src/components/HkConfirmDialog.tsx +32 -25
- package/src/components/HkJsonTree.contract.test.ts +85 -0
- package/src/components/HkJsonTree.fold.test.ts +521 -0
- package/src/components/HkJsonTree.scss +52 -4
- package/src/components/HkJsonTree.test.tsx +24 -3
- package/src/components/HkJsonTree.tsx +329 -54
- package/src/components/HkModal.open-close-scale.test.ts +499 -114
- package/src/components/HkModal.scss +82 -59
- package/src/components/HkModal.test.tsx +70 -0
- package/src/components/HkScrollPin.scrollcontract.test.ts +16 -2
- package/src/styles/selection.scss +5 -4
- package/src/styles/selectionSheet.test.ts +17 -3
package/package.json
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Compact confirmation sheet — sized like a dropdown popover (24rem) so
|
|
4
4
|
// short messages do not float in a half-empty 32rem modal. The message
|
|
5
|
-
// block centers vertically between the title and the actions; the
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// block centers vertically between the title and the actions; the action
|
|
6
|
+
// pair lives in the modal's footer band (2026-11-05 move — the same slot
|
|
7
|
+
// HkMessageBox hosts its pair in) and right-aligns there at its natural
|
|
8
|
+
// width, the two buttons still sharing that row equally via flex; on
|
|
9
|
+
// touch-width viewports they grow to the 44px thumb standard.
|
|
8
10
|
|
|
9
11
|
@use "../styles/polyfill" as *;
|
|
10
12
|
|
|
@@ -38,9 +40,11 @@
|
|
|
38
40
|
min-width: 0;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
/* Touch-width viewports: thumb
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
/* Touch-width viewports: keep the 44px thumb target on the pair. The
|
|
44
|
+
modal footer's own mobile lift owns padding/font-size for sm/md
|
|
45
|
+
buttons inside the band (HkModal.scss .hk-modal-footer .hk-btn-md),
|
|
46
|
+
so the old 15px font-size override stayed behind on the move into
|
|
47
|
+
the footer — buttons read like every other lifted footer button. */
|
|
44
48
|
@media (max-width: 640px) {
|
|
45
49
|
.hk-confirm-dialog {
|
|
46
50
|
gap: 1rem;
|
|
@@ -48,7 +52,6 @@
|
|
|
48
52
|
|
|
49
53
|
.hk-confirm-dialog-btn {
|
|
50
54
|
min-height: 44px;
|
|
51
|
-
font-size: 0.9375rem;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
.hk-confirm-dialog-actions {
|
|
@@ -67,31 +67,38 @@ export default defineComponent({
|
|
|
67
67
|
default: () => (
|
|
68
68
|
<div class="hk-confirm-dialog">
|
|
69
69
|
<p class="hk-confirm-dialog-message">{props.message}</p>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
70
|
+
</div>
|
|
71
|
+
),
|
|
72
|
+
/* The confirm pair lives in the modal's standard footer band —
|
|
73
|
+
* it used to render inline under the message while HkMessageBox
|
|
74
|
+
* (the sibling MODAL confirmation surface) already hosted the
|
|
75
|
+
* same pair in this exact slot. HkBlockingToast keeps its own
|
|
76
|
+
* inline pair by design — it is a toast card, not a modal — so
|
|
77
|
+
* the family now has exactly two placements: this footer band
|
|
78
|
+
* for every modal-hosted confirm, and the toast card's inline
|
|
79
|
+
* row. Primary action first, dismiss second — the message-box
|
|
80
|
+
* order; it mirrors under `dir="rtl"` because the row only
|
|
81
|
+
* reverses visually. */
|
|
82
|
+
footer: () => (
|
|
83
|
+
<div class="hk-confirm-dialog-actions">
|
|
84
|
+
<HButton
|
|
85
|
+
class="hk-confirm-dialog-btn"
|
|
86
|
+
variant={props.confirmVariant}
|
|
87
|
+
size="md"
|
|
88
|
+
loading={props.loading}
|
|
89
|
+
onClick={onConfirm}
|
|
90
|
+
>
|
|
91
|
+
{props.confirmLabel || t("hikari::confirmDialog.confirm", "Confirm")}
|
|
92
|
+
</HButton>
|
|
93
|
+
<HButton
|
|
94
|
+
class="hk-confirm-dialog-btn"
|
|
95
|
+
variant="secondary"
|
|
96
|
+
size="md"
|
|
97
|
+
disabled={props.loading}
|
|
98
|
+
onClick={onCancel}
|
|
99
|
+
>
|
|
100
|
+
{props.cancelLabel || t("hikari::confirmDialog.cancel", "Cancel")}
|
|
101
|
+
</HButton>
|
|
95
102
|
</div>
|
|
96
103
|
),
|
|
97
104
|
}}
|
|
@@ -113,3 +113,88 @@ describe("HkJsonTree spacing contract", () => {
|
|
|
113
113
|
expect(scss).not.toMatch(/margin-inline-start: 0\.\d+em/);
|
|
114
114
|
});
|
|
115
115
|
});
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Selection + fold-motion contract (2026-11-05 user direction).
|
|
119
|
+
*
|
|
120
|
+
* The tree is a viewer, not a document: rows must NOT be selectable by
|
|
121
|
+
* default, and the only re-enable site is the explicit `selectable` opt-in.
|
|
122
|
+
* The fold is animated through the shared motion family — the JS side reads
|
|
123
|
+
* the two `--jt-fold-*` properties, so a theme retime moves the tree with the
|
|
124
|
+
* rest of the system; the chevron is ONE glyph rotated by those same tokens
|
|
125
|
+
* (not a glyph swap) and must honour reduced motion.
|
|
126
|
+
*/
|
|
127
|
+
describe("HkJsonTree selection + motion contract", () => {
|
|
128
|
+
const rowBlock = scss.slice(scss.indexOf(".s-jt-row {"), scss.indexOf("/* The ONLY place"));
|
|
129
|
+
|
|
130
|
+
it("denies text selection on rows by default", () => {
|
|
131
|
+
expect(rowBlock, ".s-jt-row rule present").not.toBe("");
|
|
132
|
+
expect(rowBlock).toContain("@include user-select(none)");
|
|
133
|
+
expect(rowBlock).not.toContain("@include user-select(text)");
|
|
134
|
+
// iOS long-press must not raise the callout menu over a row that is
|
|
135
|
+
// itself a toggle target; the opt-in rule is the only place that lifts it.
|
|
136
|
+
const treeBlock = scss.slice(scss.indexOf(".s-tool-json-tree {"), scss.indexOf("/* ONE SCROLLBAR"));
|
|
137
|
+
expect(treeBlock).toContain("-webkit-touch-callout: none;");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("re-enables selection only through the explicit selectable hook", () => {
|
|
141
|
+
expect(scss).toContain(".s-tool-json-tree.s-jt-selectable .s-jt-row {");
|
|
142
|
+
expect(scss).toContain("@include user-select(text)");
|
|
143
|
+
// Exactly one opt-in selector site: a second selector that re-enables
|
|
144
|
+
// selection without the host asking would silently undo the contract.
|
|
145
|
+
expect(scss.match(/s-jt-selectable/g)?.length).toBe(2); // the comment + this selector
|
|
146
|
+
expect(scss.slice(scss.indexOf(".s-tool-json-tree.s-jt-selectable .s-jt-row {"))).toContain(
|
|
147
|
+
"-webkit-touch-callout: default;",
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("seeds the fold tokens from the shared motion family", () => {
|
|
152
|
+
expect(scss).toContain(
|
|
153
|
+
"--jt-fold-duration: var(--hi-duration-fast, var(--duration-fast, 0.15s));",
|
|
154
|
+
);
|
|
155
|
+
expect(scss).toContain(
|
|
156
|
+
"--jt-fold-ease: var(--ease-standard, cubic-bezier(0.4, 0, 0.2, 1));",
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("rotates one chevron with the fold tokens instead of swapping glyphs", () => {
|
|
161
|
+
const tsx = readFileSync(join(here, "HkJsonTree.tsx"), "utf-8");
|
|
162
|
+
expect(tsx).toContain('class="s-jt-chevron"');
|
|
163
|
+
expect(tsx).not.toContain("ChevronDown");
|
|
164
|
+
expect(scss).toContain(".s-jt-group[data-open] > .s-jt-row .s-jt-chevron {");
|
|
165
|
+
expect(scss).toContain("transform: rotate(90deg);");
|
|
166
|
+
// Child combinator, not a descendant: an open group must not rotate the
|
|
167
|
+
// chevrons of collapsed grandchildren nested below it.
|
|
168
|
+
const rotateAt = scss.indexOf(".s-jt-group[data-open] > .s-jt-row .s-jt-chevron {");
|
|
169
|
+
expect(rotateAt).toBeGreaterThanOrEqual(0);
|
|
170
|
+
const rotateRule = scss.slice(rotateAt, scss.indexOf("}", rotateAt));
|
|
171
|
+
expect(rotateRule).toContain("rotate(90deg)");
|
|
172
|
+
const chevron = scss.slice(scss.indexOf(".s-jt-chevron {"), scss.indexOf(".s-jt-group[data-open]"));
|
|
173
|
+
expect(chevron).toContain("var(--jt-fold-duration");
|
|
174
|
+
expect(chevron).toContain("var(--jt-fold-ease");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("switches the chevron rotation off under reduced motion", () => {
|
|
178
|
+
// The whole rule must live INSIDE the media block: slicing to EOF would
|
|
179
|
+
// also pass if a later bare `.s-jt-chevron { transition: none }` was
|
|
180
|
+
// added outside it (R2 2026-11-05), which would disable the rotation
|
|
181
|
+
// for everyone instead of just reduced-motion users.
|
|
182
|
+
expect(scss).toMatch(
|
|
183
|
+
/@media \(prefers-reduced-motion: reduce\) \{\s*\.s-jt-chevron \{\s*transition: none;\s*\}\s*\}/,
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("keeps the JS fallback duration equal to the sheet fallback", () => {
|
|
188
|
+
const tsx = readFileSync(join(here, "HkJsonTree.tsx"), "utf-8");
|
|
189
|
+
const jsFallback = /const JT_FOLD_FALLBACK_MS = (\d+);/.exec(tsx)?.[1];
|
|
190
|
+
expect(jsFallback).toBe("150");
|
|
191
|
+
expect(scss).toContain(
|
|
192
|
+
"--jt-fold-duration: var(--hi-duration-fast, var(--duration-fast, 0.15s));",
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("pins the selectable prop default to off", () => {
|
|
197
|
+
const tsx = readFileSync(join(here, "HkJsonTree.tsx"), "utf-8");
|
|
198
|
+
expect(tsx).toContain("selectable: { type: Boolean, default: false }");
|
|
199
|
+
});
|
|
200
|
+
});
|