drab 2.8.3 → 2.8.5

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.
@@ -1,108 +1,117 @@
1
- <!--
2
- @component
3
-
4
- ### DataTable
5
-
6
- Data table to display an array of JS objects. Provides sorting and pagination. Set the `maxRows` prop to enable pagination. Data can be styled conditionally with slot props.
7
-
8
- @props
1
+ <!--
2
+ @component
3
+
4
+ ### DataTable
5
+
6
+ Data table to display an array of JS objects. Provides pagination and sorting for `number`, `string`, `boolean`, and `Date`. Set the `maxRows` prop to enable pagination. Data can be styled conditionally with slot props.
7
+
8
+ @props
9
9
 
10
10
  - `ascending` - current sort order
11
- - `classButton` - `button` class
12
- - `classFooter` - footer class
13
- - `classNoscript` - `noscript` class
14
- - `classPageControls` - class of `div` that wraps the "Previous" and "Next" buttons
15
- - `classTable` - `table` class
16
11
  - `classTbodyTr` - `tbody tr` class
17
12
  - `classTbody` - `tbody` class
18
- - `classTdSorted` - currently sorted `td`
19
13
  - `classTd` - `td` class
20
- - `classThSorted` - currently sorted `th`
21
14
  - `classTh` - `th` class
22
15
  - `classTheadTr` - `thead tr` class
23
16
  - `classThead` - `thead` class
24
17
  - `classTr` - `tr` class
25
18
  - `class`
26
- - `currentPage` - current page, defaults to `1`
27
- - `data` - a list of objects to render in the table
28
- - `idTable` - `table` id
19
+ - `currentPage` - current page index, defaults to `0`
20
+ - `data` - an array of items to render in the table
29
21
  - `id`
30
- - `keys` - table columns to include in the table, in order
22
+ - `keys` - table columns to include in the table, in order. Defaults to first item's keys
31
23
  - `maxRows` - maximum number of rows to show on each page, defaults to `0` - no pagination
32
24
  - `sortBy` - key (column) to sort by, defaults to first key
33
25
 
34
- @slots
35
-
36
- | name | purpose | default value | slot props |
37
- | ------------ | ------------------------ | ------------------------------- | ------------------------------- |
38
- | `next` | next button contents | `Next` | |
39
- | `pageNumber` | page numbers | `currentPage` / `numberOfPages` | `currentPage`, `numberOfPages` |
40
- | `previous` | previous button contents | `Previous` | |
41
- | `td` | td contents | `item[key]` | `item`, `key`, `sortBy` `value` |
42
- | `th` | th contents | `key` | `key`, `sortBy` |
43
-
44
- @example
26
+ @slots
27
+
28
+ | name | purpose | default value | slot props |
29
+ | ---------- | ----------------------------------------------------- | ------------- | ------------------------------- |
30
+ | `controls` | helper that passes back `maxRows` and `numberOfPages` | none | `maxRows`, `numberOfPages` |
31
+ | `td` | td contents | `value` | `item`, `key`, `sortBy` `value` |
32
+ | `th` | th contents | `key` | `key`, `sortBy` |
33
+
34
+ @example
45
35
 
46
36
  ```svelte
47
- <script lang="ts">
48
- import { DataTable, type DataTableItem } from "drab";
49
-
50
- const data: DataTableItem[] = [
51
- { make: "Honda", model: "CR-V", year: 2011, awd: true },
52
- { make: "Volvo", model: "XC-40", year: 2024, awd: true },
53
- { make: "Ferrari", model: "458 Italia", year: 2015, awd: false },
54
- { make: "Chevrolet", model: "Silverado", year: 2022, awd: true },
55
- { make: "Ford", model: "Model A", year: 1931, awd: false },
56
- { make: "Subaru", model: "Outback", year: 2021, awd: true },
57
- { make: "Ford", model: "Bronco", year: 1970, awd: true },
58
- { make: "GMC", model: "Acadia", year: 2008, awd: true },
59
- { make: "BMW", model: "X3", year: 2023, awd: true },
60
- ];
61
- </script>
62
-
63
- <DataTable {data} class="mb-12" />
64
-
65
- <DataTable
66
- {data}
67
- sortBy="year"
68
- maxRows={4}
69
- class="tabular-nums"
70
- classTh="cursor-pointer capitalize"
71
- classThSorted="underline"
72
- classTbodyTr="transition hover:bg-neutral-50"
73
- classFooter="flex justify-between items-center"
74
- classButton="btn"
75
- >
76
- <svelte:fragment slot="th" let:key>
77
- <span class:uppercase={key === "awd"}>{key}</span>
78
- </svelte:fragment>
79
- <svelte:fragment slot="td" let:value>
80
- {#if typeof value === "boolean"}
81
- {value ? "Yes" : "No"}
82
- {:else}
83
- {value}
84
- {/if}
85
- </svelte:fragment>
86
- </DataTable>
37
+ <script lang="ts">
38
+ import type { ComponentProps } from "svelte";
39
+ import { DataTable } from "drab";
40
+
41
+ let currentPage = 0;
42
+
43
+ const data: ComponentProps<DataTable>["data"] = [
44
+ { make: "Honda", model: "CR-V", year: 2011, awd: true },
45
+ { make: "Volvo", model: "XC-40", year: 2024, awd: true },
46
+ { make: "Ferrari", model: "458 Italia", year: 2015, awd: false },
47
+ { make: "Chevrolet", model: "Silverado", year: 2022, awd: true },
48
+ { make: "Ford", model: "Model A", year: 1931, awd: false },
49
+ { make: "Subaru", model: "Outback", year: 2021, awd: true },
50
+ { make: "Ford", model: "Bronco", year: 1970, awd: true },
51
+ { make: "GMC", model: "Acadia", year: 2008, awd: true },
52
+ { make: "BMW", model: "X3", year: 2023, awd: true },
53
+ ];
54
+ </script>
55
+
56
+ <DataTable {data} class="mb-12" />
57
+
58
+ <DataTable
59
+ {data}
60
+ bind:currentPage
61
+ sortBy="year"
62
+ maxRows={4}
63
+ class="tabular-nums"
64
+ classTh="cursor-pointer capitalize"
65
+ classTbodyTr="transition hover:bg-neutral-50"
66
+ >
67
+ <svelte:fragment slot="th" let:key let:sortBy>
68
+ <span class:uppercase={key === "awd"} class:underline={key === sortBy}>
69
+ {key}
70
+ </span>
71
+ </svelte:fragment>
72
+ <svelte:fragment slot="td" let:value>
73
+ {#if typeof value === "boolean"}
74
+ {value ? "Yes" : "No"}
75
+ {:else}
76
+ {value}
77
+ {/if}
78
+ </svelte:fragment>
79
+ <svelte:fragment slot="controls" let:maxRows let:numberOfPages>
80
+ {#if maxRows}
81
+ <div class="flex items-center justify-between">
82
+ <div>{currentPage + 1} / {numberOfPages}</div>
83
+ <div>
84
+ <button
85
+ type="button"
86
+ class="btn"
87
+ disabled={currentPage < 1}
88
+ on:click={() => currentPage--}
89
+ >
90
+ Previous
91
+ </button>
92
+ <button
93
+ type="button"
94
+ class="btn"
95
+ disabled={currentPage >= numberOfPages - 1}
96
+ on:click={() => currentPage++}
97
+ >
98
+ Next
99
+ </button>
100
+ </div>
101
+ </div>
102
+ {/if}
103
+ </svelte:fragment>
104
+ </DataTable>
87
105
  ```
88
- -->
89
-
90
- <script context="module"></script>
91
-
92
- <script>import { onMount } from "svelte";
93
- import { messageNoScript } from "../util/messages";
94
- let className = "";
106
+ -->
107
+
108
+ <script>let className = "";
95
109
  export { className as class };
96
110
  export let id = "";
97
111
  export let data;
98
- export let keys = [];
99
- if (!keys.length && data[0]) {
100
- keys = Object.keys(data[0]);
101
- }
112
+ export let keys = Object.keys(data[0]);
102
113
  export let sortBy = keys[0];
103
114
  export let ascending = true;
104
- export let classTable = "";
105
- export let idTable = "";
106
115
  export let classThead = "";
107
116
  export let classTbody = "";
108
117
  export let classTr = "";
@@ -110,15 +119,8 @@ export let classTheadTr = "";
110
119
  export let classTbodyTr = "";
111
120
  export let classTh = "";
112
121
  export let classTd = "";
113
- export let classThSorted = "";
114
- export let classTdSorted = "";
115
- export let classButton = "";
116
- export let classFooter = "";
117
- export let classPageControls = "";
118
122
  export let maxRows = 0;
119
- export let currentPage = 1;
120
- export let classNoscript = "";
121
- let clientJs = false;
123
+ export let currentPage = 0;
122
124
  $:
123
125
  numberOfPages = Math.floor(data.length / maxRows) + 1;
124
126
  const sort = (key, toggleAscending = true) => {
@@ -143,14 +145,19 @@ const sort = (key, toggleAscending = true) => {
143
145
  } else {
144
146
  return collator.compare(bVal, aVal);
145
147
  }
146
- } else if (typeof aVal === "boolean") {
148
+ } else if (aVal instanceof Date && bVal instanceof Date) {
149
+ if (ascending) {
150
+ return aVal.getTime() - bVal.getTime();
151
+ } else {
152
+ return bVal.getTime() - aVal.getTime();
153
+ }
154
+ } else {
147
155
  if (ascending) {
148
156
  return aVal === bVal ? 0 : aVal ? -1 : 1;
149
157
  } else {
150
158
  return aVal === bVal ? 0 : aVal ? 1 : -1;
151
159
  }
152
- } else
153
- return 0;
160
+ }
154
161
  });
155
162
  data = data;
156
163
  sortBy = key;
@@ -158,70 +165,38 @@ const sort = (key, toggleAscending = true) => {
158
165
  const showRow = (i, currentPage2) => {
159
166
  if (!maxRows)
160
167
  return true;
161
- const overMin = i >= maxRows * (currentPage2 - 1);
162
- const underMax = i < maxRows * currentPage2;
168
+ const overMin = i >= maxRows * currentPage2;
169
+ const underMax = i < maxRows * (currentPage2 + 1);
163
170
  return overMin && underMax;
164
171
  };
165
172
  sort(sortBy, false);
166
- onMount(() => clientJs = true);
167
- </script>
168
-
169
- <div class={className} {id}>
170
- <table class={classTable} id={idTable}>
171
- <thead class={classThead}>
172
- <tr class="{classTr} {classTheadTr}">
173
- {#each keys as key}
174
- <th
175
- class="{classTh} {sortBy === key ? classThSorted : ''}"
176
- on:click={() => sort(key)}
177
- >
178
- <slot name="th" {key} {sortBy}>{key}</slot>
179
- </th>
180
- {/each}
181
- </tr>
182
- </thead>
183
- <tbody class={classTbody}>
184
- {#each data as item, i}
185
- {#if showRow(i, currentPage)}
186
- <tr class="{classTr} {classTbodyTr}">
187
- {#each keys as key}
188
- <td class="{classTd} {sortBy === key ? classTdSorted : ''}">
189
- <slot name="td" {item} {key} {sortBy} value={item[key]}>
190
- {item[key]}
191
- </slot>
192
- </td>
193
- {/each}
194
- </tr>
195
- {/if}
196
- {/each}
197
- </tbody>
198
- </table>
199
-
200
- {#if maxRows}
201
- <div class={classFooter}>
202
- <slot name="pageNumber" {currentPage} {numberOfPages}>
203
- <div>{currentPage} / {numberOfPages}</div>
204
- </slot>
205
- <div class={classPageControls}>
206
- <button
207
- type="button"
208
- class={classButton}
209
- disabled={!clientJs || currentPage < 2}
210
- on:click={() => currentPage--}
211
- >
212
- <slot name="previous">Previous</slot>
213
- </button>
214
- <button
215
- type="button"
216
- class={classButton}
217
- disabled={!clientJs || currentPage >= numberOfPages}
218
- on:click={() => currentPage++}
219
- >
220
- <slot name="next">Next</slot>
221
- </button>
222
- </div>
223
- </div>
224
-
225
- <noscript><div class={classNoscript}>{messageNoScript}</div></noscript>
226
- {/if}
227
- </div>
173
+ </script>
174
+
175
+ <table class={className} {id}>
176
+ <thead class={classThead}>
177
+ <tr class="{classTr} {classTheadTr}">
178
+ {#each keys as key}
179
+ <th class={classTh} on:click={() => sort(key)}>
180
+ <slot name="th" {key} {sortBy}>{key}</slot>
181
+ </th>
182
+ {/each}
183
+ </tr>
184
+ </thead>
185
+ <tbody class={classTbody}>
186
+ {#each data as item, i}
187
+ {#if showRow(i, currentPage)}
188
+ <tr class="{classTr} {classTbodyTr}">
189
+ {#each keys as key}
190
+ <td class={classTd}>
191
+ <slot name="td" {item} {key} {sortBy} value={item[key]}>
192
+ {item[key]}
193
+ </slot>
194
+ </td>
195
+ {/each}
196
+ </tr>
197
+ {/if}
198
+ {/each}
199
+ </tbody>
200
+ </table>
201
+
202
+ <slot name="controls" {maxRows} {numberOfPages} />
@@ -1,15 +1,12 @@
1
1
  import { SvelteComponent } from "svelte";
2
- export type DataTableItem = Record<string | number, string | number | boolean>;
3
2
  declare const __propDef: {
4
3
  props: {
5
4
  class?: string | undefined;
6
5
  id?: string | undefined;
7
- /** a list of objects to render in the table */ data: DataTableItem[];
8
- /** table columns to include in the table, in order */ keys?: string[] | undefined;
6
+ /** an array of items to render in the table */ data: Record<string | number, string | number | boolean | Date | null | undefined>[];
7
+ /** table columns to include in the table, in order. Defaults to first item's keys */ keys?: string[] | undefined;
9
8
  /** key (column) to sort by, defaults to first key */ sortBy?: string | undefined;
10
9
  /** current sort order */ ascending?: boolean | undefined;
11
- /** `table` class */ classTable?: string | undefined;
12
- /** `table` id */ idTable?: string | undefined;
13
10
  /** `thead` class */ classThead?: string | undefined;
14
11
  /** `tbody` class */ classTbody?: string | undefined;
15
12
  /** `tr` class */ classTr?: string | undefined;
@@ -17,14 +14,8 @@ declare const __propDef: {
17
14
  /** `tbody tr` class */ classTbodyTr?: string | undefined;
18
15
  /** `th` class */ classTh?: string | undefined;
19
16
  /** `td` class */ classTd?: string | undefined;
20
- /** currently sorted `th` */ classThSorted?: string | undefined;
21
- /** currently sorted `td` */ classTdSorted?: string | undefined;
22
- /** `button` class */ classButton?: string | undefined;
23
- /** footer class */ classFooter?: string | undefined;
24
- /** class of `div` that wraps the "Previous" and "Next" buttons */ classPageControls?: string | undefined;
25
17
  /** maximum number of rows to show on each page, defaults to `0` - no pagination */ maxRows?: number | undefined;
26
- /** current page, defaults to `1` */ currentPage?: number | undefined;
27
- /** `noscript` class */ classNoscript?: string | undefined;
18
+ /** current page index, defaults to `0` */ currentPage?: number | undefined;
28
19
  };
29
20
  events: {
30
21
  [evt: string]: CustomEvent<any>;
@@ -35,17 +26,15 @@ declare const __propDef: {
35
26
  sortBy: string;
36
27
  };
37
28
  td: {
38
- item: DataTableItem;
29
+ item: Record<string | number, string | number | boolean | Date | null | undefined>;
39
30
  key: string;
40
31
  sortBy: string;
41
- value: string | number | boolean;
32
+ value: string | number | boolean | Date | null | undefined;
42
33
  };
43
- pageNumber: {
44
- currentPage: number;
34
+ controls: {
35
+ maxRows: number;
45
36
  numberOfPages: number;
46
37
  };
47
- previous: {};
48
- next: {};
49
38
  };
50
39
  };
51
40
  export type DataTableProps = typeof __propDef.props;
@@ -54,51 +43,44 @@ export type DataTableSlots = typeof __propDef.slots;
54
43
  /**
55
44
  * ### DataTable
56
45
  *
57
- * Data table to display an array of JS objects. Provides sorting and pagination. Set the `maxRows` prop to enable pagination. Data can be styled conditionally with slot props.
46
+ * Data table to display an array of JS objects. Provides pagination and sorting for `number`, `string`, `boolean`, and `Date`. Set the `maxRows` prop to enable pagination. Data can be styled conditionally with slot props.
58
47
  *
59
48
  * @props
60
49
  *
61
50
  * - `ascending` - current sort order
62
- * - `classButton` - `button` class
63
- * - `classFooter` - footer class
64
- * - `classNoscript` - `noscript` class
65
- * - `classPageControls` - class of `div` that wraps the "Previous" and "Next" buttons
66
- * - `classTable` - `table` class
67
51
  * - `classTbodyTr` - `tbody tr` class
68
52
  * - `classTbody` - `tbody` class
69
- * - `classTdSorted` - currently sorted `td`
70
53
  * - `classTd` - `td` class
71
- * - `classThSorted` - currently sorted `th`
72
54
  * - `classTh` - `th` class
73
55
  * - `classTheadTr` - `thead tr` class
74
56
  * - `classThead` - `thead` class
75
57
  * - `classTr` - `tr` class
76
58
  * - `class`
77
- * - `currentPage` - current page, defaults to `1`
78
- * - `data` - a list of objects to render in the table
79
- * - `idTable` - `table` id
59
+ * - `currentPage` - current page index, defaults to `0`
60
+ * - `data` - an array of items to render in the table
80
61
  * - `id`
81
- * - `keys` - table columns to include in the table, in order
62
+ * - `keys` - table columns to include in the table, in order. Defaults to first item's keys
82
63
  * - `maxRows` - maximum number of rows to show on each page, defaults to `0` - no pagination
83
64
  * - `sortBy` - key (column) to sort by, defaults to first key
84
65
  *
85
66
  * @slots
86
67
  *
87
- * | name | purpose | default value | slot props |
88
- * | ------------ | ------------------------ | ------------------------------- | ------------------------------- |
89
- * | `next` | next button contents | `Next` | |
90
- * | `pageNumber` | page numbers | `currentPage` / `numberOfPages` | `currentPage`, `numberOfPages` |
91
- * | `previous` | previous button contents | `Previous` | |
92
- * | `td` | td contents | `item[key]` | `item`, `key`, `sortBy` `value` |
93
- * | `th` | th contents | `key` | `key`, `sortBy` |
68
+ * | name | purpose | default value | slot props |
69
+ * | ---------- | ----------------------------------------------------- | ------------- | ------------------------------- |
70
+ * | `controls` | helper that passes back `maxRows` and `numberOfPages` | none | `maxRows`, `numberOfPages` |
71
+ * | `td` | td contents | `value` | `item`, `key`, `sortBy` `value` |
72
+ * | `th` | th contents | `key` | `key`, `sortBy` |
94
73
  *
95
74
  * @example
96
75
  *
97
76
  * ```svelte
98
77
  * <script lang="ts">
99
- * import { DataTable, type DataTableItem } from "drab";
78
+ * import type { ComponentProps } from "svelte";
79
+ * import { DataTable } from "drab";
100
80
  *
101
- * const data: DataTableItem[] = [
81
+ * let currentPage = 0;
82
+ *
83
+ * const data: ComponentProps<DataTable>["data"] = [
102
84
  * { make: "Honda", model: "CR-V", year: 2011, awd: true },
103
85
  * { make: "Volvo", model: "XC-40", year: 2024, awd: true },
104
86
  * { make: "Ferrari", model: "458 Italia", year: 2015, awd: false },
@@ -115,17 +97,17 @@ export type DataTableSlots = typeof __propDef.slots;
115
97
  *
116
98
  * <DataTable
117
99
  * {data}
100
+ * bind:currentPage
118
101
  * sortBy="year"
119
102
  * maxRows={4}
120
103
  * class="tabular-nums"
121
104
  * classTh="cursor-pointer capitalize"
122
- * classThSorted="underline"
123
105
  * classTbodyTr="transition hover:bg-neutral-50"
124
- * classFooter="flex justify-between items-center"
125
- * classButton="btn"
126
106
  * >
127
- * <svelte:fragment slot="th" let:key>
128
- * <span class:uppercase={key === "awd"}>{key}</span>
107
+ * <svelte:fragment slot="th" let:key let:sortBy>
108
+ * <span class:uppercase={key === "awd"} class:underline={key === sortBy}>
109
+ * {key}
110
+ * </span>
129
111
  * </svelte:fragment>
130
112
  * <svelte:fragment slot="td" let:value>
131
113
  * {#if typeof value === "boolean"}
@@ -134,6 +116,31 @@ export type DataTableSlots = typeof __propDef.slots;
134
116
  * {value}
135
117
  * {/if}
136
118
  * </svelte:fragment>
119
+ * <svelte:fragment slot="controls" let:maxRows let:numberOfPages>
120
+ * {#if maxRows}
121
+ * <div class="flex items-center justify-between">
122
+ * <div>{currentPage + 1} / {numberOfPages}</div>
123
+ * <div>
124
+ * <button
125
+ * type="button"
126
+ * class="btn"
127
+ * disabled={currentPage < 1}
128
+ * on:click={() => currentPage--}
129
+ * >
130
+ * Previous
131
+ * </button>
132
+ * <button
133
+ * type="button"
134
+ * class="btn"
135
+ * disabled={currentPage >= numberOfPages - 1}
136
+ * on:click={() => currentPage++}
137
+ * >
138
+ * Next
139
+ * </button>
140
+ * </div>
141
+ * </div>
142
+ * {/if}
143
+ * </svelte:fragment>
137
144
  * </DataTable>
138
145
  * ```
139
146
  */
@@ -0,0 +1,103 @@
1
+ <!--
2
+ @component
3
+
4
+ ### Details
5
+
6
+ Displays a `details` element with helpful defaults and transitions. Can be used to make an accordion, or a collapse.
7
+
8
+ @props
9
+
10
+ - `class`
11
+ - `id`
12
+ - `open`
13
+ - `transition` - slides the content, set to `false` to remove
14
+
15
+ @slots
16
+
17
+ | name | purpose | default value | slot props |
18
+ | --------- | ------------------------------- | -------------- | ---------- |
19
+ | `summary` | `summary` element contents | none | `open` |
20
+ | `content` | contents when details is `open` | none | none |
21
+
22
+ @example
23
+
24
+ ```svelte
25
+ <script lang="ts">
26
+ import { Details } from "drab";
27
+ import Chevron from "../../site/svg/Chevron.svelte";
28
+ </script>
29
+
30
+ <Details class="border-b">
31
+ <svelte:fragment slot="summary" let:open>
32
+ <div
33
+ class="flex cursor-pointer items-center justify-between gap-8 p-4 font-bold underline hover:decoration-dotted"
34
+ >
35
+ <div>Does it work without JavaScript?</div>
36
+ <div class="transition" class:rotate-180={open}>
37
+ <Chevron />
38
+ </div>
39
+ </div>
40
+ </svelte:fragment>
41
+ <svelte:fragment slot="content">
42
+ <div class="px-4 pb-4">Yes.</div>
43
+ </svelte:fragment>
44
+ </Details>
45
+ ```
46
+ -->
47
+
48
+ <script>import { onMount } from "svelte";
49
+ import { slide } from "svelte/transition";
50
+ import { prefersReducedMotion } from "../util/accessibility";
51
+ import { duration } from "../util/transition";
52
+ let className = "";
53
+ export { className as class };
54
+ export let id = "";
55
+ export let transition = { duration };
56
+ export let open = false;
57
+ let clientJs = false;
58
+ const toggleOpen = () => {
59
+ open = !open;
60
+ };
61
+ onMount(() => {
62
+ clientJs = true;
63
+ if (prefersReducedMotion())
64
+ transition = false;
65
+ });
66
+ </script>
67
+
68
+ <div class={className} {id}>
69
+ <details bind:open>
70
+ <!-- svelte-ignore a11y-no-redundant-roles -->
71
+ <summary
72
+ role="button"
73
+ tabindex="0"
74
+ on:click|preventDefault={toggleOpen}
75
+ on:keydown={(e) => {
76
+ if (e.key === "Enter") {
77
+ e.preventDefault();
78
+ toggleOpen();
79
+ }
80
+ }}
81
+ >
82
+ <slot name="summary" {open} />
83
+ </summary>
84
+ {#if !clientJs || !transition}
85
+ <slot name="content" />
86
+ {/if}
87
+ </details>
88
+ {#if clientJs && open && transition}
89
+ <!-- outside the details for the transition -->
90
+ <div transition:slide={transition}>
91
+ <slot name="content" />
92
+ </div>
93
+ {/if}
94
+ </div>
95
+
96
+ <style>
97
+ summary {
98
+ list-style: none;
99
+ }
100
+ summary::-webkit-details-marker {
101
+ display: none;
102
+ }
103
+ </style>