drab 2.6.0 → 2.7.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/components/Accordion.svelte +34 -9
- package/dist/components/Accordion.svelte.d.ts +34 -9
- package/dist/components/Breakpoint.svelte +3 -1
- package/dist/components/Breakpoint.svelte.d.ts +3 -1
- package/dist/components/Chord.svelte +1 -1
- package/dist/components/Chord.svelte.d.ts +1 -1
- package/dist/components/ContextMenu.svelte +72 -47
- package/dist/components/ContextMenu.svelte.d.ts +9 -8
- package/dist/components/CopyButton.svelte +12 -4
- package/dist/components/CopyButton.svelte.d.ts +1 -1
- package/dist/components/DataTable.svelte +65 -21
- package/dist/components/DataTable.svelte.d.ts +68 -15
- package/dist/components/Editor.svelte +1 -0
- package/dist/components/FullscreenButton.svelte +20 -14
- package/dist/components/FullscreenButton.svelte.d.ts +6 -7
- package/dist/components/Popover.svelte +60 -93
- package/dist/components/Popover.svelte.d.ts +25 -32
- package/dist/components/ShareButton.svelte +11 -3
- package/dist/components/ShareButton.svelte.d.ts +1 -1
- package/dist/components/Sheet.svelte +43 -34
- package/dist/components/Sheet.svelte.d.ts +10 -8
- package/dist/components/Tabs.svelte +77 -53
- package/dist/components/Tabs.svelte.d.ts +53 -47
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/util/delay.d.ts +1 -0
- package/dist/util/delay.js +1 -0
- package/dist/util/transition.d.ts +1 -1
- package/dist/util/transition.js +1 -1
- package/package.json +1 -1
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
### Accordion
|
5
5
|
|
6
|
-
Displays a list of `details` elements.
|
6
|
+
Displays a list of `details` elements with helpful defaults and transitions.
|
7
7
|
|
8
8
|
@props
|
9
9
|
|
@@ -30,16 +30,18 @@ Displays a list of `details` elements.
|
|
30
30
|
@example
|
31
31
|
|
32
32
|
```svelte
|
33
|
-
<script>
|
33
|
+
<script lang="ts">
|
34
34
|
import { Accordion } from "drab";
|
35
|
+
import { FullscreenButton } from "drab";
|
35
36
|
import { Chevron } from "../../site/svg/Chevron.svelte";
|
36
37
|
</script>
|
37
38
|
|
38
39
|
<Accordion
|
39
40
|
icon={Chevron}
|
41
|
+
class="mb-12"
|
40
42
|
classDetails="border-b"
|
41
|
-
classHeader="flex gap-8 cursor-pointer items-center justify-between
|
42
|
-
classContent="pb-4"
|
43
|
+
classHeader="flex gap-8 cursor-pointer items-center justify-between p-4 font-bold underline hover:decoration-dotted"
|
44
|
+
classContent="pb-4 px-4"
|
43
45
|
items={[
|
44
46
|
{ summary: "Is it accessible?", content: "Yes." },
|
45
47
|
{
|
@@ -52,12 +54,35 @@ Displays a list of `details` elements.
|
|
52
54
|
},
|
53
55
|
{ summary: "Does it work without Javascript?", content: "Yes." },
|
54
56
|
]}
|
57
|
+
/>
|
58
|
+
|
59
|
+
<Accordion
|
60
|
+
icon={Chevron}
|
61
|
+
classDetails="border-b"
|
62
|
+
classHeader="flex gap-8 cursor-pointer items-center justify-between p-4 font-bold underline hover:decoration-dotted"
|
63
|
+
classContent="pb-4 px-4"
|
64
|
+
autoClose={false}
|
65
|
+
items={[
|
66
|
+
{ summary: "Summary", content: "Content" },
|
67
|
+
{ summary: "Summary", content: "Content", data: { uppercase: true } },
|
68
|
+
{
|
69
|
+
summary: "Summary",
|
70
|
+
content: "Content",
|
71
|
+
data: { component: FullscreenButton },
|
72
|
+
},
|
73
|
+
]}
|
55
74
|
>
|
56
|
-
<svelte:fragment slot="
|
57
|
-
<
|
58
|
-
|
59
|
-
|
60
|
-
</
|
75
|
+
<svelte:fragment slot="summary" let:item let:index>
|
76
|
+
<span class:uppercase={item.data?.uppercase}>
|
77
|
+
{item.summary}
|
78
|
+
{index + 1}
|
79
|
+
</span>
|
80
|
+
</svelte:fragment>
|
81
|
+
<svelte:fragment slot="content" let:item>
|
82
|
+
<span>{item.content}</span>
|
83
|
+
{#if item.data?.component === FullscreenButton}
|
84
|
+
<div><svelte:component this={FullscreenButton} class="btn mt-4" /></div>
|
85
|
+
{/if}
|
61
86
|
</svelte:fragment>
|
62
87
|
</Accordion>
|
63
88
|
```
|
@@ -49,7 +49,7 @@ export type AccordionSlots = typeof __propDef.slots;
|
|
49
49
|
/**
|
50
50
|
* ### Accordion
|
51
51
|
*
|
52
|
-
* Displays a list of `details` elements.
|
52
|
+
* Displays a list of `details` elements with helpful defaults and transitions.
|
53
53
|
*
|
54
54
|
* @props
|
55
55
|
*
|
@@ -76,16 +76,18 @@ export type AccordionSlots = typeof __propDef.slots;
|
|
76
76
|
* @example
|
77
77
|
*
|
78
78
|
* ```svelte
|
79
|
-
* <script>
|
79
|
+
* <script lang="ts">
|
80
80
|
* import { Accordion } from "drab";
|
81
|
+
* import { FullscreenButton } from "drab";
|
81
82
|
* import { Chevron } from "../../site/svg/Chevron.svelte";
|
82
83
|
* </script>
|
83
84
|
*
|
84
85
|
* <Accordion
|
85
86
|
* icon={Chevron}
|
87
|
+
* class="mb-12"
|
86
88
|
* classDetails="border-b"
|
87
|
-
* classHeader="flex gap-8 cursor-pointer items-center justify-between
|
88
|
-
* classContent="pb-4"
|
89
|
+
* classHeader="flex gap-8 cursor-pointer items-center justify-between p-4 font-bold underline hover:decoration-dotted"
|
90
|
+
* classContent="pb-4 px-4"
|
89
91
|
* items={[
|
90
92
|
* { summary: "Is it accessible?", content: "Yes." },
|
91
93
|
* {
|
@@ -98,12 +100,35 @@ export type AccordionSlots = typeof __propDef.slots;
|
|
98
100
|
* },
|
99
101
|
* { summary: "Does it work without Javascript?", content: "Yes." },
|
100
102
|
* ]}
|
103
|
+
* />
|
104
|
+
*
|
105
|
+
* <Accordion
|
106
|
+
* icon={Chevron}
|
107
|
+
* classDetails="border-b"
|
108
|
+
* classHeader="flex gap-8 cursor-pointer items-center justify-between p-4 font-bold underline hover:decoration-dotted"
|
109
|
+
* classContent="pb-4 px-4"
|
110
|
+
* autoClose={false}
|
111
|
+
* items={[
|
112
|
+
* { summary: "Summary", content: "Content" },
|
113
|
+
* { summary: "Summary", content: "Content", data: { uppercase: true } },
|
114
|
+
* {
|
115
|
+
* summary: "Summary",
|
116
|
+
* content: "Content",
|
117
|
+
* data: { component: FullscreenButton },
|
118
|
+
* },
|
119
|
+
* ]}
|
101
120
|
* >
|
102
|
-
* <svelte:fragment slot="
|
103
|
-
* <
|
104
|
-
*
|
105
|
-
*
|
106
|
-
* </
|
121
|
+
* <svelte:fragment slot="summary" let:item let:index>
|
122
|
+
* <span class:uppercase={item.data?.uppercase}>
|
123
|
+
* {item.summary}
|
124
|
+
* {index + 1}
|
125
|
+
* </span>
|
126
|
+
* </svelte:fragment>
|
127
|
+
* <svelte:fragment slot="content" let:item>
|
128
|
+
* <span>{item.content}</span>
|
129
|
+
* {#if item.data?.component === FullscreenButton}
|
130
|
+
* <div><svelte:component this={FullscreenButton} class="btn mt-4" /></div>
|
131
|
+
* {/if}
|
107
132
|
* </svelte:fragment>
|
108
133
|
* </Accordion>
|
109
134
|
* ```
|
@@ -3,7 +3,9 @@
|
|
3
3
|
|
4
4
|
### Breakpoints
|
5
5
|
|
6
|
-
Displays the current breakpoint and `window.innerWidth`, based on the `breakpoints` provided. Defaults to [TailwindCSS breakpoint sizes](https://tailwindcss.com/docs/responsive-design
|
6
|
+
Displays the current breakpoint and `window.innerWidth`, based on the `breakpoints` provided. Defaults to [TailwindCSS breakpoint sizes](https://tailwindcss.com/docs/responsive-design).
|
7
|
+
|
8
|
+
With SvelteKit, this component can be wrapped in an `{#if dev}` block that checks for the [dev module](https://kit.svelte.dev/docs/modules#$app-environment-dev), to show only during development.
|
7
9
|
|
8
10
|
@props
|
9
11
|
|
@@ -19,7 +19,9 @@ export type BreakpointSlots = typeof __propDef.slots;
|
|
19
19
|
/**
|
20
20
|
* ### Breakpoints
|
21
21
|
*
|
22
|
-
* Displays the current breakpoint and `window.innerWidth`, based on the `breakpoints` provided. Defaults to [TailwindCSS breakpoint sizes](https://tailwindcss.com/docs/responsive-design
|
22
|
+
* Displays the current breakpoint and `window.innerWidth`, based on the `breakpoints` provided. Defaults to [TailwindCSS breakpoint sizes](https://tailwindcss.com/docs/responsive-design).
|
23
|
+
*
|
24
|
+
* With SvelteKit, this component can be wrapped in an `{#if dev}` block that checks for the [dev module](https://kit.svelte.dev/docs/modules#$app-environment-dev), to show only during development.
|
23
25
|
*
|
24
26
|
* @props
|
25
27
|
*
|
@@ -3,14 +3,14 @@
|
|
3
3
|
|
4
4
|
### ContextMenu
|
5
5
|
|
6
|
-
Displays when the parent element is right clicked.
|
6
|
+
Displays when the parent element is right clicked, or long pressed on mobile.
|
7
7
|
|
8
8
|
@props
|
9
9
|
|
10
|
-
- `classNoscript` - `noscript` class
|
11
10
|
- `class`
|
12
11
|
- `display` - controls `display` css property
|
13
12
|
- `id`
|
13
|
+
- `transition` - fades the content, set to `false` to remove
|
14
14
|
|
15
15
|
@slots
|
16
16
|
|
@@ -27,12 +27,12 @@ Displays when the parent element is right clicked.
|
|
27
27
|
|
28
28
|
<div class="flex justify-center rounded border border-dashed p-12">
|
29
29
|
<div>Right click here</div>
|
30
|
-
<ContextMenu
|
31
|
-
<div class="
|
30
|
+
<ContextMenu>
|
31
|
+
<div class="flex w-48 flex-col gap-2 rounded border bg-white p-2 shadow">
|
32
32
|
<div class="font-bold">Context Menu</div>
|
33
|
-
<button class="btn">Button</button>
|
34
|
-
<button class="btn">Button</button>
|
35
|
-
<button class="btn">Button</button>
|
33
|
+
<button role="menuitem" class="btn">Button</button>
|
34
|
+
<button role="menuitem" class="btn">Button</button>
|
35
|
+
<button role="menuitem" class="btn">Button</button>
|
36
36
|
</div>
|
37
37
|
</ContextMenu>
|
38
38
|
</div>
|
@@ -40,12 +40,18 @@ Displays when the parent element is right clicked.
|
|
40
40
|
-->
|
41
41
|
|
42
42
|
<script>import { onMount, tick } from "svelte";
|
43
|
+
import { fade } from "svelte/transition";
|
44
|
+
import { duration } from "../util/transition";
|
45
|
+
import { messageNoScript } from "../util/messages";
|
46
|
+
import { prefersReducedMotion } from "../util/accessibility";
|
47
|
+
import { delay } from "../util/delay";
|
43
48
|
let className = "";
|
44
49
|
export { className as class };
|
45
50
|
export let id = "";
|
46
51
|
export let display = false;
|
47
|
-
export let
|
52
|
+
export let transition = { duration };
|
48
53
|
let contextMenu;
|
54
|
+
let base;
|
49
55
|
let coordinates = { x: 0, y: 0 };
|
50
56
|
const hide = () => display = false;
|
51
57
|
const onKeyDown = (e) => {
|
@@ -53,58 +59,77 @@ const onKeyDown = (e) => {
|
|
53
59
|
display = false;
|
54
60
|
}
|
55
61
|
};
|
62
|
+
const displayMenu = async (e) => {
|
63
|
+
e.preventDefault();
|
64
|
+
const scrollY = window.scrollY;
|
65
|
+
const scrollX = window.scrollX;
|
66
|
+
const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
|
67
|
+
const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
|
68
|
+
coordinates.x = clientX + scrollX;
|
69
|
+
coordinates.y = clientY + scrollY;
|
70
|
+
display = true;
|
71
|
+
await tick();
|
72
|
+
const offsetWidth = contextMenu.offsetWidth + 24;
|
73
|
+
const offsetHeight = contextMenu.offsetHeight + 6;
|
74
|
+
const innerWidth = window.innerWidth;
|
75
|
+
const innerHeight = window.innerHeight;
|
76
|
+
if (coordinates.x + offsetWidth > scrollX + innerWidth) {
|
77
|
+
coordinates.x = scrollX + innerWidth - offsetWidth;
|
78
|
+
}
|
79
|
+
if (coordinates.y + offsetHeight > scrollY + innerHeight) {
|
80
|
+
coordinates.y = scrollY + innerHeight - offsetHeight;
|
81
|
+
}
|
82
|
+
};
|
83
|
+
let timer;
|
84
|
+
const onTouchStart = (e) => {
|
85
|
+
timer = setTimeout(() => {
|
86
|
+
displayMenu(e);
|
87
|
+
}, delay);
|
88
|
+
};
|
89
|
+
const onTouchEnd = () => {
|
90
|
+
clearTimeout(timer);
|
91
|
+
};
|
56
92
|
onMount(() => {
|
57
|
-
|
93
|
+
if (prefersReducedMotion()) {
|
94
|
+
if (transition)
|
95
|
+
transition.duration = 0;
|
96
|
+
}
|
97
|
+
const parentElement = base.parentElement;
|
58
98
|
if (parentElement) {
|
59
|
-
parentElement.addEventListener("contextmenu",
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
const scrollX = window.scrollX;
|
64
|
-
coordinates.x = e.clientX + scrollX;
|
65
|
-
coordinates.y = e.clientY + scrollY;
|
66
|
-
display = true;
|
67
|
-
await tick();
|
68
|
-
const offsetWidth = contextMenu.offsetWidth + 24;
|
69
|
-
const offsetHeight = contextMenu.offsetHeight + 6;
|
70
|
-
const innerWidth = window.innerWidth;
|
71
|
-
const innerHeight = window.innerHeight;
|
72
|
-
if (coordinates.x + offsetWidth > scrollX + innerWidth) {
|
73
|
-
coordinates.x = scrollX + innerWidth - offsetWidth;
|
74
|
-
}
|
75
|
-
if (coordinates.y + offsetHeight > scrollY + innerHeight) {
|
76
|
-
coordinates.y = scrollY + innerHeight - offsetHeight;
|
77
|
-
}
|
78
|
-
}
|
79
|
-
});
|
99
|
+
parentElement.addEventListener("contextmenu", displayMenu);
|
100
|
+
parentElement.addEventListener("touchstart", onTouchStart);
|
101
|
+
parentElement.addEventListener("touchend", onTouchEnd);
|
102
|
+
parentElement.addEventListener("touchcancel", onTouchEnd);
|
80
103
|
}
|
81
104
|
});
|
82
105
|
</script>
|
83
106
|
|
84
107
|
<svelte:body on:click={hide} on:keydown={onKeyDown} />
|
85
108
|
|
86
|
-
<
|
87
|
-
class={className}
|
88
|
-
{id}
|
89
|
-
bind:this={contextMenu}
|
90
|
-
style:z-index={display ? "10" : "-10"}
|
91
|
-
style:opacity={display ? "100%" : "0%"}
|
92
|
-
style:top="{coordinates.y}px"
|
93
|
-
style:left="{coordinates.x}px"
|
94
|
-
inert={display ? false : true}
|
95
|
-
>
|
96
|
-
<slot>Context Menu</slot>
|
97
|
-
</div>
|
109
|
+
<span bind:this={base} role="presentation"></span>
|
98
110
|
|
99
|
-
|
100
|
-
<div
|
101
|
-
|
111
|
+
{#if display}
|
112
|
+
<div
|
113
|
+
role="menu"
|
114
|
+
class={className}
|
115
|
+
{id}
|
116
|
+
bind:this={contextMenu}
|
117
|
+
style:top="{coordinates.y}px"
|
118
|
+
style:left="{coordinates.x}px"
|
119
|
+
transition:fade={transition ? transition : { duration: 0 }}
|
120
|
+
>
|
121
|
+
<slot>Context Menu</slot>
|
102
122
|
</div>
|
103
|
-
|
123
|
+
{/if}
|
104
124
|
|
105
125
|
<style>
|
126
|
+
span {
|
127
|
+
width: 0;
|
128
|
+
height: 0;
|
129
|
+
opacity: 0;
|
130
|
+
}
|
106
131
|
div {
|
107
132
|
position: absolute;
|
108
|
-
z-index:
|
133
|
+
z-index: 10;
|
109
134
|
}
|
110
135
|
</style>
|
@@ -1,10 +1,11 @@
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
2
|
+
import { type FadeParams } from "svelte/transition";
|
2
3
|
declare const __propDef: {
|
3
4
|
props: {
|
4
5
|
class?: string | undefined;
|
5
6
|
id?: string | undefined;
|
6
7
|
/** controls `display` css property */ display?: boolean | undefined;
|
7
|
-
/** `
|
8
|
+
/** fades the content, set to `false` to remove */ transition?: false | FadeParams | undefined;
|
8
9
|
};
|
9
10
|
events: {
|
10
11
|
[evt: string]: CustomEvent<any>;
|
@@ -19,14 +20,14 @@ export type ContextMenuSlots = typeof __propDef.slots;
|
|
19
20
|
/**
|
20
21
|
* ### ContextMenu
|
21
22
|
*
|
22
|
-
* Displays when the parent element is right clicked.
|
23
|
+
* Displays when the parent element is right clicked, or long pressed on mobile.
|
23
24
|
*
|
24
25
|
* @props
|
25
26
|
*
|
26
|
-
* - `classNoscript` - `noscript` class
|
27
27
|
* - `class`
|
28
28
|
* - `display` - controls `display` css property
|
29
29
|
* - `id`
|
30
|
+
* - `transition` - fades the content, set to `false` to remove
|
30
31
|
*
|
31
32
|
* @slots
|
32
33
|
*
|
@@ -43,12 +44,12 @@ export type ContextMenuSlots = typeof __propDef.slots;
|
|
43
44
|
*
|
44
45
|
* <div class="flex justify-center rounded border border-dashed p-12">
|
45
46
|
* <div>Right click here</div>
|
46
|
-
* <ContextMenu
|
47
|
-
* <div class="
|
47
|
+
* <ContextMenu>
|
48
|
+
* <div class="flex w-48 flex-col gap-2 rounded border bg-white p-2 shadow">
|
48
49
|
* <div class="font-bold">Context Menu</div>
|
49
|
-
* <button class="btn">Button</button>
|
50
|
-
* <button class="btn">Button</button>
|
51
|
-
* <button class="btn">Button</button>
|
50
|
+
* <button role="menuitem" class="btn">Button</button>
|
51
|
+
* <button role="menuitem" class="btn">Button</button>
|
52
|
+
* <button role="menuitem" class="btn">Button</button>
|
52
53
|
* </div>
|
53
54
|
* </ContextMenu>
|
54
55
|
* </div>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
### CopyButton
|
5
5
|
|
6
|
-
Uses the
|
6
|
+
Uses the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText) to copy text to the clipboard.
|
7
7
|
|
8
8
|
@props
|
9
9
|
|
@@ -34,10 +34,11 @@ Uses the navigator api to copy text to the clipboard.
|
|
34
34
|
-->
|
35
35
|
|
36
36
|
<script>import { onMount } from "svelte";
|
37
|
+
import { delay } from "../util/delay";
|
37
38
|
let className = "";
|
38
39
|
export { className as class };
|
39
40
|
export let id = "";
|
40
|
-
export let title = "
|
41
|
+
export let title = "";
|
41
42
|
export let text;
|
42
43
|
export let classNoscript = "";
|
43
44
|
let clientJs = false;
|
@@ -46,7 +47,7 @@ const copyText = async () => {
|
|
46
47
|
try {
|
47
48
|
await navigator.clipboard.writeText(text);
|
48
49
|
complete = true;
|
49
|
-
setTimeout(() => complete = false,
|
50
|
+
setTimeout(() => complete = false, delay);
|
50
51
|
} catch (error) {
|
51
52
|
console.error(error);
|
52
53
|
}
|
@@ -54,7 +55,14 @@ const copyText = async () => {
|
|
54
55
|
onMount(() => clientJs = true);
|
55
56
|
</script>
|
56
57
|
|
57
|
-
<button
|
58
|
+
<button
|
59
|
+
type="button"
|
60
|
+
disabled={!clientJs}
|
61
|
+
on:click={copyText}
|
62
|
+
class={className}
|
63
|
+
{id}
|
64
|
+
{title}
|
65
|
+
>
|
58
66
|
{#if complete}
|
59
67
|
<slot name="complete">Copied</slot>
|
60
68
|
{:else}
|
@@ -21,7 +21,7 @@ export type CopyButtonSlots = typeof __propDef.slots;
|
|
21
21
|
/**
|
22
22
|
* ### CopyButton
|
23
23
|
*
|
24
|
-
* Uses the
|
24
|
+
* Uses the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText) to copy text to the clipboard.
|
25
25
|
*
|
26
26
|
* @props
|
27
27
|
*
|
@@ -28,15 +28,18 @@ Data table to display an array of JS objects. Click a column header to sort.
|
|
28
28
|
- `data` - a list of objects to render in the table
|
29
29
|
- `idTable` - `table` id
|
30
30
|
- `id`
|
31
|
-
- `
|
32
|
-
- `sortBy` - column to sort by
|
31
|
+
- `maxRows` - maximum number of rows to show on each page, defaults to `0` - no pagination
|
32
|
+
- `sortBy` - column to sort by, defaults to first column
|
33
33
|
|
34
34
|
@slots
|
35
35
|
|
36
|
-
| name
|
37
|
-
|
|
38
|
-
| `
|
39
|
-
| `
|
36
|
+
| name | purpose | default value | slot props |
|
37
|
+
| ------------ | ------------------------ | -------------------------------- | ------------------------------ |
|
38
|
+
| `next` | next button contents | `Next` | `currentPage` |
|
39
|
+
| `pageNumber` | page numbers | `currentPage` / `numberOfPages` | `currentPage`, `numberOfPages` |
|
40
|
+
| `previous` | previous button contents | `Previous` | `currentPage` |
|
41
|
+
| `td` | td contents | `Previous` | `column`, `row` |
|
42
|
+
| `th` | th contents | `Previous` | `column` |
|
40
43
|
|
41
44
|
@example
|
42
45
|
|
@@ -46,6 +49,7 @@ Data table to display an array of JS objects. Click a column header to sort.
|
|
46
49
|
</script>
|
47
50
|
|
48
51
|
<DataTable
|
52
|
+
class="mb-12"
|
49
53
|
data={[
|
50
54
|
{ make: "Honda", model: "CR-V", year: 2011, awd: true },
|
51
55
|
{ make: "Volvo", model: "XC-40", year: 2024, awd: true },
|
@@ -57,15 +61,49 @@ Data table to display an array of JS objects. Click a column header to sort.
|
|
57
61
|
{ make: "GMC", model: "Acadia", year: 2008, awd: true },
|
58
62
|
{ make: "BMW", model: "X3", year: 2023, awd: true },
|
59
63
|
]}
|
60
|
-
|
61
|
-
|
64
|
+
/>
|
65
|
+
|
66
|
+
<DataTable
|
67
|
+
data={[
|
68
|
+
{ make: "Honda", model: "CR-V", year: 2011, awd: true },
|
69
|
+
{ make: "Volvo", model: "XC-40", year: 2024, awd: true },
|
70
|
+
{ make: "Ferrari", model: "458 Italia", year: 2015, awd: false },
|
71
|
+
{ make: "Chevrolet", model: "Silverado", year: 2022, awd: true },
|
72
|
+
{ make: "Ford", model: "Model A", year: 1931, awd: false },
|
73
|
+
{ make: "Subaru", model: "Outback", year: 2021, awd: true },
|
74
|
+
{ make: "Ford", model: "Bronco", year: 1970, awd: true },
|
75
|
+
{ make: "GMC", model: "Acadia", year: 2008, awd: true },
|
76
|
+
{ make: "BMW", model: "X3", year: 2023, awd: true },
|
77
|
+
]}
|
78
|
+
sortBy="year"
|
79
|
+
maxRows={4}
|
62
80
|
class="tabular-nums"
|
63
|
-
classTh="cursor-pointer
|
81
|
+
classTh="cursor-pointer"
|
64
82
|
classThSorted="underline"
|
65
|
-
classTbodyTr="transition hover:bg-
|
83
|
+
classTbodyTr="transition hover:bg-neutral-50"
|
66
84
|
classFooter="flex justify-between items-center"
|
67
85
|
classButton="btn"
|
68
|
-
|
86
|
+
>
|
87
|
+
<svelte:fragment slot="th" let:column>
|
88
|
+
{#if column === "awd"}
|
89
|
+
<span class="uppercase">{column}</span>
|
90
|
+
{:else}
|
91
|
+
{column}
|
92
|
+
{/if}
|
93
|
+
</svelte:fragment>
|
94
|
+
<svelte:fragment slot="td" let:column let:row>
|
95
|
+
{@const item = row[column]}
|
96
|
+
{#if typeof item === "boolean"}
|
97
|
+
{#if item}
|
98
|
+
Yes
|
99
|
+
{:else}
|
100
|
+
No
|
101
|
+
{/if}
|
102
|
+
{:else}
|
103
|
+
{item}
|
104
|
+
{/if}
|
105
|
+
</svelte:fragment>
|
106
|
+
</DataTable>
|
69
107
|
```
|
70
108
|
-->
|
71
109
|
|
@@ -97,12 +135,12 @@ export let classButton = "";
|
|
97
135
|
export let classFooter = "";
|
98
136
|
export let classPageNumber = "";
|
99
137
|
export let classPageControls = "";
|
100
|
-
export let
|
138
|
+
export let maxRows = 0;
|
101
139
|
export let currentPage = 1;
|
102
140
|
export let classNoscript = "";
|
103
141
|
let clientJs = false;
|
104
142
|
$:
|
105
|
-
numberOfPages = Math.floor(data.length /
|
143
|
+
numberOfPages = Math.floor(data.length / maxRows) + 1;
|
106
144
|
const sort = (column, toggleAscending = true) => {
|
107
145
|
if (column === sortBy && toggleAscending) {
|
108
146
|
ascending = !ascending;
|
@@ -150,7 +188,7 @@ onMount(() => clientJs = true);
|
|
150
188
|
class="{classTh} {sortBy === column ? classThSorted : ''}"
|
151
189
|
on:click={() => sort(column)}
|
152
190
|
>
|
153
|
-
{column}
|
191
|
+
<slot name="th" {column}>{column}</slot>
|
154
192
|
</th>
|
155
193
|
{/each}
|
156
194
|
</tr>
|
@@ -158,12 +196,12 @@ onMount(() => clientJs = true);
|
|
158
196
|
<tbody class={classTbody}>
|
159
197
|
{#each data as row, i}
|
160
198
|
{@const showRow =
|
161
|
-
i <
|
162
|
-
{#if
|
199
|
+
i < maxRows * currentPage && i >= maxRows * (currentPage - 1)}
|
200
|
+
{#if maxRows ? showRow : true}
|
163
201
|
<tr class={classTbodyTr}>
|
164
202
|
{#each columns as column}
|
165
203
|
<td class="{classTd} {sortBy === column ? classTdSorted : ''}">
|
166
|
-
{row[column]}
|
204
|
+
<slot name="td" {row} {column}>{row[column]}</slot>
|
167
205
|
</td>
|
168
206
|
{/each}
|
169
207
|
</tr>
|
@@ -172,23 +210,29 @@ onMount(() => clientJs = true);
|
|
172
210
|
</tbody>
|
173
211
|
</table>
|
174
212
|
|
175
|
-
{#if
|
213
|
+
{#if maxRows}
|
176
214
|
<div class={classFooter}>
|
177
|
-
<div class={classPageNumber}>
|
215
|
+
<div class={classPageNumber}>
|
216
|
+
<slot name="pageNumber" {currentPage} {numberOfPages}>
|
217
|
+
{currentPage} / {numberOfPages}
|
218
|
+
</slot>
|
219
|
+
</div>
|
178
220
|
<div class={classPageControls}>
|
179
221
|
<button
|
222
|
+
type="button"
|
180
223
|
class={classButton}
|
181
224
|
disabled={!clientJs || currentPage < 2}
|
182
225
|
on:click={() => currentPage--}
|
183
226
|
>
|
184
|
-
<slot name="previous">Previous</slot>
|
227
|
+
<slot name="previous" {currentPage}>Previous</slot>
|
185
228
|
</button>
|
186
229
|
<button
|
230
|
+
type="button"
|
187
231
|
class={classButton}
|
188
232
|
disabled={!clientJs || currentPage >= numberOfPages}
|
189
233
|
on:click={() => currentPage++}
|
190
234
|
>
|
191
|
-
<slot name="next">Next</slot>
|
235
|
+
<slot name="next" {currentPage}>Next</slot>
|
192
236
|
</button>
|
193
237
|
</div>
|
194
238
|
</div>
|