@torpor/ui 1.0.1 → 1.1.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/dist/ColorPicker/ColorPicker.torp +36 -75
- package/dist/ColorPicker/ColorPickerInput.torp +122 -0
- package/dist/ColorPicker/ColorPickerTypes.ts +12 -0
- package/dist/ColorPicker/index.d.ts +40 -1
- package/dist/ColorPicker/index.js +2 -1
- package/dist/CommandPalette/CommandPalette.torp +49 -47
- package/dist/CommandPalette/CommandPaletteInput.torp +51 -0
- package/dist/CommandPalette/CommandPaletteItem.torp +62 -0
- package/dist/CommandPalette/CommandPaletteList.torp +55 -0
- package/dist/CommandPalette/CommandPaletteTypes.ts +32 -0
- package/dist/CommandPalette/index.d.ts +88 -2
- package/dist/CommandPalette/index.js +4 -1
- package/dist/DataGrid/DataGrid.torp +22 -22
- package/dist/DataGrid/DataGridCell.torp +50 -0
- package/dist/DataGrid/DataGridColumnHeader.torp +54 -0
- package/dist/DataGrid/DataGridTypes.ts +17 -0
- package/dist/DataGrid/index.d.ts +57 -2
- package/dist/DataGrid/index.js +3 -2
- package/dist/DatePicker/DatePicker.torp +45 -65
- package/dist/DatePicker/DatePickerContent.torp +82 -0
- package/dist/DatePicker/DatePickerTrigger.torp +56 -0
- package/dist/DatePicker/DatePickerTypes.ts +36 -0
- package/dist/DatePicker/index.d.ts +61 -2
- package/dist/DatePicker/index.js +3 -1
- package/dist/DateRangePicker/DateRangePicker.torp +52 -76
- package/dist/DateRangePicker/DateRangePickerContent.torp +90 -0
- package/dist/DateRangePicker/DateRangePickerTrigger.torp +59 -0
- package/dist/DateRangePicker/DateRangePickerTypes.ts +41 -0
- package/dist/DateRangePicker/index.d.ts +61 -2
- package/dist/DateRangePicker/index.js +3 -1
- package/dist/Progress/Progress.torp +24 -14
- package/dist/Progress/ProgressIndicator.torp +52 -0
- package/dist/Progress/ProgressTypes.ts +8 -0
- package/dist/Progress/index.d.ts +34 -1
- package/dist/Progress/index.js +2 -1
- package/dist/Rating/Rating.torp +41 -37
- package/dist/Rating/RatingStar.torp +71 -0
- package/dist/Rating/RatingTypes.ts +22 -0
- package/dist/Rating/index.d.ts +39 -1
- package/dist/Rating/index.js +2 -1
- package/dist/Slider/Slider.torp +48 -63
- package/dist/Slider/SliderHandle.torp +107 -0
- package/dist/Slider/SliderRange.torp +36 -0
- package/dist/Slider/SliderTypes.ts +34 -0
- package/dist/Slider/index.d.ts +65 -1
- package/dist/Slider/index.js +3 -1
- package/dist/TagInput/TagInput.torp +63 -84
- package/dist/TagInput/TagInputField.torp +68 -0
- package/dist/TagInput/TagInputSuggestions.torp +79 -0
- package/dist/TagInput/TagInputTag.torp +55 -0
- package/dist/TagInput/TagInputTypes.ts +42 -0
- package/dist/TagInput/index.d.ts +93 -1
- package/dist/TagInput/index.js +4 -1
- package/dist/TimePicker/TimePicker.torp +88 -85
- package/dist/TimePicker/TimePickerPart.torp +61 -0
- package/dist/TimePicker/TimePickerPeriod.torp +48 -0
- package/dist/TimePicker/TimePickerTypes.ts +37 -0
- package/dist/TimePicker/index.d.ts +61 -1
- package/dist/TimePicker/index.js +3 -1
- package/package.json +7 -7
|
@@ -32,7 +32,16 @@ interface CommandPaletteProps {
|
|
|
32
32
|
* shortcut or programmatically.
|
|
33
33
|
*
|
|
34
34
|
* The structure of a CommandPalette is a Modal containing a search input and
|
|
35
|
-
* a filtered list of commands
|
|
35
|
+
* a filtered list of commands:
|
|
36
|
+
*
|
|
37
|
+
* ```
|
|
38
|
+
* <CommandPalette>
|
|
39
|
+
* <CommandPaletteInput />
|
|
40
|
+
* <CommandPaletteList />
|
|
41
|
+
* </CommandPalette>
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* With no children, the input and list are rendered automatically.
|
|
36
45
|
*/
|
|
37
46
|
declare function CommandPalette(
|
|
38
47
|
$parent: ParentNode,
|
|
@@ -42,4 +51,81 @@ declare function CommandPalette(
|
|
|
42
51
|
$slots?: Record<string, SlotRender>,
|
|
43
52
|
): void;
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
interface CommandPaletteInputProps {
|
|
57
|
+
/** An ID for the input element */
|
|
58
|
+
id?: string;
|
|
59
|
+
/** Classes for the input element */
|
|
60
|
+
class?: ClassValue;
|
|
61
|
+
/** Styles for the input element */
|
|
62
|
+
style?: StyleValue;
|
|
63
|
+
/** Text to display in the input when it is empty; falls back to the palette's placeholder */
|
|
64
|
+
placeholder?: string;
|
|
65
|
+
/** An accessible name for the input; falls back to the palette's ariaLabel */
|
|
66
|
+
ariaLabel?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The search input of a CommandPalette, filtering the commands as the user
|
|
71
|
+
* types.
|
|
72
|
+
*/
|
|
73
|
+
declare function CommandPaletteInput(
|
|
74
|
+
$parent: ParentNode,
|
|
75
|
+
$anchor: Node | null,
|
|
76
|
+
$props: CommandPaletteInputProps | undefined,
|
|
77
|
+
$context?: Record<PropertyKey, any>,
|
|
78
|
+
$slots?: Record<string, SlotRender>,
|
|
79
|
+
): void;
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
interface CommandPaletteItemProps {
|
|
84
|
+
/** An ID for the item element */
|
|
85
|
+
id?: string;
|
|
86
|
+
/** Classes for the item element */
|
|
87
|
+
class?: ClassValue;
|
|
88
|
+
/** Styles for the item element */
|
|
89
|
+
style?: StyleValue;
|
|
90
|
+
/** The item's position in the list */
|
|
91
|
+
index: number;
|
|
92
|
+
/** The command that choosing this item runs */
|
|
93
|
+
command: Command;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* One command of a CommandPalette, showing its label and shortcut hint.
|
|
98
|
+
* Choosing it runs the command and closes the palette.
|
|
99
|
+
*/
|
|
100
|
+
declare function CommandPaletteItem(
|
|
101
|
+
$parent: ParentNode,
|
|
102
|
+
$anchor: Node | null,
|
|
103
|
+
$props: CommandPaletteItemProps | undefined,
|
|
104
|
+
$context?: Record<PropertyKey, any>,
|
|
105
|
+
$slots?: Record<string, SlotRender>,
|
|
106
|
+
): void;
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
interface CommandPaletteListProps {
|
|
111
|
+
/** An ID for the list element */
|
|
112
|
+
id?: string;
|
|
113
|
+
/** Classes for the list element */
|
|
114
|
+
class?: ClassValue;
|
|
115
|
+
/** Styles for the list element */
|
|
116
|
+
style?: StyleValue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The list of a CommandPalette, showing the commands that match the current
|
|
121
|
+
* search text (or a message when none match).
|
|
122
|
+
*/
|
|
123
|
+
declare function CommandPaletteList(
|
|
124
|
+
$parent: ParentNode,
|
|
125
|
+
$anchor: Node | null,
|
|
126
|
+
$props: CommandPaletteListProps | undefined,
|
|
127
|
+
$context?: Record<PropertyKey, any>,
|
|
128
|
+
$slots?: Record<string, SlotRender>,
|
|
129
|
+
): void;
|
|
130
|
+
|
|
131
|
+
export { CommandPalette, CommandPaletteInput, CommandPaletteItem, CommandPaletteList };
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import CommandPalette from "./CommandPalette.torp";
|
|
2
|
-
|
|
2
|
+
import CommandPaletteInput from "./CommandPaletteInput.torp";
|
|
3
|
+
import CommandPaletteItem from "./CommandPaletteItem.torp";
|
|
4
|
+
import CommandPaletteList from "./CommandPaletteList.torp";
|
|
5
|
+
export { CommandPalette, CommandPaletteInput, CommandPaletteItem, CommandPaletteList };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Bindable, ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
-
import { type DataColumn } from "./DataGridTypes";
|
|
2
|
+
import { type DataColumn, type DataGridContext, DataGridContextName } from "./DataGridTypes";
|
|
3
|
+
import DataGridCell from "./DataGridCell.torp";
|
|
4
|
+
import DataGridColumnHeader from "./DataGridColumnHeader.torp";
|
|
3
5
|
import type {
|
|
4
6
|
LoadRequest,
|
|
5
7
|
LoadResult,
|
|
@@ -63,7 +65,8 @@ interface DataGridProps {
|
|
|
63
65
|
*
|
|
64
66
|
* See [the WAI-ARIA guidelines for grids](https://www.w3.org/WAI/ARIA/apg/patterns/grid/).
|
|
65
67
|
*
|
|
66
|
-
* The structure of a DataGrid is
|
|
68
|
+
* The structure of a DataGrid is a table of DataGridColumnHeaders and
|
|
69
|
+
* DataGridCells, rendered automatically from the columns and rows:
|
|
67
70
|
*
|
|
68
71
|
* ```
|
|
69
72
|
* <DataGrid columns={columns} data={rows}>
|
|
@@ -386,6 +389,17 @@ export default function DataGrid($props: DataGridProps) {
|
|
|
386
389
|
return `max-height:${getViewportHeight()}px;overflow:auto;`;
|
|
387
390
|
}
|
|
388
391
|
|
|
392
|
+
// Set the context to pass down to the headers and cells
|
|
393
|
+
let context: DataGridContext = {
|
|
394
|
+
toggleSort,
|
|
395
|
+
getAriaSort: ariaSort,
|
|
396
|
+
getAlignClass: alignClass,
|
|
397
|
+
getCellValue: cellValue,
|
|
398
|
+
isActiveCell,
|
|
399
|
+
getCellId: cellId,
|
|
400
|
+
};
|
|
401
|
+
$context[DataGridContextName] = context;
|
|
402
|
+
|
|
389
403
|
@render {
|
|
390
404
|
@try {
|
|
391
405
|
@await {
|
|
@@ -418,20 +432,7 @@ export default function DataGrid($props: DataGridProps) {
|
|
|
418
432
|
<tr role="row">
|
|
419
433
|
@for (let entry of columnEntries) {
|
|
420
434
|
@key = entry.column.key
|
|
421
|
-
<
|
|
422
|
-
scope="col"
|
|
423
|
-
role="columnheader"
|
|
424
|
-
class={[alignClass(entry.column.align), entry.column.sortable ? "torp-data-grid-sortable" : undefined]}
|
|
425
|
-
aria-sort={ariaSort(entry.column.key)}
|
|
426
|
-
>
|
|
427
|
-
@if (entry.column.sortable) {
|
|
428
|
-
<button type="button" onclick={() => toggleSort(entry.column)}>
|
|
429
|
-
{entry.column.label ?? entry.column.key}
|
|
430
|
-
</button>
|
|
431
|
-
} else {
|
|
432
|
-
<span>{entry.column.label ?? entry.column.key}</span>
|
|
433
|
-
}
|
|
434
|
-
</th>
|
|
435
|
+
<DataGridColumnHeader column={entry.column} />
|
|
435
436
|
}
|
|
436
437
|
</tr>
|
|
437
438
|
</thead>
|
|
@@ -453,11 +454,10 @@ export default function DataGrid($props: DataGridProps) {
|
|
|
453
454
|
<tr role="row">
|
|
454
455
|
@for (let entry of columnEntries) {
|
|
455
456
|
@key = entry.column.key
|
|
456
|
-
<
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
tabindex={isActiveCell(rowEntry.index, entry.index) ? 0 : -1}
|
|
457
|
+
<DataGridCell
|
|
458
|
+
column={entry.column}
|
|
459
|
+
rowIndex={rowEntry.index}
|
|
460
|
+
colIndex={entry.index}
|
|
461
461
|
>
|
|
462
462
|
<slot
|
|
463
463
|
row={rowEntry.row}
|
|
@@ -466,7 +466,7 @@ export default function DataGrid($props: DataGridProps) {
|
|
|
466
466
|
>
|
|
467
467
|
{cellValue(rowEntry.row, entry.column)}
|
|
468
468
|
</slot>
|
|
469
|
-
</
|
|
469
|
+
</DataGridCell>
|
|
470
470
|
}
|
|
471
471
|
</tr>
|
|
472
472
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import { type DataColumn, type DataGridContext, DataGridContextName } from "./DataGridTypes";
|
|
3
|
+
|
|
4
|
+
interface DataGridCellProps {
|
|
5
|
+
/** An ID for the cell element */
|
|
6
|
+
id?: string;
|
|
7
|
+
/** Classes for the cell element */
|
|
8
|
+
class?: ClassValue;
|
|
9
|
+
/** Styles for the cell element */
|
|
10
|
+
style?: StyleValue;
|
|
11
|
+
/** The column this cell is in (for alignment) */
|
|
12
|
+
column?: DataColumn;
|
|
13
|
+
/** The cell's row position */
|
|
14
|
+
rowIndex: number;
|
|
15
|
+
/** The cell's column position */
|
|
16
|
+
colIndex: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* One cell of a DataGrid's body, carrying the roving tabindex for keyboard
|
|
21
|
+
* navigation. Cell content goes in the default slot.
|
|
22
|
+
*/
|
|
23
|
+
export default function DataGridCell($props: DataGridCellProps | undefined) {
|
|
24
|
+
// Get stuff out of the DataGrid context
|
|
25
|
+
const context = $context[DataGridContextName] as DataGridContext;
|
|
26
|
+
if (!context) {
|
|
27
|
+
throw new Error("DataGridCell must be contained within a DataGrid");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getRowIndex(): number {
|
|
31
|
+
return $props?.rowIndex ?? 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getColIndex(): number {
|
|
35
|
+
return $props?.colIndex ?? 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@render {
|
|
39
|
+
<td
|
|
40
|
+
{$props?.id}
|
|
41
|
+
role="gridcell"
|
|
42
|
+
class={[context.getAlignClass($props?.column?.align), $props?.class]}
|
|
43
|
+
{$props?.style}
|
|
44
|
+
data-cell={context.getCellId(getRowIndex(), getColIndex())}
|
|
45
|
+
tabindex={context.isActiveCell(getRowIndex(), getColIndex()) ? 0 : -1}
|
|
46
|
+
>
|
|
47
|
+
<slot />
|
|
48
|
+
</td>
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import { type DataColumn, type DataGridContext, DataGridContextName } from "./DataGridTypes";
|
|
3
|
+
|
|
4
|
+
interface DataGridColumnHeaderProps {
|
|
5
|
+
/** An ID for the header element */
|
|
6
|
+
id?: string;
|
|
7
|
+
/** Classes for the header element */
|
|
8
|
+
class?: ClassValue;
|
|
9
|
+
/** Styles for the header element */
|
|
10
|
+
style?: StyleValue;
|
|
11
|
+
/** The column this header is for */
|
|
12
|
+
column: DataColumn;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* One column header of a DataGrid, showing the column's label -- in a
|
|
17
|
+
* button that sorts by the column when it is sortable.
|
|
18
|
+
*/
|
|
19
|
+
export default function DataGridColumnHeader($props: DataGridColumnHeaderProps | undefined) {
|
|
20
|
+
// Get stuff out of the DataGrid context
|
|
21
|
+
const context = $context[DataGridContextName] as DataGridContext;
|
|
22
|
+
if (!context) {
|
|
23
|
+
throw new Error("DataGridColumnHeader must be contained within a DataGrid");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getColumn(): DataColumn {
|
|
27
|
+
return $props?.column ?? { key: "" };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@render {
|
|
31
|
+
<th
|
|
32
|
+
{$props?.id}
|
|
33
|
+
scope="col"
|
|
34
|
+
role="columnheader"
|
|
35
|
+
class={[
|
|
36
|
+
context.getAlignClass(getColumn().align),
|
|
37
|
+
getColumn().sortable ? "torp-data-grid-sortable" : undefined,
|
|
38
|
+
$props?.class,
|
|
39
|
+
]}
|
|
40
|
+
{$props?.style}
|
|
41
|
+
aria-sort={context.getAriaSort(getColumn().key)}
|
|
42
|
+
>
|
|
43
|
+
<slot>
|
|
44
|
+
@if (getColumn().sortable) {
|
|
45
|
+
<button type="button" onclick={() => context.toggleSort(getColumn())}>
|
|
46
|
+
{getColumn().label ?? getColumn().key}
|
|
47
|
+
</button>
|
|
48
|
+
} else {
|
|
49
|
+
<span>{getColumn().label ?? getColumn().key}</span>
|
|
50
|
+
}
|
|
51
|
+
</slot>
|
|
52
|
+
</th>
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -23,3 +23,20 @@ export interface DataGridCellSlot<T = any> {
|
|
|
23
23
|
|
|
24
24
|
/** Data passed to the `onload` callback after each successful load */
|
|
25
25
|
export type DataGridLoadEvent<T = any> = LoadResult<T>;
|
|
26
|
+
|
|
27
|
+
export const DataGridContextName: unique symbol = Symbol.for("torp.DataGrid");
|
|
28
|
+
|
|
29
|
+
export interface DataGridContext {
|
|
30
|
+
/** Sorts by a column, toggling the direction when already sorted by it */
|
|
31
|
+
toggleSort: (column: DataColumn) => void;
|
|
32
|
+
/** The aria-sort value for a column header */
|
|
33
|
+
getAriaSort: (key: string) => "ascending" | "descending" | undefined;
|
|
34
|
+
/** The alignment class for a column */
|
|
35
|
+
getAlignClass: (align: "start" | "center" | "end" | undefined) => string | undefined;
|
|
36
|
+
/** Extracts a cell's value from its row */
|
|
37
|
+
getCellValue: (row: any, column: DataColumn) => any;
|
|
38
|
+
/** Whether a cell is the single tab stop (roving tabindex) */
|
|
39
|
+
isActiveCell: (row: number, col: number) => boolean;
|
|
40
|
+
/** The data-cell ID for a cell */
|
|
41
|
+
getCellId: (row: number, col: number) => string;
|
|
42
|
+
}
|
package/dist/DataGrid/index.d.ts
CHANGED
|
@@ -55,7 +55,8 @@ interface DataGridProps {
|
|
|
55
55
|
*
|
|
56
56
|
* See [the WAI-ARIA guidelines for grids](https://www.w3.org/WAI/ARIA/apg/patterns/grid/).
|
|
57
57
|
*
|
|
58
|
-
* The structure of a DataGrid is
|
|
58
|
+
* The structure of a DataGrid is a table of DataGridColumnHeaders and
|
|
59
|
+
* DataGridCells, rendered automatically from the columns and rows:
|
|
59
60
|
*
|
|
60
61
|
* ```
|
|
61
62
|
* <DataGrid columns={columns} data={rows}>
|
|
@@ -87,4 +88,58 @@ declare function DataGrid(
|
|
|
87
88
|
$slots?: Record<string, SlotRender>,
|
|
88
89
|
): void;
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
interface DataGridCellProps {
|
|
94
|
+
/** An ID for the cell element */
|
|
95
|
+
id?: string;
|
|
96
|
+
/** Classes for the cell element */
|
|
97
|
+
class?: ClassValue;
|
|
98
|
+
/** Styles for the cell element */
|
|
99
|
+
style?: StyleValue;
|
|
100
|
+
/** The column this cell is in (for alignment) */
|
|
101
|
+
column?: DataColumn;
|
|
102
|
+
/** The cell's row position */
|
|
103
|
+
rowIndex: number;
|
|
104
|
+
/** The cell's column position */
|
|
105
|
+
colIndex: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* One cell of a DataGrid's body, carrying the roving tabindex for keyboard
|
|
110
|
+
* navigation. Cell content goes in the default slot.
|
|
111
|
+
*/
|
|
112
|
+
declare function DataGridCell(
|
|
113
|
+
$parent: ParentNode,
|
|
114
|
+
$anchor: Node | null,
|
|
115
|
+
$props: DataGridCellProps | undefined,
|
|
116
|
+
$context?: Record<PropertyKey, any>,
|
|
117
|
+
$slots?: Record<string, SlotRender>,
|
|
118
|
+
): void;
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
interface DataGridColumnHeaderProps {
|
|
123
|
+
/** An ID for the header element */
|
|
124
|
+
id?: string;
|
|
125
|
+
/** Classes for the header element */
|
|
126
|
+
class?: ClassValue;
|
|
127
|
+
/** Styles for the header element */
|
|
128
|
+
style?: StyleValue;
|
|
129
|
+
/** The column this header is for */
|
|
130
|
+
column: DataColumn;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* One column header of a DataGrid, showing the column's label -- in a
|
|
135
|
+
* button that sorts by the column when it is sortable.
|
|
136
|
+
*/
|
|
137
|
+
declare function DataGridColumnHeader(
|
|
138
|
+
$parent: ParentNode,
|
|
139
|
+
$anchor: Node | null,
|
|
140
|
+
$props: DataGridColumnHeaderProps | undefined,
|
|
141
|
+
$context?: Record<PropertyKey, any>,
|
|
142
|
+
$slots?: Record<string, SlotRender>,
|
|
143
|
+
): void;
|
|
144
|
+
|
|
145
|
+
export { DataGrid, DataGridCell, DataGridColumnHeader };
|
package/dist/DataGrid/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import { n as normalizeLoadResult } from "../loader-DEeG5vFY.js";
|
|
2
1
|
import DataGrid from "./DataGrid.torp";
|
|
3
|
-
|
|
2
|
+
import DataGridCell from "./DataGridCell.torp";
|
|
3
|
+
import DataGridColumnHeader from "./DataGridColumnHeader.torp";
|
|
4
|
+
export { DataGrid, DataGridCell, DataGridColumnHeader };
|
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
import { type Bindable, type ClassValue, type StyleValue } from "@torpor/view";
|
|
2
|
-
import {
|
|
3
|
-
Calendar,
|
|
4
|
-
CalendarDay,
|
|
5
|
-
CalendarDayHeader,
|
|
6
|
-
CalendarGrid,
|
|
7
|
-
CalendarGridHeader,
|
|
8
|
-
CalendarHeader,
|
|
9
|
-
CalendarHeaderTrigger,
|
|
10
|
-
} from "../Calendar/index";
|
|
11
2
|
import {
|
|
12
3
|
type FieldContext,
|
|
13
4
|
FieldContextName,
|
|
@@ -18,7 +9,9 @@ import { formatDate } from "../utils/dateUtils";
|
|
|
18
9
|
import { addDocumentEvent, removeDocumentEvent } from "../utils/documentEvents";
|
|
19
10
|
import getId from "../utils/getId";
|
|
20
11
|
import createFormField from "../utils/formField";
|
|
21
|
-
import
|
|
12
|
+
import DatePickerContent from "./DatePickerContent.torp";
|
|
13
|
+
import DatePickerTrigger from "./DatePickerTrigger.torp";
|
|
14
|
+
import { type DatePickerContext, DatePickerContextName } from "./DatePickerTypes";
|
|
22
15
|
|
|
23
16
|
interface DatePickerProps {
|
|
24
17
|
/** An ID for the root element */
|
|
@@ -47,12 +40,21 @@ interface DatePickerProps {
|
|
|
47
40
|
* An input for selecting a date from a calendar popout.
|
|
48
41
|
*
|
|
49
42
|
* The structure of a DatePicker is a trigger button that displays the
|
|
50
|
-
* selected date, with a selectable Calendar in a popout
|
|
43
|
+
* selected date, with a selectable Calendar in a popout:
|
|
44
|
+
*
|
|
45
|
+
* ```
|
|
46
|
+
* <DatePicker>
|
|
47
|
+
* <DatePickerTrigger />
|
|
48
|
+
* <DatePickerContent />
|
|
49
|
+
* </DatePicker>
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* With no children, the trigger and content are rendered automatically.
|
|
51
53
|
*/
|
|
52
54
|
export default function DatePicker($props: DatePickerProps | undefined) {
|
|
53
55
|
let defaultId = getId();
|
|
54
56
|
|
|
55
|
-
let content: HTMLDivElement;
|
|
57
|
+
let content: HTMLDivElement | undefined;
|
|
56
58
|
let closeOnClick = false;
|
|
57
59
|
|
|
58
60
|
// Wire up the enclosing Form and Field, if any
|
|
@@ -97,6 +99,33 @@ export default function DatePicker($props: DatePickerProps | undefined) {
|
|
|
97
99
|
setVisible(!$state.visible);
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
// Set the context to pass down to the trigger and content
|
|
103
|
+
let context: DatePickerContext = {
|
|
104
|
+
getDisabled,
|
|
105
|
+
getTriggerId: () => $state.triggerId,
|
|
106
|
+
getContentId: () => $state.contentId,
|
|
107
|
+
isVisible: () => $state.visible,
|
|
108
|
+
toggle,
|
|
109
|
+
getDisplayText: displayText,
|
|
110
|
+
getAriaLabel: () => $props?.ariaLabel,
|
|
111
|
+
inField: () => form.inField,
|
|
112
|
+
valid: () => form.valid,
|
|
113
|
+
messageId: () => form.messageId,
|
|
114
|
+
handleBlur: () => form.handleBlur(),
|
|
115
|
+
getValue: () => $state.value,
|
|
116
|
+
selectDate: handleDate,
|
|
117
|
+
close: handleClose,
|
|
118
|
+
registerContent: (el) => {
|
|
119
|
+
content = el;
|
|
120
|
+
},
|
|
121
|
+
unregisterContent: (el) => {
|
|
122
|
+
if (content === el) {
|
|
123
|
+
content = undefined;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
$context[DatePickerContextName] = context;
|
|
128
|
+
|
|
100
129
|
function setVisible(visible: boolean) {
|
|
101
130
|
$state.visible = visible;
|
|
102
131
|
if (visible) {
|
|
@@ -154,69 +183,20 @@ export default function DatePicker($props: DatePickerProps | undefined) {
|
|
|
154
183
|
}
|
|
155
184
|
}
|
|
156
185
|
|
|
157
|
-
function handleClick(e: MouseEvent) {
|
|
158
|
-
// Don't close when the trigger itself is clicked
|
|
159
|
-
e.stopPropagation();
|
|
160
|
-
toggle();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
186
|
@render {
|
|
164
187
|
<div
|
|
165
188
|
{$props?.id}
|
|
166
189
|
class={["torp-date-picker", getDisabled() ? "torp-date-picker-disabled" : undefined, $props?.class]}
|
|
167
190
|
{$props?.style}
|
|
168
191
|
>
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
aria-haspopup="dialog"
|
|
174
|
-
aria-expanded={$state.visible ? "true" : "false"}
|
|
175
|
-
aria-controls={$state.visible ? $state.contentId : undefined}
|
|
176
|
-
aria-label={$props?.ariaLabel}
|
|
177
|
-
data-valid={form.valid ? "valid" : "invalid"}
|
|
178
|
-
aria-describedby={form.messageId}
|
|
179
|
-
aria-invalid={form.inField && !form.valid ? "true" : undefined}
|
|
180
|
-
onblur={() => form.handleBlur()}
|
|
181
|
-
disabled={getDisabled()}
|
|
182
|
-
onclick={handleClick}
|
|
183
|
-
>
|
|
184
|
-
{displayText()}
|
|
185
|
-
</button>
|
|
186
|
-
|
|
187
|
-
<div
|
|
188
|
-
id={$state.contentId}
|
|
189
|
-
class={["torp-date-picker-content", $state.visible ? undefined : "hidden"]}
|
|
190
|
-
aria-hidden={$state.visible ? undefined : "true"}
|
|
191
|
-
&ref={content}
|
|
192
|
-
>
|
|
193
|
-
<Calendar selectable {$state.value} onchange={handleDate} onclose={handleClose}>
|
|
194
|
-
<CalendarHeader>
|
|
195
|
-
<CalendarHeaderTrigger type="previous" />
|
|
196
|
-
<DatePickerHeader class="torp-date-picker-header" />
|
|
197
|
-
<CalendarHeaderTrigger type="next" />
|
|
198
|
-
</CalendarHeader>
|
|
199
|
-
<CalendarGridHeader>
|
|
200
|
-
@for (let day of $slot.days) {
|
|
201
|
-
<CalendarDayHeader {day.dayName} {day.shortDayName} />
|
|
202
|
-
}
|
|
203
|
-
</CalendarGridHeader>
|
|
204
|
-
<CalendarGrid>
|
|
205
|
-
<CalendarDay date={$slot.day.date} muted={$slot.day.muted} active={$slot.day.active} />
|
|
206
|
-
</CalendarGrid>
|
|
207
|
-
</Calendar>
|
|
208
|
-
</div>
|
|
192
|
+
<slot>
|
|
193
|
+
<DatePickerTrigger />
|
|
194
|
+
<DatePickerContent />
|
|
195
|
+
</slot>
|
|
209
196
|
</div>
|
|
210
197
|
|
|
211
198
|
@if (form.name) {
|
|
212
199
|
<input type="hidden" name={form.name} value={$state.value ? formatDate($unwrap($state.value), "yyyy-MM-dd") : ""} />
|
|
213
200
|
}
|
|
214
201
|
}
|
|
215
|
-
|
|
216
|
-
@style {
|
|
217
|
-
/* Visibility only -- see SelectBoxContent */
|
|
218
|
-
.torp-date-picker-content.hidden {
|
|
219
|
-
display: none;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
202
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import {
|
|
3
|
+
Calendar,
|
|
4
|
+
CalendarDay,
|
|
5
|
+
CalendarDayHeader,
|
|
6
|
+
CalendarGrid,
|
|
7
|
+
CalendarGridHeader,
|
|
8
|
+
CalendarHeader,
|
|
9
|
+
CalendarHeaderTrigger,
|
|
10
|
+
} from "../Calendar/index";
|
|
11
|
+
import { type DatePickerContext, DatePickerContextName } from "./DatePickerTypes";
|
|
12
|
+
import DatePickerHeader from "./DatePickerHeader.torp";
|
|
13
|
+
|
|
14
|
+
interface DatePickerContentProps {
|
|
15
|
+
/** An ID for the content element */
|
|
16
|
+
id?: string;
|
|
17
|
+
/** Classes for the content element */
|
|
18
|
+
class?: ClassValue;
|
|
19
|
+
/** Styles for the content element */
|
|
20
|
+
style?: StyleValue;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The popout of a DatePicker, containing the calendar the user selects a
|
|
25
|
+
* date from.
|
|
26
|
+
*/
|
|
27
|
+
export default function DatePickerContent($props: DatePickerContentProps | undefined) {
|
|
28
|
+
// Get stuff out of the DatePicker context
|
|
29
|
+
const context = $context[DatePickerContextName] as DatePickerContext;
|
|
30
|
+
if (!context) {
|
|
31
|
+
throw new Error("DatePickerContent must be contained within a DatePicker");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@render {
|
|
35
|
+
<div
|
|
36
|
+
id={$props?.id ?? context.getContentId()}
|
|
37
|
+
class={[
|
|
38
|
+
"torp-date-picker-content",
|
|
39
|
+
context.isVisible() ? undefined : "hidden",
|
|
40
|
+
$props?.class,
|
|
41
|
+
]}
|
|
42
|
+
{$props?.style}
|
|
43
|
+
aria-hidden={context.isVisible() ? undefined : "true"}
|
|
44
|
+
onmount={(el) => {
|
|
45
|
+
context.registerContent(el);
|
|
46
|
+
return () => {
|
|
47
|
+
context.unregisterContent(el);
|
|
48
|
+
};
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
<slot>
|
|
52
|
+
<Calendar
|
|
53
|
+
selectable
|
|
54
|
+
value={context.getValue()}
|
|
55
|
+
onchange={context.selectDate}
|
|
56
|
+
onclose={context.close}
|
|
57
|
+
>
|
|
58
|
+
<CalendarHeader>
|
|
59
|
+
<CalendarHeaderTrigger type="previous" />
|
|
60
|
+
<DatePickerHeader class="torp-date-picker-header" />
|
|
61
|
+
<CalendarHeaderTrigger type="next" />
|
|
62
|
+
</CalendarHeader>
|
|
63
|
+
<CalendarGridHeader>
|
|
64
|
+
@for (let day of $slot.days) {
|
|
65
|
+
<CalendarDayHeader {day.dayName} {day.shortDayName} />
|
|
66
|
+
}
|
|
67
|
+
</CalendarGridHeader>
|
|
68
|
+
<CalendarGrid>
|
|
69
|
+
<CalendarDay date={$slot.day.date} muted={$slot.day.muted} active={$slot.day.active} />
|
|
70
|
+
</CalendarGrid>
|
|
71
|
+
</Calendar>
|
|
72
|
+
</slot>
|
|
73
|
+
</div>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@style {
|
|
77
|
+
/* Visibility only -- see SelectBoxContent */
|
|
78
|
+
.torp-date-picker-content.hidden {
|
|
79
|
+
display: none;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import { type DatePickerContext, DatePickerContextName } from "./DatePickerTypes";
|
|
3
|
+
|
|
4
|
+
interface DatePickerTriggerProps {
|
|
5
|
+
/** An ID for the trigger element */
|
|
6
|
+
id?: string;
|
|
7
|
+
/** Classes for the trigger element */
|
|
8
|
+
class?: ClassValue;
|
|
9
|
+
/** Styles for the trigger element */
|
|
10
|
+
style?: StyleValue;
|
|
11
|
+
/** An accessible name for the trigger; falls back to the picker's ariaLabel */
|
|
12
|
+
ariaLabel?: string;
|
|
13
|
+
/** If set to true, the user cannot interact with this trigger */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The button of a DatePicker that displays the selected date and opens the
|
|
19
|
+
* calendar popout.
|
|
20
|
+
*/
|
|
21
|
+
export default function DatePickerTrigger($props: DatePickerTriggerProps | undefined) {
|
|
22
|
+
// Get stuff out of the DatePicker context
|
|
23
|
+
const context = $context[DatePickerContextName] as DatePickerContext;
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error("DatePickerTrigger must be contained within a DatePicker");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getDisabled(): boolean {
|
|
29
|
+
return $props?.disabled === true || context.getDisabled();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@render {
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
id={$props?.id ?? context.getTriggerId()}
|
|
36
|
+
class={["torp-date-picker-trigger", $props?.class]}
|
|
37
|
+
{$props?.style}
|
|
38
|
+
aria-haspopup="dialog"
|
|
39
|
+
aria-expanded={context.isVisible() ? "true" : "false"}
|
|
40
|
+
aria-controls={context.isVisible() ? context.getContentId() : undefined}
|
|
41
|
+
aria-label={$props?.ariaLabel ?? context.getAriaLabel()}
|
|
42
|
+
data-valid={context.valid() ? "valid" : "invalid"}
|
|
43
|
+
aria-describedby={context.messageId()}
|
|
44
|
+
aria-invalid={context.inField() && !context.valid() ? "true" : undefined}
|
|
45
|
+
onblur={() => context.handleBlur()}
|
|
46
|
+
disabled={getDisabled()}
|
|
47
|
+
onclick={(e: MouseEvent) => {
|
|
48
|
+
// Don't close when the trigger itself is clicked
|
|
49
|
+
e.stopPropagation();
|
|
50
|
+
context.toggle();
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<slot>{context.getDisplayText()}</slot>
|
|
54
|
+
</button>
|
|
55
|
+
}
|
|
56
|
+
}
|