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.
@@ -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 py-4 font-bold hover:underline"
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="content" let:item let:index>
57
- <div class="mb-4">
58
- <span>{index + 1}.</span>
59
- <span>{item.content}</span>
60
- </div>
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 py-4 font-bold hover:underline"
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="content" let:item let:index>
103
- * <div class="mb-4">
104
- * <span>{index + 1}.</span>
105
- * <span>{item.content}</span>
106
- * </div>
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#using-custom-breakpoints).
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#using-custom-breakpoints).
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
  *
@@ -22,7 +22,7 @@ Generates a guitar chord `svg`.
22
22
  </script>
23
23
 
24
24
  <Chord
25
- class="text-gray-950"
25
+ class="text-neutral-950"
26
26
  name="D"
27
27
  notes={[
28
28
  {
@@ -46,7 +46,7 @@ export type ChordSlots = typeof __propDef.slots;
46
46
  * </script>
47
47
  *
48
48
  * <Chord
49
- * class="text-gray-950"
49
+ * class="text-neutral-950"
50
50
  * name="D"
51
51
  * notes={[
52
52
  * {
@@ -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 class="transition">
31
- <div class="card flex w-48 flex-col gap-2">
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 classNoscript = "";
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
- const parentElement = contextMenu.parentElement;
93
+ if (prefersReducedMotion()) {
94
+ if (transition)
95
+ transition.duration = 0;
96
+ }
97
+ const parentElement = base.parentElement;
58
98
  if (parentElement) {
59
- parentElement.addEventListener("contextmenu", async (e) => {
60
- if (contextMenu) {
61
- e.preventDefault();
62
- const scrollY = window.scrollY;
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
- <div
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
- <noscript>
100
- <div class={classNoscript}>
101
- <slot />
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
- </noscript>
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: 1;
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
- /** `noscript` class */ classNoscript?: string | undefined;
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 class="transition">
47
- * <div class="card flex w-48 flex-col gap-2">
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 navigator api to copy text to the clipboard.
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 = "Copy";
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, 800);
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 disabled={!clientJs} on:click={copyText} class={className} {id} {title}>
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 navigator api to copy text to the clipboard.
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
- - `paginate` - number of rows to show on each page, defaults to `0` - no pagination
32
- - `sortBy` - column to sort by--defaults to first column
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 | purpose | default value |
37
- | ---------- | ------------------------ | ------------- |
38
- | `previous` | previous button contents | `Previous` |
39
- | `next` | next button contents | `Next` |
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
- sortBy="make"
61
- paginate={4}
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 uppercase"
81
+ classTh="cursor-pointer"
64
82
  classThSorted="underline"
65
- classTbodyTr="transition hover:bg-gray-50"
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 paginate = 0;
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 / paginate) + 1;
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 < paginate * currentPage && i >= paginate * (currentPage - 1)}
162
- {#if paginate ? showRow : true}
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 paginate}
213
+ {#if maxRows}
176
214
  <div class={classFooter}>
177
- <div class={classPageNumber}>{currentPage} / {numberOfPages}</div>
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>