@torpor/ui 0.3.6 → 0.4.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/Accordion/Accordion.torp +26 -25
- package/dist/Accordion/AccordionItem.torp +3 -3
- package/dist/Accordion/AccordionTypes.ts +5 -5
- package/dist/Accordion/index.d.ts +2 -2
- package/dist/Calendar/CalendarTypes.ts +1 -1
- package/dist/Charts/BarChart.torp +159 -0
- package/dist/Charts/DataSeries.ts +5 -0
- package/dist/Charts/GridLines.torp +65 -0
- package/dist/Charts/LineChart.torp +196 -0
- package/dist/Charts/PieChart.torp +145 -0
- package/dist/Charts/ScatterChart.torp +215 -0
- package/dist/Charts/SparkLine.torp +71 -0
- package/dist/Charts/XAxis.torp +66 -0
- package/dist/Charts/YAxis.torp +47 -0
- package/dist/Charts/index.d.ts +220 -0
- package/dist/Charts/index.js +34 -0
- package/dist/Charts/index.js.map +1 -0
- package/dist/Contextual/Contextual.torp +8 -3
- package/dist/Contextual/ContextualTypes.ts +1 -1
- package/dist/Dialog/Dialog.torp +12 -16
- package/dist/Dialog/DialogButton.torp +3 -0
- package/dist/Dialog/DialogTypes.ts +2 -2
- package/dist/Dialog/index.d.ts +2 -0
- package/dist/Disclosure/Disclosure.torp +3 -3
- package/dist/Disclosure/DisclosureTypes.ts +1 -1
- package/dist/Drawer/Drawer.torp +2 -2
- package/dist/Drawer/DrawerTypes.ts +1 -1
- package/dist/Form/CheckBox.torp +1 -2
- package/dist/Form/Input.torp +1 -1
- package/dist/Form/MessageSummary.torp +38 -0
- package/dist/Form/TextArea.torp +3 -0
- package/dist/Form/index.d.ts +25 -1
- package/dist/Form/index.js +4 -0
- package/dist/ListBox/ListBox.torp +271 -0
- package/dist/ListBox/ListBoxItem.torp +107 -0
- package/dist/ListBox/ListBoxSeparator.torp +26 -0
- package/dist/ListBox/ListBoxTypes.ts +23 -0
- package/dist/ListBox/index.d.ts +97 -0
- package/dist/ListBox/index.js +14 -0
- package/dist/ListBox/index.js.map +1 -0
- package/dist/Menu/Menu.torp +19 -14
- package/dist/Menu/MenuTypes.ts +5 -4
- package/dist/MenuBar/MenuBarItem.torp +5 -8
- package/dist/MenuBar/MenuBarTypes.ts +2 -2
- package/dist/Modal/Modal.torp +7 -2
- package/dist/Modal/ModalTypes.ts +1 -1
- package/dist/NavMenu/NavMenu.torp +7 -0
- package/dist/NavMenu/NavMenuButton.torp +5 -2
- package/dist/NavMenu/NavMenuPopout.torp +1 -1
- package/dist/NavMenu/{NavMenuPopoutButton.torp → NavMenuPopoutTrigger.torp} +4 -4
- package/dist/NavMenu/NavMenuSeparator.torp +2 -1
- package/dist/NavMenu/NavMenuTypes.ts +4 -3
- package/dist/NavMenu/index.d.ts +22 -18
- package/dist/NavMenu/index.js +4 -4
- package/dist/Pagination/Pagination.torp +145 -0
- package/dist/Pagination/PaginationItem.torp +100 -0
- package/dist/Pagination/PaginationTypes.ts +25 -0
- package/dist/Pagination/buildPages.ts +57 -0
- package/dist/Pagination/index.d.ts +53 -0
- package/dist/Pagination/index.js +40 -0
- package/dist/Pagination/index.js.map +1 -0
- package/dist/Popover/Popover.torp +8 -3
- package/dist/Popover/PopoverTypes.ts +1 -1
- package/dist/SelectBox/SelectBox.torp +136 -0
- package/dist/SelectBox/SelectBoxContent.torp +81 -0
- package/dist/SelectBox/SelectBoxInput.torp +95 -0
- package/dist/SelectBox/SelectBoxTrigger.torp +41 -0
- package/dist/SelectBox/SelectBoxTypes.ts +18 -0
- package/dist/SelectBox/index.d.ts +127 -0
- package/dist/SelectBox/index.js +18 -0
- package/dist/SelectBox/index.js.map +1 -0
- package/dist/TabGroup/TabGroup.torp +3 -3
- package/dist/TabGroup/TabGroupTypes.ts +2 -2
- package/dist/ToolBar/ToolBar.torp +198 -0
- package/dist/ToolBar/ToolBarButton.torp +57 -0
- package/dist/ToolBar/ToolBarElement.torp +55 -0
- package/dist/ToolBar/ToolBarPopout.torp +97 -0
- package/dist/ToolBar/ToolBarPopoutContent.torp +92 -0
- package/dist/ToolBar/ToolBarPopoutTrigger.torp +138 -0
- package/dist/ToolBar/ToolBarSeparator.torp +25 -0
- package/dist/ToolBar/ToolBarTypes.ts +35 -0
- package/dist/ToolBar/index.d.ts +212 -0
- package/dist/ToolBar/index.js +30 -0
- package/dist/ToolBar/index.js.map +1 -0
- package/dist/metafile-esm.json +1 -1
- package/dist/utils/PopoutTypes.ts +35 -0
- package/dist/utils/chartColors.ts +17 -0
- package/dist/utils/chartUtils.ts +71 -0
- package/package.json +22 -2
package/dist/Modal/Modal.torp
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
2
|
import { ModalContextName, type ModalContext, type ModalState } from "./ModalTypes";
|
|
3
|
+
import { PopoutContextName, type PopoutContext } from "../utils/PopoutTypes";
|
|
3
4
|
|
|
4
5
|
interface ModalProps {
|
|
5
6
|
/** An ID for the root element */
|
|
@@ -60,6 +61,10 @@ export default function Modal($props: ModalProps | undefined) {
|
|
|
60
61
|
};
|
|
61
62
|
$context[ModalContextName] = context;
|
|
62
63
|
|
|
64
|
+
// Set the context to pass down to items
|
|
65
|
+
let popoutContext: PopoutContext = context;
|
|
66
|
+
$context[PopoutContextName] = popoutContext;
|
|
67
|
+
|
|
63
68
|
// Dispatch the toggle event when the modal is opened or closed
|
|
64
69
|
let firstToggle = true;
|
|
65
70
|
$run(() => {
|
|
@@ -83,11 +88,11 @@ export default function Modal($props: ModalProps | undefined) {
|
|
|
83
88
|
// Get the button's result
|
|
84
89
|
switch (type) {
|
|
85
90
|
case "confirm": {
|
|
86
|
-
result = value
|
|
91
|
+
result = value ?? true;
|
|
87
92
|
break;
|
|
88
93
|
}
|
|
89
94
|
case "cancel": {
|
|
90
|
-
result = value
|
|
95
|
+
result = value ?? false;
|
|
91
96
|
break;
|
|
92
97
|
}
|
|
93
98
|
default: {
|
package/dist/Modal/ModalTypes.ts
CHANGED
|
@@ -51,6 +51,7 @@ export default function NavMenu($props: NavMenuProps | undefined) {
|
|
|
51
51
|
// Set the focusFirstElement method in the PopoverContext(s) so that it can be called after everything has been shown
|
|
52
52
|
if (popoutContext) {
|
|
53
53
|
popoutContext.focusFirstElement = focusFirstElement;
|
|
54
|
+
popoutContext.focusLastElement = focusLastElement;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
// Set the context to pass down to items
|
|
@@ -74,6 +75,12 @@ export default function NavMenu($props: NavMenuProps | undefined) {
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
function focusLastElement() {
|
|
79
|
+
if (itemStates.length > 0) {
|
|
80
|
+
itemStates.at(-1)!.setFocused();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
77
84
|
function registerItem(setFocused: () => void) {
|
|
78
85
|
// Add the item's state to the array and return its index
|
|
79
86
|
const newItemState: ItemState = {
|
|
@@ -10,6 +10,10 @@ interface NavMenuButtonProps {
|
|
|
10
10
|
style?: StyleValue,
|
|
11
11
|
/** Whether this button is disabled */
|
|
12
12
|
disabled?: boolean,
|
|
13
|
+
/** Raised when the button is clicked */
|
|
14
|
+
onclick?: (e: MouseEvent) => void,
|
|
15
|
+
/** Raised when a key is pressed down */
|
|
16
|
+
onkeydown?: (e: KeyboardEvent) => void,
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
/**
|
|
@@ -43,9 +47,8 @@ export default function NavMenuButton($props: NavMenuButtonProps | undefined) {
|
|
|
43
47
|
role="menuitem"
|
|
44
48
|
aria-disabled={$props?.disabled ? "true" : undefined}
|
|
45
49
|
data-disabled={$props?.disabled ? "true" : undefined}
|
|
46
|
-
onkeydown
|
|
47
50
|
onkeydown={handleItemKey}
|
|
48
|
-
onclick
|
|
51
|
+
{$props?.onclick}
|
|
49
52
|
onfocus={() => handleItemFocus(index)}
|
|
50
53
|
&ref={button}
|
|
51
54
|
>
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type NavMenuPopoutContext,
|
|
7
7
|
} from "./NavMenuTypes";
|
|
8
8
|
|
|
9
|
-
interface
|
|
9
|
+
interface NavMenuPopoutTriggerProps {
|
|
10
10
|
/** An ID for the root element */
|
|
11
11
|
id?: string;
|
|
12
12
|
/** Classes for the root element */
|
|
@@ -23,7 +23,7 @@ interface NavMenuPopoutButtonProps {
|
|
|
23
23
|
* A button that is placed in a NavMenu and signals a response by a user.
|
|
24
24
|
*
|
|
25
25
|
*/
|
|
26
|
-
export default function
|
|
26
|
+
export default function NavMenuPopoutTrigger($props: NavMenuPopoutTriggerProps | undefined) {
|
|
27
27
|
let defaultHoverDelay = 500;
|
|
28
28
|
|
|
29
29
|
let button: HTMLButtonElement;
|
|
@@ -32,14 +32,14 @@ export default function NavMenuPopoutButton($props: NavMenuPopoutButtonProps | u
|
|
|
32
32
|
// Get stuff out of the NavMenuContext
|
|
33
33
|
const menuContext = $context[NavMenuContextName] as NavMenuContext;
|
|
34
34
|
if (!menuContext) {
|
|
35
|
-
throw new Error("
|
|
35
|
+
throw new Error("NavMenuPopoutTrigger must be contained within a NavMenu");
|
|
36
36
|
}
|
|
37
37
|
const { registerItem, handleItemKey, handleItemFocus } = menuContext;
|
|
38
38
|
|
|
39
39
|
// Get stuff out of the NavMenuPopoutContext
|
|
40
40
|
const popoutContext = $context[NavMenuPopoutContextName] as NavMenuPopoutContext;
|
|
41
41
|
if (!popoutContext) {
|
|
42
|
-
throw new Error("
|
|
42
|
+
throw new Error("NavMenuPopoutTrigger must be contained within a NavMenuPopout");
|
|
43
43
|
}
|
|
44
44
|
let $state = popoutContext.state;
|
|
45
45
|
|
|
@@ -16,7 +16,8 @@ export default function NavMenuSeparator($props: NavMenuSeparatorProps | undefin
|
|
|
16
16
|
@render {
|
|
17
17
|
<div
|
|
18
18
|
{$props?.id}
|
|
19
|
-
class={
|
|
19
|
+
class={["torp-nav-menu-separator", $props?.class]}
|
|
20
|
+
role="separator"
|
|
20
21
|
>
|
|
21
22
|
<slot />
|
|
22
23
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export const NavMenuContextName = "NavMenu";
|
|
2
|
-
export const NavMenuPopoutContextName = "NavMenuPopout";
|
|
3
|
-
export const NavMenuGroupContextName = "NavMenuGroup";
|
|
1
|
+
export const NavMenuContextName: unique symbol = Symbol.for("torp.NavMenu");
|
|
2
|
+
export const NavMenuPopoutContextName: unique symbol = Symbol.for("torp.NavMenuPopout");
|
|
3
|
+
export const NavMenuGroupContextName: unique symbol = Symbol.for("torp.NavMenuGroup");
|
|
4
4
|
|
|
5
5
|
export interface NavMenuContext {
|
|
6
6
|
/** Called from a NavMenuLink or NavMenuButton when its button is focused */
|
|
@@ -19,6 +19,7 @@ export interface NavMenuPopoutContext {
|
|
|
19
19
|
};
|
|
20
20
|
anchorElement?: HTMLElement;
|
|
21
21
|
focusFirstElement?: () => void;
|
|
22
|
+
focusLastElement?: () => void;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export interface NavMenuGroupContext {
|
package/dist/NavMenu/index.d.ts
CHANGED
|
@@ -49,6 +49,10 @@ interface NavMenuButtonProps {
|
|
|
49
49
|
style?: StyleValue,
|
|
50
50
|
/** Whether this button is disabled */
|
|
51
51
|
disabled?: boolean,
|
|
52
|
+
/** Raised when the button is clicked */
|
|
53
|
+
onclick?: (e: MouseEvent) => void,
|
|
54
|
+
/** Raised when a key is pressed down */
|
|
55
|
+
onkeydown?: (e: KeyboardEvent) => void,
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
/**
|
|
@@ -193,7 +197,7 @@ interface NavMenuPopoutProps {
|
|
|
193
197
|
*
|
|
194
198
|
* ```
|
|
195
199
|
* <NavMenuPopout>
|
|
196
|
-
* <
|
|
200
|
+
* <NavMenuPopoutTrigger />
|
|
197
201
|
* <NavMenuPopoutContent />
|
|
198
202
|
* </NavMenuPopout>
|
|
199
203
|
* ```
|
|
@@ -208,54 +212,54 @@ declare function NavMenuPopout(
|
|
|
208
212
|
|
|
209
213
|
|
|
210
214
|
|
|
211
|
-
interface
|
|
215
|
+
interface NavMenuPopoutContentProps {
|
|
212
216
|
/** An ID for the root element */
|
|
213
217
|
id?: string;
|
|
214
218
|
/** Classes for the root element */
|
|
215
219
|
class?: ClassValue,
|
|
216
220
|
/** Styles for the root element */
|
|
217
221
|
style?: StyleValue,
|
|
218
|
-
/**
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
|
|
222
|
+
/** Where the popout will be shown, relative to the popout button */
|
|
223
|
+
side?: "top" | "right" | "bottom" | "left",
|
|
224
|
+
/** How the popout will be aligned, relative to the popout button */
|
|
225
|
+
alignment?: "start" | "center" | "end",
|
|
222
226
|
}
|
|
223
227
|
|
|
224
228
|
/**
|
|
225
|
-
*
|
|
229
|
+
* The content that is displayed for a NavMenuPopout when it is open.
|
|
226
230
|
*
|
|
227
231
|
*/
|
|
228
|
-
declare function
|
|
232
|
+
declare function NavMenuPopoutContent(
|
|
229
233
|
$parent: ParentNode,
|
|
230
234
|
$anchor: Node | null,
|
|
231
|
-
$props:
|
|
235
|
+
$props: NavMenuPopoutContentProps | undefined,
|
|
232
236
|
$context?: Record<PropertyKey, any>,
|
|
233
237
|
$slots?: Record<string, SlotRender>,
|
|
234
238
|
): void;
|
|
235
239
|
|
|
236
240
|
|
|
237
241
|
|
|
238
|
-
interface
|
|
242
|
+
interface NavMenuPopoutTriggerProps {
|
|
239
243
|
/** An ID for the root element */
|
|
240
244
|
id?: string;
|
|
241
245
|
/** Classes for the root element */
|
|
242
246
|
class?: ClassValue,
|
|
243
247
|
/** Styles for the root element */
|
|
244
248
|
style?: StyleValue,
|
|
245
|
-
/**
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
|
|
249
|
+
/** The delay before showing the popout, in milliseconds */
|
|
250
|
+
hoverDelay?: number,
|
|
251
|
+
/** Whether this button is disabled */
|
|
252
|
+
disabled?: boolean,
|
|
249
253
|
}
|
|
250
254
|
|
|
251
255
|
/**
|
|
252
|
-
*
|
|
256
|
+
* A button that is placed in a NavMenu and signals a response by a user.
|
|
253
257
|
*
|
|
254
258
|
*/
|
|
255
|
-
declare function
|
|
259
|
+
declare function NavMenuPopoutTrigger(
|
|
256
260
|
$parent: ParentNode,
|
|
257
261
|
$anchor: Node | null,
|
|
258
|
-
$props:
|
|
262
|
+
$props: NavMenuPopoutTriggerProps | undefined,
|
|
259
263
|
$context?: Record<PropertyKey, any>,
|
|
260
264
|
$slots?: Record<string, SlotRender>,
|
|
261
265
|
): void;
|
|
@@ -282,4 +286,4 @@ declare function NavMenuSeparator(
|
|
|
282
286
|
$slots?: Record<string, SlotRender>,
|
|
283
287
|
): void;
|
|
284
288
|
|
|
285
|
-
export { NavMenu, NavMenuButton, NavMenuElement, NavMenuGroup, NavMenuGroupHeader, NavMenuLink, NavMenuPopout,
|
|
289
|
+
export { NavMenu, NavMenuButton, NavMenuElement, NavMenuGroup, NavMenuGroupHeader, NavMenuLink, NavMenuPopout, NavMenuPopoutContent, NavMenuPopoutTrigger, NavMenuSeparator };
|
package/dist/NavMenu/index.js
CHANGED
|
@@ -19,12 +19,12 @@ import NavMenuLink_default from "./NavMenuLink.torp";
|
|
|
19
19
|
// src/NavMenu/NavMenuPopout.torp
|
|
20
20
|
import NavMenuPopout_default from "./NavMenuPopout.torp";
|
|
21
21
|
|
|
22
|
-
// src/NavMenu/NavMenuPopoutButton.torp
|
|
23
|
-
import NavMenuPopoutButton_default from "./NavMenuPopoutButton.torp";
|
|
24
|
-
|
|
25
22
|
// src/NavMenu/NavMenuPopoutContent.torp
|
|
26
23
|
import NavMenuPopoutContent_default from "./NavMenuPopoutContent.torp";
|
|
27
24
|
|
|
25
|
+
// src/NavMenu/NavMenuPopoutTrigger.torp
|
|
26
|
+
import NavMenuPopoutTrigger_default from "./NavMenuPopoutTrigger.torp";
|
|
27
|
+
|
|
28
28
|
// src/NavMenu/NavMenuSeparator.torp
|
|
29
29
|
import NavMenuSeparator_default from "./NavMenuSeparator.torp";
|
|
30
30
|
export {
|
|
@@ -35,8 +35,8 @@ export {
|
|
|
35
35
|
NavMenuGroupHeader_default as NavMenuGroupHeader,
|
|
36
36
|
NavMenuLink_default as NavMenuLink,
|
|
37
37
|
NavMenuPopout_default as NavMenuPopout,
|
|
38
|
-
NavMenuPopoutButton_default as NavMenuPopoutButton,
|
|
39
38
|
NavMenuPopoutContent_default as NavMenuPopoutContent,
|
|
39
|
+
NavMenuPopoutTrigger_default as NavMenuPopoutTrigger,
|
|
40
40
|
NavMenuSeparator_default as NavMenuSeparator
|
|
41
41
|
};
|
|
42
42
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import { PaginationContextName, type PaginationContext, type ItemState, type PageNumber } from "./PaginationTypes";
|
|
3
|
+
|
|
4
|
+
interface PaginationProps {
|
|
5
|
+
/** An ID for the root element */
|
|
6
|
+
id?: string;
|
|
7
|
+
/** Classes for the root element */
|
|
8
|
+
class?: ClassValue,
|
|
9
|
+
/** Styles for the root element */
|
|
10
|
+
style?: StyleValue,
|
|
11
|
+
/** The total number of pages (or items if the page size is greater than 1) */
|
|
12
|
+
count: number,
|
|
13
|
+
/** The active page number */
|
|
14
|
+
number: number,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Buttons with page numbers that can be used to move between pages.
|
|
19
|
+
*/
|
|
20
|
+
export default function Pagination($props: PaginationProps) {
|
|
21
|
+
// TODO: Should we (by default) create PaginationItems?
|
|
22
|
+
|
|
23
|
+
// TODO: Do we actually need ItemStates when we're creating the item collection ourselves??
|
|
24
|
+
// A collection containing the active state and some functions for each item
|
|
25
|
+
let itemStates: ItemState[] = [];
|
|
26
|
+
|
|
27
|
+
// Put stuff into a PaginationContext
|
|
28
|
+
const context: PaginationContext = {
|
|
29
|
+
registerItem,
|
|
30
|
+
removeItem,
|
|
31
|
+
toggleItem,
|
|
32
|
+
handleItemKey
|
|
33
|
+
};
|
|
34
|
+
$context[PaginationContextName] = context;
|
|
35
|
+
|
|
36
|
+
function registerItem(
|
|
37
|
+
target: PageNumber,
|
|
38
|
+
setActive: (value: boolean) => void,
|
|
39
|
+
setFocused: () => void
|
|
40
|
+
) {
|
|
41
|
+
// Add the item's state to the array
|
|
42
|
+
const newItemState: ItemState = {
|
|
43
|
+
target,
|
|
44
|
+
active: false,
|
|
45
|
+
setActive,
|
|
46
|
+
setFocused
|
|
47
|
+
};
|
|
48
|
+
itemStates.push(newItemState);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function removeItem(target: PageNumber) {
|
|
52
|
+
// Remove the item
|
|
53
|
+
const index = itemStates.findIndex((i) => i.target === target);
|
|
54
|
+
if (index != -1) {
|
|
55
|
+
itemStates.splice(index, 1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function toggleItem(target: PageNumber) {
|
|
60
|
+
switch (target) {
|
|
61
|
+
case "start": {
|
|
62
|
+
$props.number = 1;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case "previous": {
|
|
66
|
+
$props.number = Math.max(1, $props.number - 1);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case "next": {
|
|
70
|
+
$props.number = Math.min($props.count, $props.number + 1);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case "end": {
|
|
74
|
+
$props.number = $props.count;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case "startgap":
|
|
78
|
+
case "endgap": {
|
|
79
|
+
let input = prompt("Go to page:");
|
|
80
|
+
if (input) {
|
|
81
|
+
$props.number = parseInt(input);
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
default: {
|
|
86
|
+
$props.number = target;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const item = itemStates.find((i) => i.target === target);
|
|
91
|
+
if (item) {
|
|
92
|
+
item.setFocused();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function handleFocus(e: FocusEvent & { currentTarget: EventTarget & HTMLElement }) {
|
|
97
|
+
// Focus the first button so that its key events can be triggered by the user
|
|
98
|
+
const el = e.currentTarget.getElementsByTagName("button")[0];
|
|
99
|
+
if (el) {
|
|
100
|
+
el.focus();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function handleItemKey(e: KeyboardEvent) {
|
|
105
|
+
// TODO: Actually just change focus??
|
|
106
|
+
switch (e.key) {
|
|
107
|
+
case "Left":
|
|
108
|
+
case "ArrowLeft": {
|
|
109
|
+
toggleItem("previous");
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case "Right":
|
|
114
|
+
case "ArrowRight": {
|
|
115
|
+
toggleItem("next");
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case "Home": {
|
|
120
|
+
toggleItem("start");
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
case "End": {
|
|
125
|
+
toggleItem("end");
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@render {
|
|
133
|
+
@if ($props.count > 0) {
|
|
134
|
+
<div
|
|
135
|
+
{$props?.id}
|
|
136
|
+
class={["torp-pagination", $props?.class]}
|
|
137
|
+
{$props?.style}
|
|
138
|
+
tabindex="0"
|
|
139
|
+
onfocus={handleFocus}
|
|
140
|
+
>
|
|
141
|
+
<slot />
|
|
142
|
+
</div>
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ClassValue, StyleValue } from "@torpor/view";
|
|
2
|
+
import { PaginationContextName, type PaginationContext, type PageNumber } from "./PaginationTypes";
|
|
3
|
+
|
|
4
|
+
interface PaginationItemProps {
|
|
5
|
+
/** An ID for the root element */
|
|
6
|
+
id?: string;
|
|
7
|
+
/** Classes for the root element */
|
|
8
|
+
class?: ClassValue,
|
|
9
|
+
/** Styles for the root element */
|
|
10
|
+
style?: StyleValue,
|
|
11
|
+
/** The page for the button */
|
|
12
|
+
page: PageNumber;
|
|
13
|
+
/** Whether this is the active item */
|
|
14
|
+
active?: boolean;
|
|
15
|
+
/** Raised when this item is made active */
|
|
16
|
+
ontoggle?: (active: boolean) => void,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default function PaginationItem($props: PaginationItemProps) {
|
|
20
|
+
let button: HTMLButtonElement;
|
|
21
|
+
|
|
22
|
+
// Get stuff out of the Pagination context
|
|
23
|
+
const context = $context[PaginationContextName] as PaginationContext;
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error("PaginationItem must be contained within a Pagination");
|
|
26
|
+
}
|
|
27
|
+
const { registerItem, /*removeItem,*/ toggleItem, handleItemKey } = context;
|
|
28
|
+
|
|
29
|
+
// Register this item with the parent Pagination, which will handle setting this item's focus on keyboard events
|
|
30
|
+
registerItem($props.page, setActive, setFocused);
|
|
31
|
+
|
|
32
|
+
// This function is called by the parent Pagination to set this item's active value
|
|
33
|
+
function setActive(value: boolean) {
|
|
34
|
+
if ($props.active !== value) {
|
|
35
|
+
$props.active = value;
|
|
36
|
+
|
|
37
|
+
/** Raised when this item is made active */
|
|
38
|
+
//dispatch("toggle", value);
|
|
39
|
+
if ($props?.ontoggle) {
|
|
40
|
+
$props.ontoggle(value);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// This function is called by the parent Pagination to focus this item's button
|
|
46
|
+
function setFocused() {
|
|
47
|
+
button.focus();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function handleClick() {
|
|
51
|
+
// Pass the value to the toggleItem method in the parent Pagination which will handle toggling
|
|
52
|
+
// the value for all items
|
|
53
|
+
toggleItem($props.page);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function handleKey(e: KeyboardEvent) {
|
|
57
|
+
// Pass the key press up to the parent Pagination which will handle setting the focused button
|
|
58
|
+
handleItemKey(e);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@render {
|
|
62
|
+
@if ($props.active) {
|
|
63
|
+
<span
|
|
64
|
+
{$props?.id}
|
|
65
|
+
class={["torp-pagination-item", $props?.class]}
|
|
66
|
+
{$props?.style}
|
|
67
|
+
data-state={$props.active ? "active" : "inactive"}
|
|
68
|
+
>
|
|
69
|
+
{$props.page}
|
|
70
|
+
</span>
|
|
71
|
+
} else {
|
|
72
|
+
<button
|
|
73
|
+
type="button"
|
|
74
|
+
{$props?.id}
|
|
75
|
+
class={["torp-pagination-item", $props?.class]}
|
|
76
|
+
data-type={$props.page}
|
|
77
|
+
data-state={$props.active ? "active" : "inactive"}
|
|
78
|
+
&ref={button}
|
|
79
|
+
onclick={handleClick}
|
|
80
|
+
onkeydown={handleKey}
|
|
81
|
+
>
|
|
82
|
+
<slot>
|
|
83
|
+
@if ($props.page === "start") {
|
|
84
|
+
<span><<</span>
|
|
85
|
+
} else if ($props.page === "previous") {
|
|
86
|
+
<span><</span>
|
|
87
|
+
} else if ($props.page === "next") {
|
|
88
|
+
<span>></span>
|
|
89
|
+
} else if ($props.page === "end") {
|
|
90
|
+
<span>>></span>
|
|
91
|
+
} else if ($props.page === "startgap" || $props.page === "endgap") {
|
|
92
|
+
<span>…</span>
|
|
93
|
+
} else {
|
|
94
|
+
<span>{$props.page}</span>
|
|
95
|
+
}
|
|
96
|
+
</slot>
|
|
97
|
+
</button>
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const PaginationContextName: unique symbol = Symbol.for("torp.Pagination");
|
|
2
|
+
|
|
3
|
+
export interface PaginationContext {
|
|
4
|
+
// The registerItem function is called from each SeriesItem to register itself and its
|
|
5
|
+
// methods with this component
|
|
6
|
+
registerItem: (
|
|
7
|
+
target: PageNumber,
|
|
8
|
+
setActive: (value: boolean) => void,
|
|
9
|
+
setFocused: () => void,
|
|
10
|
+
) => void;
|
|
11
|
+
// The removeItem function is called from a SeriesItem when it has been unloaded so that we
|
|
12
|
+
// can remove it from the itemStates array
|
|
13
|
+
removeItem: (target: PageNumber) => void;
|
|
14
|
+
toggleItem: (target: PageNumber) => void;
|
|
15
|
+
handleItemKey: (e: KeyboardEvent) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ItemState {
|
|
19
|
+
target: PageNumber;
|
|
20
|
+
active: boolean;
|
|
21
|
+
setActive: (value: boolean) => void;
|
|
22
|
+
setFocused: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type PageNumber = "start" | "startgap" | "previous" | "next" | "end" | "endgap" | number;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type PageNumber } from "./PaginationTypes";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builds the pages that should be displayed for pagination.
|
|
5
|
+
*
|
|
6
|
+
* @param count The total number of pages (or items if the page size is greater than 1)
|
|
7
|
+
* @param number The active page number
|
|
8
|
+
* @param pageSize The number of items on each page
|
|
9
|
+
* @param maxPages The maximum number of pages to display (gaps will be indicated with ellipses)
|
|
10
|
+
*/
|
|
11
|
+
export default function buildPages(
|
|
12
|
+
count: number,
|
|
13
|
+
number: number,
|
|
14
|
+
pageSize: number = 1,
|
|
15
|
+
maxPages: number = 9,
|
|
16
|
+
): PageNumber[] {
|
|
17
|
+
// Calculate the page count
|
|
18
|
+
const pageCount = Math.ceil(count / pageSize);
|
|
19
|
+
|
|
20
|
+
if (pageCount <= maxPages) {
|
|
21
|
+
// There"s no need for ellipses, just return the numbers
|
|
22
|
+
return Array.from({ length: pageCount })
|
|
23
|
+
.fill(0)
|
|
24
|
+
.map((_, i) => i + 1);
|
|
25
|
+
} else {
|
|
26
|
+
// Get the start and end around the current page number
|
|
27
|
+
// Use floor and ceil in case there is an even number of pages (e.g. if
|
|
28
|
+
// maxPages is 8, there will be 3 numbers at the start and 4 at the end)
|
|
29
|
+
let start = number - Math.floor((maxPages - 1) / 2);
|
|
30
|
+
let end = number + Math.ceil((maxPages - 1) / 2);
|
|
31
|
+
if (start < 1) {
|
|
32
|
+
end += Math.abs(start) + 1;
|
|
33
|
+
start = 1;
|
|
34
|
+
} else if (end > pageCount) {
|
|
35
|
+
start -= end - pageCount;
|
|
36
|
+
end = pageCount;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Build the numbers
|
|
40
|
+
const numbers: PageNumber[] = Array.from({ length: end - start + 1 })
|
|
41
|
+
.fill(0)
|
|
42
|
+
.map((_, i) => start + i);
|
|
43
|
+
|
|
44
|
+
// Replace numbers at the start and end with ellipses if necessary
|
|
45
|
+
if (numbers[0] !== 1) {
|
|
46
|
+
numbers[0] = 1;
|
|
47
|
+
numbers[1] = "startgap";
|
|
48
|
+
}
|
|
49
|
+
const l = numbers.length;
|
|
50
|
+
if (numbers[l - 1] !== pageCount) {
|
|
51
|
+
numbers[l - 1] = pageCount;
|
|
52
|
+
numbers[l - 2] = "endgap";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return numbers;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type SlotRender } from "@torpor/view";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
interface PaginationProps {
|
|
5
|
+
/** An ID for the root element */
|
|
6
|
+
id?: string;
|
|
7
|
+
/** Classes for the root element */
|
|
8
|
+
class?: ClassValue,
|
|
9
|
+
/** Styles for the root element */
|
|
10
|
+
style?: StyleValue,
|
|
11
|
+
/** The total number of pages (or items if the page size is greater than 1) */
|
|
12
|
+
count: number,
|
|
13
|
+
/** The active page number */
|
|
14
|
+
number: number,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Buttons with page numbers that can be used to move between pages.
|
|
19
|
+
*/
|
|
20
|
+
declare function Pagination(
|
|
21
|
+
$parent: ParentNode,
|
|
22
|
+
$anchor: Node | null,
|
|
23
|
+
$props: PaginationProps,
|
|
24
|
+
$context?: Record<PropertyKey, any>,
|
|
25
|
+
$slots?: Record<string, SlotRender>,
|
|
26
|
+
): void;
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
interface PaginationItemProps {
|
|
31
|
+
/** An ID for the root element */
|
|
32
|
+
id?: string;
|
|
33
|
+
/** Classes for the root element */
|
|
34
|
+
class?: ClassValue,
|
|
35
|
+
/** Styles for the root element */
|
|
36
|
+
style?: StyleValue,
|
|
37
|
+
/** The page for the button */
|
|
38
|
+
page: PageNumber;
|
|
39
|
+
/** Whether this is the active item */
|
|
40
|
+
active?: boolean;
|
|
41
|
+
/** Raised when this item is made active */
|
|
42
|
+
ontoggle?: (active: boolean) => void,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare function PaginationItem(
|
|
46
|
+
$parent: ParentNode,
|
|
47
|
+
$anchor: Node | null,
|
|
48
|
+
$props: PaginationItemProps,
|
|
49
|
+
$context?: Record<PropertyKey, any>,
|
|
50
|
+
$slots?: Record<string, SlotRender>,
|
|
51
|
+
): void;
|
|
52
|
+
|
|
53
|
+
export { Pagination, PaginationItem };
|