@astryxdesign/core 0.1.3 → 0.1.4-canary.02643db
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/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +29 -8
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +49 -20
- package/dist/Calendar/dayCellUtils.d.ts +67 -5
- package/dist/Calendar/dayCellUtils.d.ts.map +1 -1
- package/dist/Calendar/dayCellUtils.js +139 -18
- package/dist/CheckboxInput/CheckboxInput.d.ts +6 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +7 -1
- package/dist/Code/Code.d.ts +15 -1
- package/dist/Code/Code.d.ts.map +1 -1
- package/dist/Code/Code.js +32 -2
- package/dist/Code/index.d.ts +1 -1
- package/dist/Code/index.d.ts.map +1 -1
- package/dist/CodeBlock/index.d.ts +1 -1
- package/dist/CodeBlock/index.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts +1 -1
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteEmpty.js +24 -4
- package/dist/ContextMenu/ContextMenu.d.ts +9 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +6 -3
- package/dist/DateRangeInput/DateRangeInput.js +2 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts +0 -5
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/Divider/Divider.d.ts +0 -22
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Markdown/parser.d.ts +7 -0
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +300 -6
- package/dist/MultiSelector/MultiSelector.d.ts +7 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +10 -1
- package/dist/RadioList/RadioList.d.ts +7 -1
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +3 -1
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +6 -1
- package/dist/Selector/Selector.d.ts +6 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +9 -0
- package/dist/Slider/Slider.d.ts +6 -0
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +10 -1
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +1 -1
- package/dist/Switch/Switch.d.ts +6 -1
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +7 -1
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +8 -1
- package/dist/Table/TableCell.d.ts.map +1 -1
- package/dist/Table/TableCell.js +73 -4
- package/dist/Table/index.d.ts +2 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +1 -0
- package/dist/Table/plugins/rowExpansion/index.d.ts +3 -0
- package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -0
- package/dist/Table/plugins/rowExpansion/index.js +3 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +90 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +398 -0
- package/dist/Table/tableContextMenu.d.ts +5 -1
- package/dist/Table/tableContextMenu.d.ts.map +1 -1
- package/dist/Table/tableContextMenu.js +10 -3
- package/dist/Text/Text.d.ts +1 -1
- package/dist/Text/Text.d.ts.map +1 -1
- package/dist/Text/Text.js +4 -3
- package/dist/Text/text.stylex.d.ts +35 -0
- package/dist/Text/text.stylex.d.ts.map +1 -1
- package/dist/Text/text.stylex.js +52 -1
- package/dist/Tokenizer/Tokenizer.d.ts +6 -1
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +10 -1
- package/dist/astryx.css +16 -0
- package/dist/astryx.umd.js +51 -49
- package/dist/astryx.umd.js.map +4 -4
- package/dist/docs-types.d.ts +12 -0
- package/dist/docs-types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/AspectRatio/AspectRatio.doc.mjs +1 -1
- package/src/Avatar/Avatar.doc.mjs +2 -2
- package/src/Button/Button.tsx +39 -6
- package/src/ButtonGroup/ButtonGroup.doc.mjs +1 -1
- package/src/Calendar/Calendar.test.tsx +106 -0
- package/src/Calendar/Calendar.tsx +59 -22
- package/src/Calendar/dayCellUtils.test.ts +306 -1
- package/src/Calendar/dayCellUtils.ts +206 -13
- package/src/Card/Card.doc.mjs +1 -1
- package/src/Chat/useChatNewMessages.test.tsx +4 -4
- package/src/CheckboxInput/CheckboxInput.doc.mjs +9 -1
- package/src/CheckboxInput/CheckboxInput.test.tsx +36 -0
- package/src/CheckboxInput/CheckboxInput.tsx +11 -0
- package/src/CheckboxList/CheckboxList.doc.mjs +1 -1
- package/src/Code/Code.test.tsx +54 -0
- package/src/Code/Code.tsx +48 -4
- package/src/Code/index.ts +1 -1
- package/src/CodeBlock/index.ts +1 -1
- package/src/CommandPalette/CommandPaletteEmpty.tsx +15 -1
- package/src/ContextMenu/ContextMenu.tsx +17 -1
- package/src/DateInput/DateInput.doc.mjs +6 -1
- package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
- package/src/DateRangeInput/DateRangeInput.tsx +2 -2
- package/src/DateTimeInput/DateTimeInput.doc.mjs +1 -1
- package/src/DateTimeInput/DateTimeInput.tsx +1 -6
- package/src/Divider/Divider.tsx +1 -23
- package/src/DropdownMenu/DropdownMenu.test.tsx +5 -3
- package/src/EmptyState/EmptyState.doc.mjs +1 -1
- package/src/Field/Field.doc.mjs +1 -1
- package/src/FileInput/FileInput.doc.mjs +1 -1
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +0 -3
- package/src/Grid/Grid.doc.mjs +3 -3
- package/src/Layout/__tests__/edgeCompensation.test.tsx +72 -0
- package/src/Markdown/Markdown.test.tsx +23 -0
- package/src/Markdown/incremental.test.ts +36 -0
- package/src/Markdown/parser.test.ts +210 -0
- package/src/Markdown/parser.ts +301 -6
- package/src/MobileNav/MobileNav.doc.mjs +20 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +14 -1
- package/src/MultiSelector/MultiSelector.test.tsx +33 -0
- package/src/MultiSelector/MultiSelector.tsx +20 -0
- package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
- package/src/RadioList/RadioList.doc.mjs +7 -1
- package/src/RadioList/RadioList.test.tsx +46 -0
- package/src/RadioList/RadioList.tsx +10 -1
- package/src/RadioList/RadioListItem.tsx +4 -0
- package/src/Section/Section.test.tsx +4 -10
- package/src/SegmentedControl/SegmentedControl.doc.mjs +1 -1
- package/src/Selector/Selector.doc.mjs +12 -1
- package/src/Selector/Selector.test.tsx +30 -0
- package/src/Selector/Selector.tsx +18 -0
- package/src/Slider/Slider.doc.mjs +14 -2
- package/src/Slider/Slider.test.tsx +30 -0
- package/src/Slider/Slider.tsx +20 -0
- package/src/Spinner/Spinner.tsx +1 -0
- package/src/Switch/Switch.doc.mjs +14 -2
- package/src/Switch/Switch.test.tsx +38 -0
- package/src/Switch/Switch.tsx +12 -0
- package/src/TabList/TabList.doc.mjs +3 -3
- package/src/Table/BaseTable.tsx +9 -1
- package/src/Table/Table.test.tsx +23 -0
- package/src/Table/TableCell.tsx +87 -8
- package/src/Table/index.ts +5 -0
- package/src/Table/plugins/rowExpansion/index.ts +7 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +155 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +590 -0
- package/src/Table/tableContextMenu.test.tsx +27 -3
- package/src/Table/tableContextMenu.tsx +20 -2
- package/src/Table/useTablePagination.doc.mjs +4 -1
- package/src/Table/useTableRowExpansion.doc.mjs +80 -0
- package/src/Table/useTableSortable.doc.mjs +4 -1
- package/src/Text/Text.doc.mjs +2 -2
- package/src/Text/Text.test.tsx +11 -0
- package/src/Text/Text.tsx +4 -2
- package/src/Text/text.stylex.ts +41 -0
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextInput/TextInput.doc.mjs +1 -1
- package/src/TimeInput/TimeInput.doc.mjs +11 -6
- package/src/Tokenizer/Tokenizer.doc.mjs +14 -2
- package/src/Tokenizer/Tokenizer.test.tsx +33 -0
- package/src/Tokenizer/Tokenizer.tsx +19 -0
- package/src/Toolbar/Toolbar.test.tsx +1 -1
- package/src/Typeahead/BaseTypeahead.doc.mjs +10 -0
- package/src/Typeahead/Typeahead.doc.mjs +6 -1
- package/src/docs-types.ts +12 -0
- package/src/hooks/useKeyboardHint.doc.mjs +6 -6
|
@@ -5,10 +5,13 @@ import {
|
|
|
5
5
|
computeDayCellState,
|
|
6
6
|
computeRangeRounding,
|
|
7
7
|
computePreviewRounding,
|
|
8
|
+
computeDayNeighborContinuity,
|
|
8
9
|
isEndpoint,
|
|
10
|
+
isRangeHighlighted,
|
|
11
|
+
isPreviewHighlighted,
|
|
9
12
|
type DayCellStateInput,
|
|
10
13
|
} from './dayCellUtils';
|
|
11
|
-
import {plainDateFromISO} from '../utils/plainDate';
|
|
14
|
+
import {plainDateFromISO, plainDateIsEqual} from '../utils/plainDate';
|
|
12
15
|
|
|
13
16
|
function makeInput(
|
|
14
17
|
overrides: Partial<DayCellStateInput> = {},
|
|
@@ -118,6 +121,49 @@ describe('computeDayCellState', () => {
|
|
|
118
121
|
expect(stateLast.isFirstColumn).toBe(false);
|
|
119
122
|
expect(stateLast.isLastColumn).toBe(true);
|
|
120
123
|
});
|
|
124
|
+
|
|
125
|
+
// Regression (#2715): outside (adjacent-month) days must not receive
|
|
126
|
+
// selection/range/preview state. In the two-month layout the same date
|
|
127
|
+
// renders in both panes, so highlighting the spillover copy would duplicate
|
|
128
|
+
// the selection onto the wrong month's pane.
|
|
129
|
+
it('does not apply range state to outside days', () => {
|
|
130
|
+
const state = computeDayCellState(
|
|
131
|
+
makeInput({
|
|
132
|
+
date: plainDateFromISO('2024-03-17'),
|
|
133
|
+
isOutside: true,
|
|
134
|
+
mode: 'range',
|
|
135
|
+
rangeStart: plainDateFromISO('2024-03-15'),
|
|
136
|
+
rangeEnd: plainDateFromISO('2024-03-20'),
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
expect(state.isInRange).toBe(false);
|
|
140
|
+
expect(state.isRangeStart).toBe(false);
|
|
141
|
+
expect(state.isRangeEnd).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('does not apply preview state to outside days', () => {
|
|
145
|
+
const state = computeDayCellState(
|
|
146
|
+
makeInput({
|
|
147
|
+
date: plainDateFromISO('2024-03-17'),
|
|
148
|
+
isOutside: true,
|
|
149
|
+
previewStart: plainDateFromISO('2024-03-15'),
|
|
150
|
+
previewEnd: plainDateFromISO('2024-03-20'),
|
|
151
|
+
}),
|
|
152
|
+
);
|
|
153
|
+
expect(state.isInPreview).toBe(false);
|
|
154
|
+
expect(state.isPreviewStart).toBe(false);
|
|
155
|
+
expect(state.isPreviewEnd).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('does not mark an outside day as selected', () => {
|
|
159
|
+
const state = computeDayCellState(
|
|
160
|
+
makeInput({
|
|
161
|
+
isOutside: true,
|
|
162
|
+
selectedDate: plainDateFromISO('2024-03-15'),
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
expect(state.isSelected).toBe(false);
|
|
166
|
+
});
|
|
121
167
|
});
|
|
122
168
|
|
|
123
169
|
describe('computeRangeRounding', () => {
|
|
@@ -161,6 +207,96 @@ describe('computeRangeRounding', () => {
|
|
|
161
207
|
const rounding = computeRangeRounding(state);
|
|
162
208
|
expect(rounding.roundRight).toBe(true);
|
|
163
209
|
});
|
|
210
|
+
|
|
211
|
+
// #2715 follow-up: cap the highlight where the range meets a disabled or
|
|
212
|
+
// adjacent-month (outside) day. A mid-range day (neither endpoint nor grid
|
|
213
|
+
// edge) gets an end cap on the side whose neighbour breaks the run.
|
|
214
|
+
it('caps the right edge when the next day breaks the range (e.g. disabled)', () => {
|
|
215
|
+
const state = computeDayCellState(
|
|
216
|
+
makeInput({
|
|
217
|
+
date: plainDateFromISO('2024-03-17'),
|
|
218
|
+
dayIndex: 3,
|
|
219
|
+
mode: 'range',
|
|
220
|
+
rangeStart: plainDateFromISO('2024-03-15'),
|
|
221
|
+
rangeEnd: plainDateFromISO('2024-03-20'),
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
const rounding = computeRangeRounding(state, {
|
|
225
|
+
prevInRange: true,
|
|
226
|
+
nextInRange: false,
|
|
227
|
+
});
|
|
228
|
+
expect(rounding.roundLeft).toBe(false);
|
|
229
|
+
expect(rounding.roundRight).toBe(true);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('caps the left edge when the previous day breaks the range', () => {
|
|
233
|
+
const state = computeDayCellState(
|
|
234
|
+
makeInput({
|
|
235
|
+
date: plainDateFromISO('2024-03-17'),
|
|
236
|
+
dayIndex: 3,
|
|
237
|
+
mode: 'range',
|
|
238
|
+
rangeStart: plainDateFromISO('2024-03-15'),
|
|
239
|
+
rangeEnd: plainDateFromISO('2024-03-20'),
|
|
240
|
+
}),
|
|
241
|
+
);
|
|
242
|
+
const rounding = computeRangeRounding(state, {
|
|
243
|
+
prevInRange: false,
|
|
244
|
+
nextInRange: true,
|
|
245
|
+
});
|
|
246
|
+
expect(rounding.roundLeft).toBe(true);
|
|
247
|
+
expect(rounding.roundRight).toBe(false);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('does not cap a mid-range day when both neighbours continue the range', () => {
|
|
251
|
+
const state = computeDayCellState(
|
|
252
|
+
makeInput({
|
|
253
|
+
date: plainDateFromISO('2024-03-17'),
|
|
254
|
+
dayIndex: 3,
|
|
255
|
+
mode: 'range',
|
|
256
|
+
rangeStart: plainDateFromISO('2024-03-15'),
|
|
257
|
+
rangeEnd: plainDateFromISO('2024-03-20'),
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
260
|
+
const rounding = computeRangeRounding(state, {
|
|
261
|
+
prevInRange: true,
|
|
262
|
+
nextInRange: true,
|
|
263
|
+
});
|
|
264
|
+
expect(rounding.roundLeft).toBe(false);
|
|
265
|
+
expect(rounding.roundRight).toBe(false);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe('isRangeHighlighted', () => {
|
|
270
|
+
const base = {
|
|
271
|
+
date: plainDateFromISO('2024-03-17'),
|
|
272
|
+
mode: 'range' as const,
|
|
273
|
+
rangeStart: plainDateFromISO('2024-03-15'),
|
|
274
|
+
rangeEnd: plainDateFromISO('2024-03-20'),
|
|
275
|
+
isDisabled: false,
|
|
276
|
+
isOutside: false,
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
it('true for an enabled in-month day inside the range', () => {
|
|
280
|
+
expect(isRangeHighlighted(base)).toBe(true);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('false for a disabled day (breaks continuity)', () => {
|
|
284
|
+
expect(isRangeHighlighted({...base, isDisabled: true})).toBe(false);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('false for an adjacent-month (outside) day', () => {
|
|
288
|
+
expect(isRangeHighlighted({...base, isOutside: true})).toBe(false);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('false outside the range bounds', () => {
|
|
292
|
+
expect(
|
|
293
|
+
isRangeHighlighted({...base, date: plainDateFromISO('2024-03-25')}),
|
|
294
|
+
).toBe(false);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('false in single mode', () => {
|
|
298
|
+
expect(isRangeHighlighted({...base, mode: 'single'})).toBe(false);
|
|
299
|
+
});
|
|
164
300
|
});
|
|
165
301
|
|
|
166
302
|
describe('computePreviewRounding', () => {
|
|
@@ -175,6 +311,175 @@ describe('computePreviewRounding', () => {
|
|
|
175
311
|
expect(rounding.roundLeft).toBe(true);
|
|
176
312
|
expect(rounding.roundRight).toBe(false);
|
|
177
313
|
});
|
|
314
|
+
|
|
315
|
+
// #2715: the hover preview needs the same disabled/outside caps as the
|
|
316
|
+
// committed range, so the transient highlight terminates cleanly at the gap.
|
|
317
|
+
it('caps the right edge when the next day breaks the preview run', () => {
|
|
318
|
+
const state = computeDayCellState(
|
|
319
|
+
makeInput({
|
|
320
|
+
date: plainDateFromISO('2024-03-17'),
|
|
321
|
+
dayIndex: 3,
|
|
322
|
+
previewStart: plainDateFromISO('2024-03-15'),
|
|
323
|
+
previewEnd: plainDateFromISO('2024-03-20'),
|
|
324
|
+
}),
|
|
325
|
+
);
|
|
326
|
+
const rounding = computePreviewRounding(state, {
|
|
327
|
+
prevInPreview: true,
|
|
328
|
+
nextInPreview: false,
|
|
329
|
+
});
|
|
330
|
+
expect(rounding.roundLeft).toBe(false);
|
|
331
|
+
expect(rounding.roundRight).toBe(true);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('caps the left edge when the previous day breaks the preview run', () => {
|
|
335
|
+
const state = computeDayCellState(
|
|
336
|
+
makeInput({
|
|
337
|
+
date: plainDateFromISO('2024-03-17'),
|
|
338
|
+
dayIndex: 3,
|
|
339
|
+
previewStart: plainDateFromISO('2024-03-15'),
|
|
340
|
+
previewEnd: plainDateFromISO('2024-03-20'),
|
|
341
|
+
}),
|
|
342
|
+
);
|
|
343
|
+
const rounding = computePreviewRounding(state, {
|
|
344
|
+
prevInPreview: false,
|
|
345
|
+
nextInPreview: true,
|
|
346
|
+
});
|
|
347
|
+
expect(rounding.roundLeft).toBe(true);
|
|
348
|
+
expect(rounding.roundRight).toBe(false);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('does not cap a mid-preview day when both neighbours continue', () => {
|
|
352
|
+
const state = computeDayCellState(
|
|
353
|
+
makeInput({
|
|
354
|
+
date: plainDateFromISO('2024-03-17'),
|
|
355
|
+
dayIndex: 3,
|
|
356
|
+
previewStart: plainDateFromISO('2024-03-15'),
|
|
357
|
+
previewEnd: plainDateFromISO('2024-03-20'),
|
|
358
|
+
}),
|
|
359
|
+
);
|
|
360
|
+
const rounding = computePreviewRounding(state, {
|
|
361
|
+
prevInPreview: true,
|
|
362
|
+
nextInPreview: true,
|
|
363
|
+
});
|
|
364
|
+
expect(rounding.roundLeft).toBe(false);
|
|
365
|
+
expect(rounding.roundRight).toBe(false);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
describe('isPreviewHighlighted', () => {
|
|
370
|
+
const base = {
|
|
371
|
+
date: plainDateFromISO('2024-03-17'),
|
|
372
|
+
previewStart: plainDateFromISO('2024-03-15'),
|
|
373
|
+
previewEnd: plainDateFromISO('2024-03-20'),
|
|
374
|
+
isDisabled: false,
|
|
375
|
+
isOutside: false,
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
it('true for an enabled in-month day inside the preview span', () => {
|
|
379
|
+
expect(isPreviewHighlighted(base)).toBe(true);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('false for a disabled day', () => {
|
|
383
|
+
expect(isPreviewHighlighted({...base, isDisabled: true})).toBe(false);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('false for an adjacent-month (outside) day', () => {
|
|
387
|
+
expect(isPreviewHighlighted({...base, isOutside: true})).toBe(false);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('false outside the preview bounds', () => {
|
|
391
|
+
expect(
|
|
392
|
+
isPreviewHighlighted({...base, date: plainDateFromISO('2024-03-25')}),
|
|
393
|
+
).toBe(false);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('false with no preview span', () => {
|
|
397
|
+
expect(
|
|
398
|
+
isPreviewHighlighted({...base, previewStart: null, previewEnd: null}),
|
|
399
|
+
).toBe(false);
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
describe('computeDayNeighborContinuity', () => {
|
|
404
|
+
// A week row of 5 consecutive in-month days, Mar 15–19.
|
|
405
|
+
const week = (
|
|
406
|
+
[
|
|
407
|
+
'2024-03-15',
|
|
408
|
+
'2024-03-16',
|
|
409
|
+
'2024-03-17',
|
|
410
|
+
'2024-03-18',
|
|
411
|
+
'2024-03-19',
|
|
412
|
+
] as const
|
|
413
|
+
).map(iso => ({
|
|
414
|
+
date: plainDateFromISO(iso),
|
|
415
|
+
isOutside: false,
|
|
416
|
+
}));
|
|
417
|
+
|
|
418
|
+
const baseInput = {
|
|
419
|
+
week,
|
|
420
|
+
mode: 'range' as const,
|
|
421
|
+
rangeStart: plainDateFromISO('2024-03-15'),
|
|
422
|
+
rangeEnd: plainDateFromISO('2024-03-19'),
|
|
423
|
+
previewStart: null,
|
|
424
|
+
previewEnd: null,
|
|
425
|
+
isDisabled: () => false,
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
it('reports both range neighbours continuing for a mid-range day', () => {
|
|
429
|
+
const c = computeDayNeighborContinuity({...baseInput, dayIndex: 2});
|
|
430
|
+
expect(c.prevInRange).toBe(true);
|
|
431
|
+
expect(c.nextInRange).toBe(true);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('breaks continuity where a neighbour is disabled', () => {
|
|
435
|
+
// Disable Mar 18 (index 3): its neighbour Mar 17 (index 2) sees a break to
|
|
436
|
+
// the right.
|
|
437
|
+
const disabledDay = plainDateFromISO('2024-03-18');
|
|
438
|
+
const c = computeDayNeighborContinuity({
|
|
439
|
+
...baseInput,
|
|
440
|
+
dayIndex: 2,
|
|
441
|
+
isDisabled: date => plainDateIsEqual(date, disabledDay),
|
|
442
|
+
});
|
|
443
|
+
expect(c.prevInRange).toBe(true);
|
|
444
|
+
expect(c.nextInRange).toBe(false);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('breaks continuity where a neighbour is an outside day', () => {
|
|
448
|
+
const weekWithOutside = week.map((d, i) =>
|
|
449
|
+
i === 3 ? {...d, isOutside: true} : d,
|
|
450
|
+
);
|
|
451
|
+
const c = computeDayNeighborContinuity({
|
|
452
|
+
...baseInput,
|
|
453
|
+
week: weekWithOutside,
|
|
454
|
+
dayIndex: 2,
|
|
455
|
+
});
|
|
456
|
+
expect(c.nextInRange).toBe(false);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('treats a missing neighbour (row edge) as a break', () => {
|
|
460
|
+
const first = computeDayNeighborContinuity({...baseInput, dayIndex: 0});
|
|
461
|
+
expect(first.prevInRange).toBe(false);
|
|
462
|
+
const last = computeDayNeighborContinuity({
|
|
463
|
+
...baseInput,
|
|
464
|
+
dayIndex: week.length - 1,
|
|
465
|
+
});
|
|
466
|
+
expect(last.nextInRange).toBe(false);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('derives preview continuity independently of range', () => {
|
|
470
|
+
const c = computeDayNeighborContinuity({
|
|
471
|
+
...baseInput,
|
|
472
|
+
rangeStart: null,
|
|
473
|
+
rangeEnd: null,
|
|
474
|
+
previewStart: plainDateFromISO('2024-03-15'),
|
|
475
|
+
previewEnd: plainDateFromISO('2024-03-19'),
|
|
476
|
+
dayIndex: 2,
|
|
477
|
+
});
|
|
478
|
+
expect(c.prevInRange).toBe(false);
|
|
479
|
+
expect(c.nextInRange).toBe(false);
|
|
480
|
+
expect(c.prevInPreview).toBe(true);
|
|
481
|
+
expect(c.nextInPreview).toBe(true);
|
|
482
|
+
});
|
|
178
483
|
});
|
|
179
484
|
|
|
180
485
|
describe('isEndpoint', () => {
|
|
@@ -31,7 +31,14 @@ export interface DayCellStateInput {
|
|
|
31
31
|
isOutside: boolean;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Derives all visual/interaction states for a single day cell.
|
|
36
|
+
*
|
|
37
|
+
* Outside (adjacent-month) days never receive selection, range, or preview
|
|
38
|
+
* state: in the two-month layout the same date renders in both panes, and
|
|
39
|
+
* highlighting the spillover copy would duplicate the selection onto the wrong
|
|
40
|
+
* month's pane (#2715).
|
|
41
|
+
*/
|
|
35
42
|
export function computeDayCellState(input: DayCellStateInput): DayCellState {
|
|
36
43
|
const {
|
|
37
44
|
date,
|
|
@@ -51,55 +58,241 @@ export function computeDayCellState(input: DayCellStateInput): DayCellState {
|
|
|
51
58
|
effectivelyDisabled: isDisabled || isOutside,
|
|
52
59
|
isToday: plainDateIsEqual(date, today),
|
|
53
60
|
isSelected: !!(
|
|
61
|
+
!isOutside &&
|
|
54
62
|
mode === 'single' &&
|
|
55
63
|
selectedDate &&
|
|
56
64
|
plainDateIsEqual(date, selectedDate)
|
|
57
65
|
),
|
|
58
66
|
isInRange: !!(
|
|
67
|
+
!isOutside &&
|
|
59
68
|
mode === 'range' &&
|
|
60
69
|
rangeStart &&
|
|
61
70
|
rangeEnd &&
|
|
62
71
|
plainDateIsInRange(date, [rangeStart, rangeEnd])
|
|
63
72
|
),
|
|
64
73
|
isRangeStart: !!(
|
|
74
|
+
!isOutside &&
|
|
65
75
|
mode === 'range' &&
|
|
66
76
|
rangeStart &&
|
|
67
77
|
plainDateIsEqual(date, rangeStart)
|
|
68
78
|
),
|
|
69
79
|
isRangeEnd: !!(
|
|
80
|
+
!isOutside &&
|
|
70
81
|
mode === 'range' &&
|
|
71
82
|
rangeEnd &&
|
|
72
83
|
plainDateIsEqual(date, rangeEnd)
|
|
73
84
|
),
|
|
74
85
|
isInPreview: !!(
|
|
86
|
+
!isOutside &&
|
|
75
87
|
previewStart &&
|
|
76
88
|
previewEnd &&
|
|
77
89
|
plainDateIsInRange(date, [previewStart, previewEnd])
|
|
78
90
|
),
|
|
79
|
-
isPreviewStart: !!(
|
|
80
|
-
|
|
91
|
+
isPreviewStart: !!(
|
|
92
|
+
!isOutside &&
|
|
93
|
+
previewStart &&
|
|
94
|
+
plainDateIsEqual(date, previewStart)
|
|
95
|
+
),
|
|
96
|
+
isPreviewEnd: !!(
|
|
97
|
+
!isOutside &&
|
|
98
|
+
previewEnd &&
|
|
99
|
+
plainDateIsEqual(date, previewEnd)
|
|
100
|
+
),
|
|
81
101
|
isFirstColumn: dayIndex === 0,
|
|
82
102
|
isLastColumn: dayIndex === 6,
|
|
83
103
|
};
|
|
84
104
|
}
|
|
85
105
|
|
|
86
|
-
/**
|
|
87
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Generic rounding for a highlight run (range or preview).
|
|
108
|
+
*
|
|
109
|
+
* Rounds at the run's endpoints and the grid row edges, and also caps the
|
|
110
|
+
* highlight where the run meets a break in continuity — a neighbouring cell
|
|
111
|
+
* that is disabled or an adjacent-month (outside) day (#2715).
|
|
112
|
+
*/
|
|
113
|
+
function computeHighlightRounding(input: {
|
|
114
|
+
isStart: boolean;
|
|
115
|
+
isEnd: boolean;
|
|
116
|
+
isFirstColumn: boolean;
|
|
117
|
+
isLastColumn: boolean;
|
|
118
|
+
prevContinues?: boolean;
|
|
119
|
+
nextContinues?: boolean;
|
|
120
|
+
}) {
|
|
121
|
+
const prevBreaks =
|
|
122
|
+
input.prevContinues !== undefined ? !input.prevContinues : false;
|
|
123
|
+
const nextBreaks =
|
|
124
|
+
input.nextContinues !== undefined ? !input.nextContinues : false;
|
|
88
125
|
return {
|
|
89
|
-
roundLeft:
|
|
90
|
-
roundRight:
|
|
126
|
+
roundLeft: input.isStart || input.isFirstColumn || prevBreaks,
|
|
127
|
+
roundRight: input.isEnd || input.isLastColumn || nextBreaks,
|
|
91
128
|
};
|
|
92
129
|
}
|
|
93
130
|
|
|
94
|
-
/**
|
|
95
|
-
export function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
131
|
+
/** Rounding for the committed range background. */
|
|
132
|
+
export function computeRangeRounding(
|
|
133
|
+
state: DayCellState,
|
|
134
|
+
neighbors?: {prevInRange?: boolean; nextInRange?: boolean},
|
|
135
|
+
) {
|
|
136
|
+
return computeHighlightRounding({
|
|
137
|
+
isStart: state.isRangeStart,
|
|
138
|
+
isEnd: state.isRangeEnd,
|
|
139
|
+
isFirstColumn: state.isFirstColumn,
|
|
140
|
+
isLastColumn: state.isLastColumn,
|
|
141
|
+
prevContinues: neighbors?.prevInRange,
|
|
142
|
+
nextContinues: neighbors?.nextInRange,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Rounding for the hover-preview background. */
|
|
147
|
+
export function computePreviewRounding(
|
|
148
|
+
state: DayCellState,
|
|
149
|
+
neighbors?: {prevInPreview?: boolean; nextInPreview?: boolean},
|
|
150
|
+
) {
|
|
151
|
+
return computeHighlightRounding({
|
|
152
|
+
isStart: state.isPreviewStart,
|
|
153
|
+
isEnd: state.isPreviewEnd,
|
|
154
|
+
isFirstColumn: state.isFirstColumn,
|
|
155
|
+
isLastColumn: state.isLastColumn,
|
|
156
|
+
prevContinues: neighbors?.prevInPreview,
|
|
157
|
+
nextContinues: neighbors?.nextInPreview,
|
|
158
|
+
});
|
|
100
159
|
}
|
|
101
160
|
|
|
102
161
|
/** Whether a day is a selection endpoint (selected, range start, or range end). */
|
|
103
162
|
export function isEndpoint(state: DayCellState): boolean {
|
|
104
163
|
return state.isSelected || state.isRangeStart || state.isRangeEnd;
|
|
105
164
|
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Whether a day is part of an unbroken highlight run (enabled, in-month, inside
|
|
168
|
+
* the given span). Outside and disabled days break the run, producing end caps.
|
|
169
|
+
*/
|
|
170
|
+
function isSpanHighlighted(input: {
|
|
171
|
+
date: PlainDate;
|
|
172
|
+
spanStart: PlainDate | null;
|
|
173
|
+
spanEnd: PlainDate | null;
|
|
174
|
+
isDisabled: boolean;
|
|
175
|
+
isOutside: boolean;
|
|
176
|
+
}): boolean {
|
|
177
|
+
const {date, spanStart, spanEnd, isDisabled, isOutside} = input;
|
|
178
|
+
return !!(
|
|
179
|
+
!isOutside &&
|
|
180
|
+
!isDisabled &&
|
|
181
|
+
spanStart &&
|
|
182
|
+
spanEnd &&
|
|
183
|
+
plainDateIsInRange(date, [spanStart, spanEnd])
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Whether a day participates in the continuous committed-range highlight. */
|
|
188
|
+
export function isRangeHighlighted(input: {
|
|
189
|
+
date: PlainDate;
|
|
190
|
+
mode: 'single' | 'range';
|
|
191
|
+
rangeStart: PlainDate | null;
|
|
192
|
+
rangeEnd: PlainDate | null;
|
|
193
|
+
isDisabled: boolean;
|
|
194
|
+
isOutside: boolean;
|
|
195
|
+
}): boolean {
|
|
196
|
+
if (input.mode !== 'range') {return false;}
|
|
197
|
+
return isSpanHighlighted({
|
|
198
|
+
date: input.date,
|
|
199
|
+
spanStart: input.rangeStart,
|
|
200
|
+
spanEnd: input.rangeEnd,
|
|
201
|
+
isDisabled: input.isDisabled,
|
|
202
|
+
isOutside: input.isOutside,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Whether a day participates in the continuous hover-preview highlight. */
|
|
207
|
+
export function isPreviewHighlighted(input: {
|
|
208
|
+
date: PlainDate;
|
|
209
|
+
previewStart: PlainDate | null;
|
|
210
|
+
previewEnd: PlainDate | null;
|
|
211
|
+
isDisabled: boolean;
|
|
212
|
+
isOutside: boolean;
|
|
213
|
+
}): boolean {
|
|
214
|
+
return isSpanHighlighted({
|
|
215
|
+
date: input.date,
|
|
216
|
+
spanStart: input.previewStart,
|
|
217
|
+
spanEnd: input.previewEnd,
|
|
218
|
+
isDisabled: input.isDisabled,
|
|
219
|
+
isOutside: input.isOutside,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Continuity of the highlighted run through a day's immediate neighbours in the
|
|
225
|
+
* same week row, for both the committed range and the hover preview. A day gets
|
|
226
|
+
* an end cap on whichever side its neighbour does not continue the run (see
|
|
227
|
+
* {@link computeRangeRounding} / {@link computePreviewRounding}).
|
|
228
|
+
*/
|
|
229
|
+
export interface DayNeighborContinuity {
|
|
230
|
+
prevInRange: boolean;
|
|
231
|
+
nextInRange: boolean;
|
|
232
|
+
prevInPreview: boolean;
|
|
233
|
+
nextInPreview: boolean;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Minimal shape needed from a week's day cells to derive neighbour continuity. */
|
|
237
|
+
export interface NeighborDay {
|
|
238
|
+
date: PlainDate;
|
|
239
|
+
isOutside: boolean;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Derives whether the previous/next day in the same week row continues the
|
|
244
|
+
* highlighted run, for both range and preview. Broken out from the render path
|
|
245
|
+
* so the neighbour logic is unit-testable in isolation.
|
|
246
|
+
*/
|
|
247
|
+
export function computeDayNeighborContinuity(input: {
|
|
248
|
+
week: ReadonlyArray<NeighborDay>;
|
|
249
|
+
dayIndex: number;
|
|
250
|
+
mode: 'single' | 'range';
|
|
251
|
+
rangeStart: PlainDate | null;
|
|
252
|
+
rangeEnd: PlainDate | null;
|
|
253
|
+
previewStart: PlainDate | null;
|
|
254
|
+
previewEnd: PlainDate | null;
|
|
255
|
+
isDisabled: (date: PlainDate) => boolean;
|
|
256
|
+
}): DayNeighborContinuity {
|
|
257
|
+
const {
|
|
258
|
+
week,
|
|
259
|
+
dayIndex,
|
|
260
|
+
mode,
|
|
261
|
+
rangeStart,
|
|
262
|
+
rangeEnd,
|
|
263
|
+
previewStart,
|
|
264
|
+
previewEnd,
|
|
265
|
+
isDisabled,
|
|
266
|
+
} = input;
|
|
267
|
+
|
|
268
|
+
const prev = week[dayIndex - 1];
|
|
269
|
+
const next = week[dayIndex + 1];
|
|
270
|
+
|
|
271
|
+
const rangeContinues = (day: NeighborDay | undefined): boolean =>
|
|
272
|
+
day != null &&
|
|
273
|
+
isRangeHighlighted({
|
|
274
|
+
date: day.date,
|
|
275
|
+
mode,
|
|
276
|
+
rangeStart,
|
|
277
|
+
rangeEnd,
|
|
278
|
+
isDisabled: isDisabled(day.date),
|
|
279
|
+
isOutside: day.isOutside,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const previewContinues = (day: NeighborDay | undefined): boolean =>
|
|
283
|
+
day != null &&
|
|
284
|
+
isPreviewHighlighted({
|
|
285
|
+
date: day.date,
|
|
286
|
+
previewStart,
|
|
287
|
+
previewEnd,
|
|
288
|
+
isDisabled: isDisabled(day.date),
|
|
289
|
+
isOutside: day.isOutside,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
prevInRange: rangeContinues(prev),
|
|
294
|
+
nextInRange: rangeContinues(next),
|
|
295
|
+
prevInPreview: previewContinues(prev),
|
|
296
|
+
nextInPreview: previewContinues(next),
|
|
297
|
+
};
|
|
298
|
+
}
|
package/src/Card/Card.doc.mjs
CHANGED
|
@@ -84,7 +84,7 @@ export const docs = {
|
|
|
84
84
|
theming: {
|
|
85
85
|
container: true,
|
|
86
86
|
targets: [
|
|
87
|
-
{className: 'astryx-card'},
|
|
87
|
+
{className: 'astryx-card', visualProps: ['variant']},
|
|
88
88
|
],
|
|
89
89
|
vars: [
|
|
90
90
|
{name: '--_card-radius', description: 'Border radius of the card', default: 'var(--radius-container)', private: true},
|
|
@@ -64,7 +64,7 @@ describe('useChatNewMessages — callback ref (issue #2282)', () => {
|
|
|
64
64
|
const {contentRef} = useChatNewMessages({isLocked: true, onResize});
|
|
65
65
|
return (
|
|
66
66
|
<div ref={contentRef} data-testid="content">
|
|
67
|
-
<div className="
|
|
67
|
+
<div className="astryx-chat-message">msg</div>
|
|
68
68
|
</div>
|
|
69
69
|
);
|
|
70
70
|
}
|
|
@@ -91,7 +91,7 @@ describe('useChatNewMessages — callback ref (issue #2282)', () => {
|
|
|
91
91
|
</button>
|
|
92
92
|
{mounted && (
|
|
93
93
|
<div ref={contentRef} data-testid="content">
|
|
94
|
-
<div className="
|
|
94
|
+
<div className="astryx-chat-message">msg</div>
|
|
95
95
|
</div>
|
|
96
96
|
)}
|
|
97
97
|
</>
|
|
@@ -129,7 +129,7 @@ describe('useChatNewMessages — callback ref (issue #2282)', () => {
|
|
|
129
129
|
</button>
|
|
130
130
|
{mounted && (
|
|
131
131
|
<div ref={contentRef} data-testid="content">
|
|
132
|
-
<div className="
|
|
132
|
+
<div className="astryx-chat-message">msg</div>
|
|
133
133
|
</div>
|
|
134
134
|
)}
|
|
135
135
|
</>
|
|
@@ -198,7 +198,7 @@ describe('useChatNewMessages — callback ref (issue #2282)', () => {
|
|
|
198
198
|
if (!el.querySelector?.('.astryx-chat-message')) {
|
|
199
199
|
return false;
|
|
200
200
|
}
|
|
201
|
-
if (el.className?.includes('
|
|
201
|
+
if (el.className?.includes('astryx-chat-layout')) {
|
|
202
202
|
return false;
|
|
203
203
|
}
|
|
204
204
|
return true;
|
|
@@ -63,11 +63,17 @@ export const docs = {
|
|
|
63
63
|
description: 'Whether the checkbox is disabled.',
|
|
64
64
|
default: 'false',
|
|
65
65
|
},
|
|
66
|
+
{
|
|
67
|
+
name: 'htmlName',
|
|
68
|
+
type: 'string',
|
|
69
|
+
description:
|
|
70
|
+
'The HTML name attribute for the underlying checkbox input, useful for form submissions (submits "on" when checked).',
|
|
71
|
+
},
|
|
66
72
|
{
|
|
67
73
|
name: 'disabledMessage',
|
|
68
74
|
type: 'string',
|
|
69
75
|
description:
|
|
70
|
-
'Explains why the checkbox is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the checkbox focusable via aria-disabled (toggling stays blocked). Use this instead of wrapping a disabled CheckboxInput in Tooltip
|
|
76
|
+
'Explains why the checkbox is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the checkbox focusable via aria-disabled (toggling stays blocked). Use this instead of wrapping a disabled CheckboxInput in Tooltip. Disabled controls swallow the hover events an external Tooltip needs.',
|
|
71
77
|
},
|
|
72
78
|
{
|
|
73
79
|
name: 'isReadOnly',
|
|
@@ -171,6 +177,7 @@ export const docsZh = {
|
|
|
171
177
|
},
|
|
172
178
|
{name: 'isLoading', type: 'boolean', description: '复选框是否处于加载状态。显示旋转器并阻止交互。', default: 'false'},
|
|
173
179
|
{name: 'isDisabled', type: 'boolean', description: '复选框是否禁用。', default: 'false'},
|
|
180
|
+
{name: 'htmlName', type: 'string', description: '底层复选框输入的 HTML name 属性,用于表单提交(勾选时提交 "on")。'},
|
|
174
181
|
{name: 'disabledMessage', type: 'string', description: 'Explains why the checkbox is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the checkbox focusable via aria-disabled (toggling stays blocked). Use this instead of wrapping a disabled CheckboxInput in Tooltip — disabled controls swallow the hover events an external Tooltip needs.'},
|
|
175
182
|
{name: 'isReadOnly', type: 'boolean', description: '复选框是否为只读。以完整不透明度显示当前状态但阻止交互。与 isDisabled 不同,只读复选框不会变暗。', default: 'false'},
|
|
176
183
|
{name: 'isOptional', type: 'boolean', description: '字段是否可选。与 isRequired 互斥。', default: 'false'},
|
|
@@ -222,6 +229,7 @@ export const docsDense = {
|
|
|
222
229
|
changeAction: 'async action; fires after onChange, shows spinner while pending',
|
|
223
230
|
isLoading: 'shows spinner + prevents interaction',
|
|
224
231
|
isDisabled: 'disable checkbox',
|
|
232
|
+
htmlName: 'HTML name attr for the checkbox; submits "on" when checked.',
|
|
225
233
|
isOptional: 'mark field as optional (mutually exclusive w/ isRequired)',
|
|
226
234
|
isRequired: 'mark field as required (mutually exclusive w/ isOptional)',
|
|
227
235
|
size: 'sm (compact) or md (default)',
|