drab 3.0.0 → 3.0.2

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,116 +1,116 @@
1
- <!--
2
- @component
3
-
4
- ### DataTable
5
-
6
- Data table to display an array of objects. Works with zero configuration, just pass an array of objects into the `data` prop.
7
-
8
- - Set the `maxRows` prop to enable pagination, bind to `currentPage` to navigate (see second example below)
9
- - Provides sorting for `number`, `string`, `boolean`, and `Date`
10
- - Strings are sorted using `Intl.Collator`
11
- - Style or modify the rendering of data with slot props
12
- - `numberOfPages` is calculated and sent back through the `controls` slot
13
-
14
- @props
15
-
16
- - `ascending` - current sort order
17
- - `classTbodyTr` - `tbody tr` class
18
- - `classTbody` - `tbody` class
19
- - `classTd` - `td` class
20
- - `classTh` - `th` class
21
- - `classTheadTr` - `thead tr` class
22
- - `classThead` - `thead` class
23
- - `classTr` - `tr` class
24
- - `class`
25
- - `currentPage` - current page index, defaults to `0`
26
- - `data` - an array of items to render in the table
27
- - `id`
28
- - `keys` - table columns to include in the table, in order. Defaults to first item's keys
29
- - `maxRows` - maximum number of rows to show on each page, defaults to `0` - no pagination
30
- - `sortBy` - key (column) to sort by, defaults to first key
31
-
32
- @slots
33
-
34
- | name | purpose | default value | slot props |
35
- | ---------- | ---------------------------------------------------------------- | ------------- | ------------------------------- |
36
- | `controls` | helper that passes back `maxRows` and calculated `numberOfPages` | empty | `maxRows`, `numberOfPages` |
37
- | `td` | td contents | `value` | `item`, `key`, `sortBy` `value` |
38
- | `th` | th contents | `key` | `key`, `sortBy` |
39
-
40
- @example
41
-
42
- ```svelte
43
- <script lang="ts">
44
- import type { ComponentProps } from "svelte";
45
- import { DataTable } from "drab";
46
-
47
- let currentPage = 0;
48
-
49
- const data: ComponentProps<DataTable>["data"] = [
50
- { make: "Honda", model: "CR-V", year: 2011, awd: true },
51
- { make: "Volvo", model: "XC-40", year: 2024, awd: true },
52
- { make: "Ferrari", model: "458 Italia", year: 2015, awd: false },
53
- { make: "Chevrolet", model: "Silverado", year: 2022, awd: true },
54
- { make: "Ford", model: "Model A", year: 1931, awd: false },
55
- { make: "Subaru", model: "Outback", year: 2021, awd: true },
56
- { make: "Ford", model: "Bronco", year: 1970, awd: true },
57
- { make: "GMC", model: "Acadia", year: 2008, awd: true },
58
- { make: "BMW", model: "X3", year: 2023, awd: true },
59
- ];
60
- </script>
61
-
62
- <DataTable {data} class="mb-12" />
63
-
64
- <DataTable
65
- {data}
66
- bind:currentPage
67
- sortBy="year"
68
- maxRows={4}
69
- class="tabular-nums"
70
- classTh="cursor-pointer capitalize"
71
- classTbodyTr="transition hover:bg-neutral-50"
72
- >
73
- <svelte:fragment slot="th" let:key let:sortBy>
74
- <span class:uppercase={key === "awd"} class:underline={key === sortBy}>
75
- {key}
76
- </span>
77
- </svelte:fragment>
78
- <svelte:fragment slot="td" let:value>
79
- {#if typeof value === "boolean"}
80
- {value ? "Yes" : "No"}
81
- {:else}
82
- {value}
83
- {/if}
84
- </svelte:fragment>
85
- <svelte:fragment slot="controls" let:maxRows let:numberOfPages>
86
- {#if maxRows}
87
- <div class="flex items-center justify-between gap-4">
88
- <div class="min-w-fit">{currentPage + 1} / {numberOfPages}</div>
89
- <div class="flex gap-2">
90
- <button
91
- type="button"
92
- class="btn"
93
- disabled={currentPage < 1}
94
- on:click={() => currentPage--}
95
- >
96
- Previous
97
- </button>
98
- <button
99
- type="button"
100
- class="btn"
101
- disabled={currentPage >= numberOfPages - 1}
102
- on:click={() => currentPage++}
103
- >
104
- Next
105
- </button>
106
- </div>
107
- </div>
108
- {/if}
109
- </svelte:fragment>
110
- </DataTable>
111
- ```
112
- -->
113
-
1
+ <!--
2
+ @component
3
+
4
+ ### DataTable
5
+
6
+ Data table to display an array of objects. Works with zero configuration, just pass an array of objects into the `data` prop.
7
+
8
+ - Set the `maxRows` prop to enable pagination, bind to `currentPage` to navigate (see second example below)
9
+ - Provides sorting for `number`, `string`, `boolean`, and `Date`
10
+ - Strings are sorted using `Intl.Collator`
11
+ - Style or modify the rendering of data with slot props
12
+ - `numberOfPages` is calculated and sent back through the `controls` slot
13
+
14
+ @props
15
+
16
+ - `ascending` - current sort order
17
+ - `classTbodyTr` - `tbody tr` class
18
+ - `classTbody` - `tbody` class
19
+ - `classTd` - `td` class
20
+ - `classTh` - `th` class
21
+ - `classTheadTr` - `thead tr` class
22
+ - `classThead` - `thead` class
23
+ - `classTr` - `tr` class
24
+ - `class`
25
+ - `currentPage` - current page index, defaults to `0`
26
+ - `data` - an array of items to render in the table
27
+ - `id`
28
+ - `keys` - table columns to include in the table, in order. Defaults to first item's keys
29
+ - `maxRows` - maximum number of rows to show on each page, defaults to `0` - no pagination
30
+ - `sortBy` - key (column) to sort by, defaults to first key
31
+
32
+ @slots
33
+
34
+ | name | purpose | default value | slot props |
35
+ | ---------- | ---------------------------------------------------------------- | ------------- | ------------------------------- |
36
+ | `controls` | helper that passes back `maxRows` and calculated `numberOfPages` | empty | `maxRows`, `numberOfPages` |
37
+ | `td` | td contents | `value` | `item`, `key`, `sortBy` `value` |
38
+ | `th` | th contents | `key` | `key`, `sortBy` |
39
+
40
+ @example
41
+
42
+ ```svelte
43
+ <script lang="ts">
44
+ import type { ComponentProps } from "svelte";
45
+ import { DataTable } from "drab";
46
+
47
+ let currentPage = 0;
48
+
49
+ const data: ComponentProps<DataTable>["data"] = [
50
+ { make: "Honda", model: "CR-V", year: 2011, awd: true },
51
+ { make: "Volvo", model: "XC-40", year: 2024, awd: true },
52
+ { make: "Ferrari", model: "458 Italia", year: 2015, awd: false },
53
+ { make: "Chevrolet", model: "Silverado", year: 2022, awd: true },
54
+ { make: "Ford", model: "Model A", year: 1931, awd: false },
55
+ { make: "Subaru", model: "Outback", year: 2021, awd: true },
56
+ { make: "Ford", model: "Bronco", year: 1970, awd: true },
57
+ { make: "GMC", model: "Acadia", year: 2008, awd: true },
58
+ { make: "BMW", model: "X3", year: 2023, awd: true },
59
+ ];
60
+ </script>
61
+
62
+ <DataTable {data} class="mb-12" />
63
+
64
+ <DataTable
65
+ {data}
66
+ bind:currentPage
67
+ sortBy="year"
68
+ maxRows={4}
69
+ class="tabular-nums"
70
+ classTh="cursor-pointer capitalize"
71
+ classTbodyTr="transition hover:bg-neutral-50"
72
+ >
73
+ <svelte:fragment slot="th" let:key let:sortBy>
74
+ <span class:uppercase={key === "awd"} class:underline={key === sortBy}>
75
+ {key}
76
+ </span>
77
+ </svelte:fragment>
78
+ <svelte:fragment slot="td" let:value>
79
+ {#if typeof value === "boolean"}
80
+ {value ? "Yes" : "No"}
81
+ {:else}
82
+ {value}
83
+ {/if}
84
+ </svelte:fragment>
85
+ <svelte:fragment slot="controls" let:maxRows let:numberOfPages>
86
+ {#if maxRows}
87
+ <div class="flex items-center justify-between gap-4">
88
+ <div class="min-w-fit">{currentPage + 1} / {numberOfPages}</div>
89
+ <div class="flex gap-2">
90
+ <button
91
+ type="button"
92
+ class="btn"
93
+ disabled={currentPage < 1}
94
+ on:click={() => currentPage--}
95
+ >
96
+ Previous
97
+ </button>
98
+ <button
99
+ type="button"
100
+ class="btn"
101
+ disabled={currentPage >= numberOfPages - 1}
102
+ on:click={() => currentPage++}
103
+ >
104
+ Next
105
+ </button>
106
+ </div>
107
+ </div>
108
+ {/if}
109
+ </svelte:fragment>
110
+ </DataTable>
111
+ ```
112
+ -->
113
+
114
114
  <script>let className = "";
115
115
  export { className as class };
116
116
  export let id = "";
@@ -176,33 +176,33 @@ const showRow = (i, currentPage2) => {
176
176
  return overMin && underMax;
177
177
  };
178
178
  sort(sortBy, false);
179
- </script>
180
-
181
- <table class={className} {id}>
182
- <thead class={classThead}>
183
- <tr class="{classTr} {classTheadTr}">
184
- {#each keys as key}
185
- <th class={classTh} on:click={() => sort(key)}>
186
- <slot name="th" {key} {sortBy}>{key}</slot>
187
- </th>
188
- {/each}
189
- </tr>
190
- </thead>
191
- <tbody class={classTbody}>
192
- {#each data as item, i}
193
- {#if showRow(i, currentPage)}
194
- <tr class="{classTr} {classTbodyTr}">
195
- {#each keys as key}
196
- <td class={classTd}>
197
- <slot name="td" {item} {key} {sortBy} value={item[key]}>
198
- {item[key]}
199
- </slot>
200
- </td>
201
- {/each}
202
- </tr>
203
- {/if}
204
- {/each}
205
- </tbody>
206
- </table>
207
-
208
- <slot name="controls" {maxRows} {numberOfPages} />
179
+ </script>
180
+
181
+ <table class={className} {id}>
182
+ <thead class={classThead}>
183
+ <tr class="{classTr} {classTheadTr}">
184
+ {#each keys as key}
185
+ <th class={classTh} on:click={() => sort(key)}>
186
+ <slot name="th" {key} {sortBy}>{key}</slot>
187
+ </th>
188
+ {/each}
189
+ </tr>
190
+ </thead>
191
+ <tbody class={classTbody}>
192
+ {#each data as item, i}
193
+ {#if showRow(i, currentPage)}
194
+ <tr class="{classTr} {classTbodyTr}">
195
+ {#each keys as key}
196
+ <td class={classTd}>
197
+ <slot name="td" {item} {key} {sortBy} value={item[key]}>
198
+ {item[key]}
199
+ </slot>
200
+ </td>
201
+ {/each}
202
+ </tr>
203
+ {/if}
204
+ {/each}
205
+ </tbody>
206
+ </table>
207
+
208
+ <slot name="controls" {maxRows} {numberOfPages} />
@@ -1,50 +1,50 @@
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 | empty | `open` |
20
- | `content` | contents when details is `open` | empty | 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 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
-
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 | empty | `open` |
20
+ | `content` | contents when details is `open` | empty | 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 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
48
  <script>import { onMount } from "svelte";
49
49
  import { slide } from "svelte/transition";
50
50
  import { prefersReducedMotion } from "../util/accessibility";
@@ -63,41 +63,41 @@ onMount(() => {
63
63
  if (prefersReducedMotion())
64
64
  transition = false;
65
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>
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>
@@ -1,65 +1,65 @@
1
- <!--
2
- @component
3
-
4
- ### Editor
5
-
6
- `textarea` element with controls to add content and keyboard shortcuts. Compared to other WYSIWYG editors, the `valueTextarea` is just a `string`, so you can easily store it in a database or manipulate it without learning a separate API.
7
-
8
- - This component is used to create [Typo](https://typo.robino.dev)
9
-
10
- @props
11
-
12
- - `classButton` - `class` of all the `button` elements
13
- - `classControls` - `class` of the `div` that wraps the controls
14
- - `classTextarea` - `class` of the `textarea` element
15
- - `contentElements` - an array of `EditorContentElement`s for the controls
16
- - `idControls` - `id` of the `div` that wraps the controls
17
- - `idTextarea` - `id` of the `textarea` element
18
- - `keyPairs` - keys that will auto-close if typed, value is their closing character
19
- - `nameTextarea` - `name` of the `textarea` element
20
- - `placeholderTextarea` - `placeholder` of the `textarea` element
21
- - `selectionStartTextarea` - `selectionStart` value of the `textarea`
22
- - `valueTextarea` - `value` of the `textarea` element
23
-
24
- @example
25
-
26
- ```svelte
27
- <script lang="ts">
28
- import { Editor } from "drab";
29
- </script>
30
-
31
- <Editor
32
- classButton="btn"
33
- classControls="flex gap-2"
34
- classTextarea="border w-full h-36 p-2 rounded mb-2"
35
- placeholderTextarea="asterisk: ctrl+i, anchor: ctrl+["
36
- contentElements={[
37
- {
38
- title: "Bullet",
39
- text: "- ",
40
- display: "block",
41
- icon: "Bullet",
42
- },
43
- {
44
- title: "Italic",
45
- text: "*",
46
- display: "wrap",
47
- icon: "Italic",
48
- key: "i",
49
- class: "italic",
50
- },
51
- {
52
- title: "Anchor",
53
- text: "[text](href)",
54
- display: "inline",
55
- icon: "Anchor",
56
- key: "[",
57
- },
58
- ]}
59
- />
60
- ```
61
- -->
62
-
1
+ <!--
2
+ @component
3
+
4
+ ### Editor
5
+
6
+ `textarea` element with controls to add content and keyboard shortcuts. Compared to other WYSIWYG editors, the `valueTextarea` is just a `string`, so you can easily store it in a database or manipulate it without learning a separate API.
7
+
8
+ - This component is used to create [Typo](https://typo.robino.dev)
9
+
10
+ @props
11
+
12
+ - `classButton` - `class` of all the `button` elements
13
+ - `classControls` - `class` of the `div` that wraps the controls
14
+ - `classTextarea` - `class` of the `textarea` element
15
+ - `contentElements` - an array of `EditorContentElement`s for the controls
16
+ - `idControls` - `id` of the `div` that wraps the controls
17
+ - `idTextarea` - `id` of the `textarea` element
18
+ - `keyPairs` - keys that will auto-close if typed, value is their closing character
19
+ - `nameTextarea` - `name` of the `textarea` element
20
+ - `placeholderTextarea` - `placeholder` of the `textarea` element
21
+ - `selectionStartTextarea` - `selectionStart` value of the `textarea`
22
+ - `valueTextarea` - `value` of the `textarea` element
23
+
24
+ @example
25
+
26
+ ```svelte
27
+ <script lang="ts">
28
+ import { Editor } from "drab";
29
+ </script>
30
+
31
+ <Editor
32
+ classButton="btn"
33
+ classControls="flex gap-2"
34
+ classTextarea="border w-full h-36 p-2 rounded mb-2"
35
+ placeholderTextarea="asterisk: ctrl+i, anchor: ctrl+["
36
+ contentElements={[
37
+ {
38
+ title: "Bullet",
39
+ text: "- ",
40
+ display: "block",
41
+ icon: "Bullet",
42
+ },
43
+ {
44
+ title: "Italic",
45
+ text: "*",
46
+ display: "wrap",
47
+ icon: "Italic",
48
+ key: "i",
49
+ class: "italic",
50
+ },
51
+ {
52
+ title: "Anchor",
53
+ text: "[text](href)",
54
+ display: "inline",
55
+ icon: "Anchor",
56
+ key: "[",
57
+ },
58
+ ]}
59
+ />
60
+ ```
61
+ -->
62
+
63
63
  <script>import { onMount } from "svelte";
64
64
  export let contentElements = [];
65
65
  export let valueTextarea = "";
@@ -338,39 +338,39 @@ const correctFollowing = (currentLineNumber, decrement = false) => {
338
338
  valueTextarea = lines.join("\n");
339
339
  };
340
340
  onMount(() => clientJs = true);
341
- </script>
342
-
343
- <textarea
344
- id={idTextarea}
345
- class={classTextarea}
346
- name={nameTextarea}
347
- placeholder={placeholderTextarea}
348
- on:keydown={onKeyDown}
349
- on:keyup={updateSelectionStart}
350
- on:dblclick={trimSelection}
351
- bind:value={valueTextarea}
352
- bind:this={textArea}
353
- on:click={() => {
354
- openChars = [];
355
- updateSelectionStart();
356
- }}
357
- on:input
358
- />
359
-
360
- <div id={idControls} class={classControls}>
361
- {#each contentElements as el}
362
- <button
363
- type="button"
364
- class={el.class ? `${classButton} ${el.class}` : classButton}
365
- on:click={() => addContent(el)}
366
- title={el.title}
367
- disabled={!clientJs}
368
- >
369
- {#if typeof el.icon !== "string"}
370
- <svelte:component this={el.icon} />
371
- {:else}
372
- {el.icon}
373
- {/if}
374
- </button>
375
- {/each}
376
- </div>
341
+ </script>
342
+
343
+ <textarea
344
+ id={idTextarea}
345
+ class={classTextarea}
346
+ name={nameTextarea}
347
+ placeholder={placeholderTextarea}
348
+ on:keydown={onKeyDown}
349
+ on:keyup={updateSelectionStart}
350
+ on:dblclick={trimSelection}
351
+ bind:value={valueTextarea}
352
+ bind:this={textArea}
353
+ on:click={() => {
354
+ openChars = [];
355
+ updateSelectionStart();
356
+ }}
357
+ on:input
358
+ />
359
+
360
+ <div id={idControls} class={classControls}>
361
+ {#each contentElements as el}
362
+ <button
363
+ type="button"
364
+ class={el.class ? `${classButton} ${el.class}` : classButton}
365
+ on:click={() => addContent(el)}
366
+ title={el.title}
367
+ disabled={!clientJs}
368
+ >
369
+ {#if typeof el.icon !== "string"}
370
+ <svelte:component this={el.icon} />
371
+ {:else}
372
+ {el.icon}
373
+ {/if}
374
+ </button>
375
+ {/each}
376
+ </div>