drab 2.8.0 → 2.8.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.
package/README.md CHANGED
@@ -5,5 +5,3 @@
5
5
  [drab.robino.dev](https://drab.robino.dev)
6
6
 
7
7
  ### MIT License
8
-
9
- ### Open to contributions
@@ -3,7 +3,7 @@
3
3
 
4
4
  ### Accordion
5
5
 
6
- Displays a list of `details` elements with helpful defaults and transitions.
6
+ Displays a list of `details` elements with helpful defaults and transitions. Use `AccordionItem.data` to send any additional data through the slot props. Works without JavaScript.
7
7
 
8
8
  @props
9
9
 
@@ -49,7 +49,7 @@ export type AccordionSlots = typeof __propDef.slots;
49
49
  /**
50
50
  * ### Accordion
51
51
  *
52
- * Displays a list of `details` elements with helpful defaults and transitions.
52
+ * Displays a list of `details` elements with helpful defaults and transitions. Use `AccordionItem.data` to send any additional data through the slot props. Works without JavaScript.
53
53
  *
54
54
  * @props
55
55
  *
@@ -20,11 +20,9 @@ With SvelteKit, this component can be wrapped in an `{#if dev}` block that check
20
20
  import { Breakpoint } from "drab";
21
21
  </script>
22
22
 
23
- <div>
24
- <Breakpoint
25
- class="inline-block rounded border px-2 py-1 font-mono tabular-nums shadow"
26
- />
27
- </div>
23
+ <Breakpoint
24
+ class="inline-block rounded border px-2 py-1 font-mono tabular-nums shadow"
25
+ />
28
26
  ```
29
27
  -->
30
28
 
@@ -36,11 +36,9 @@ export type BreakpointSlots = typeof __propDef.slots;
36
36
  * import { Breakpoint } from "drab";
37
37
  * </script>
38
38
  *
39
- * <div>
40
39
  * <Breakpoint
41
- * class="inline-block rounded border px-2 py-1 font-mono tabular-nums shadow"
40
+ * class="inline-block rounded border px-2 py-1 font-mono tabular-nums shadow"
42
41
  * />
43
- * </div>
44
42
  * ```
45
43
  */
46
44
  export default class Breakpoint extends SvelteComponent<BreakpointProps, BreakpointEvents, BreakpointSlots> {
@@ -17,7 +17,7 @@ Generates a guitar chord `svg`.
17
17
  @example
18
18
 
19
19
  ```svelte
20
- <script>
20
+ <script lang="ts">
21
21
  import { Chord } from "drab";
22
22
  </script>
23
23
 
@@ -41,7 +41,7 @@ export type ChordSlots = typeof __propDef.slots;
41
41
  * @example
42
42
  *
43
43
  * ```svelte
44
- * <script>
44
+ * <script lang="ts">
45
45
  * import { Chord } from "drab";
46
46
  * </script>
47
47
  *
@@ -3,14 +3,15 @@
3
3
 
4
4
  ### ContextMenu
5
5
 
6
- Displays when the parent element is right clicked, or long pressed on mobile.
6
+ Displays when the `target` element is right clicked, or long pressed on mobile.
7
7
 
8
8
  @props
9
9
 
10
10
  - `class`
11
- - `display` - controls `display` css property
11
+ - `display` - shows / hides the menu
12
12
  - `id`
13
- - `transition` - fades the content, set to `false` to remove
13
+ - `target` - target element to right click, defaults to the parent element
14
+ - `transition` - scales the menu, set to `false` to disable
14
15
 
15
16
  @slots
16
17
 
@@ -21,12 +22,14 @@ Displays when the parent element is right clicked, or long pressed on mobile.
21
22
  @example
22
23
 
23
24
  ```svelte
24
- <script>
25
+ <script lang="ts">
25
26
  import { ContextMenu } from "drab";
27
+
28
+ let target: HTMLButtonElement;
26
29
  </script>
27
30
 
28
- <div class="flex justify-center rounded border border-dashed p-12">
29
- <div>Right click here</div>
31
+ <div class="mb-8 flex justify-center rounded border border-dashed p-12">
32
+ <div>Parent right click</div>
30
33
  <ContextMenu>
31
34
  <div class="flex w-48 flex-col gap-2 rounded border bg-white p-2 shadow">
32
35
  <div class="font-bold">Context Menu</div>
@@ -36,20 +39,30 @@ Displays when the parent element is right clicked, or long pressed on mobile.
36
39
  </div>
37
40
  </ContextMenu>
38
41
  </div>
42
+
43
+ <button type="button" class="btn" bind:this={target}>Target Right Click</button>
44
+ <ContextMenu {target}>
45
+ <div class="flex w-48 flex-col gap-2 rounded border bg-white p-2 shadow">
46
+ <div class="font-bold">Context Menu</div>
47
+ <button role="menuitem" class="btn">Button</button>
48
+ <button role="menuitem" class="btn">Button</button>
49
+ <button role="menuitem" class="btn">Button</button>
50
+ </div>
51
+ </ContextMenu>
39
52
  ```
40
53
  -->
41
54
 
42
55
  <script>import { onMount, tick } from "svelte";
43
- import { fade } from "svelte/transition";
44
- import { duration } from "../util/transition";
45
- import { messageNoScript } from "../util/messages";
56
+ import { scale } from "svelte/transition";
57
+ import { duration, start } from "../util/transition";
46
58
  import { prefersReducedMotion } from "../util/accessibility";
47
59
  import { delay } from "../util/delay";
48
60
  let className = "";
49
61
  export { className as class };
50
62
  export let id = "";
51
63
  export let display = false;
52
- export let transition = { duration };
64
+ export let transition = { duration, start };
65
+ export let target = null;
53
66
  let contextMenu;
54
67
  let base;
55
68
  let coordinates = { x: 0, y: 0 };
@@ -89,17 +102,20 @@ const onTouchStart = (e) => {
89
102
  const onTouchEnd = () => {
90
103
  clearTimeout(timer);
91
104
  };
92
- onMount(() => {
105
+ onMount(async () => {
93
106
  if (prefersReducedMotion()) {
94
107
  if (transition)
95
108
  transition.duration = 0;
96
109
  }
97
- const parentElement = base.parentElement;
98
- if (parentElement) {
99
- parentElement.addEventListener("contextmenu", displayMenu);
100
- parentElement.addEventListener("touchstart", onTouchStart);
101
- parentElement.addEventListener("touchend", onTouchEnd);
102
- parentElement.addEventListener("touchcancel", onTouchEnd);
110
+ await tick();
111
+ if (!target) {
112
+ target = base.parentElement;
113
+ }
114
+ if (target) {
115
+ target.addEventListener("contextmenu", displayMenu);
116
+ target.addEventListener("touchstart", onTouchStart);
117
+ target.addEventListener("touchend", onTouchEnd);
118
+ target.addEventListener("touchcancel", onTouchEnd);
103
119
  }
104
120
  });
105
121
  </script>
@@ -116,7 +132,7 @@ onMount(() => {
116
132
  bind:this={contextMenu}
117
133
  style:top="{coordinates.y}px"
118
134
  style:left="{coordinates.x}px"
119
- transition:fade={transition ? transition : { duration: 0 }}
135
+ transition:scale={transition ? transition : { duration: 0 }}
120
136
  >
121
137
  <slot>Context Menu</slot>
122
138
  </div>
@@ -1,11 +1,12 @@
1
1
  import { SvelteComponent } from "svelte";
2
- import { type FadeParams } from "svelte/transition";
2
+ import { type ScaleParams } from "svelte/transition";
3
3
  declare const __propDef: {
4
4
  props: {
5
5
  class?: string | undefined;
6
6
  id?: string | undefined;
7
- /** controls `display` css property */ display?: boolean | undefined;
8
- /** fades the content, set to `false` to remove */ transition?: false | FadeParams | undefined;
7
+ /** shows / hides the menu */ display?: boolean | undefined;
8
+ /** scales the menu, set to `false` to disable */ transition?: false | ScaleParams | undefined;
9
+ /** target element to right click, defaults to the parent element */ target?: HTMLElement | null | undefined;
9
10
  };
10
11
  events: {
11
12
  [evt: string]: CustomEvent<any>;
@@ -20,14 +21,15 @@ export type ContextMenuSlots = typeof __propDef.slots;
20
21
  /**
21
22
  * ### ContextMenu
22
23
  *
23
- * Displays when the parent element is right clicked, or long pressed on mobile.
24
+ * Displays when the `target` element is right clicked, or long pressed on mobile.
24
25
  *
25
26
  * @props
26
27
  *
27
28
  * - `class`
28
- * - `display` - controls `display` css property
29
+ * - `display` - shows / hides the menu
29
30
  * - `id`
30
- * - `transition` - fades the content, set to `false` to remove
31
+ * - `target` - target element to right click, defaults to the parent element
32
+ * - `transition` - scales the menu, set to `false` to disable
31
33
  *
32
34
  * @slots
33
35
  *
@@ -38,12 +40,14 @@ export type ContextMenuSlots = typeof __propDef.slots;
38
40
  * @example
39
41
  *
40
42
  * ```svelte
41
- * <script>
43
+ * <script lang="ts">
42
44
  * import { ContextMenu } from "drab";
45
+ *
46
+ * let target: HTMLButtonElement;
43
47
  * </script>
44
48
  *
45
- * <div class="flex justify-center rounded border border-dashed p-12">
46
- * <div>Right click here</div>
49
+ * <div class="mb-8 flex justify-center rounded border border-dashed p-12">
50
+ * <div>Parent right click</div>
47
51
  * <ContextMenu>
48
52
  * <div class="flex w-48 flex-col gap-2 rounded border bg-white p-2 shadow">
49
53
  * <div class="font-bold">Context Menu</div>
@@ -53,6 +57,16 @@ export type ContextMenuSlots = typeof __propDef.slots;
53
57
  * </div>
54
58
  * </ContextMenu>
55
59
  * </div>
60
+ *
61
+ * <button type="button" class="btn" bind:this={target}>Target Right Click</button>
62
+ * <ContextMenu {target}>
63
+ * <div class="flex w-48 flex-col gap-2 rounded border bg-white p-2 shadow">
64
+ * <div class="font-bold">Context Menu</div>
65
+ * <button role="menuitem" class="btn">Button</button>
66
+ * <button role="menuitem" class="btn">Button</button>
67
+ * <button role="menuitem" class="btn">Button</button>
68
+ * </div>
69
+ * </ContextMenu>
56
70
  * ```
57
71
  */
58
72
  export default class ContextMenu extends SvelteComponent<ContextMenuProps, ContextMenuEvents, ContextMenuSlots> {
@@ -3,7 +3,7 @@
3
3
 
4
4
  ### CopyButton
5
5
 
6
- Uses the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText) 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. If JavaScript is disabled, the button is disabled and `text` is displayed after the button.
7
7
 
8
8
  @props
9
9
 
@@ -23,13 +23,11 @@ Uses the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipbo
23
23
  @example
24
24
 
25
25
  ```svelte
26
- <script>
26
+ <script lang="ts">
27
27
  import { CopyButton } from "drab";
28
28
  </script>
29
29
 
30
- <div>
31
- <CopyButton class="btn" text="Text to copy" />
32
- </div>
30
+ <CopyButton class="btn" text="Text to copy" />
33
31
  ```
34
32
  -->
35
33
 
@@ -21,7 +21,7 @@ export type CopyButtonSlots = typeof __propDef.slots;
21
21
  /**
22
22
  * ### CopyButton
23
23
  *
24
- * Uses the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText) 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. If JavaScript is disabled, the button is disabled and `text` is displayed after the button.
25
25
  *
26
26
  * @props
27
27
  *
@@ -41,13 +41,11 @@ export type CopyButtonSlots = typeof __propDef.slots;
41
41
  * @example
42
42
  *
43
43
  * ```svelte
44
- * <script>
44
+ * <script lang="ts">
45
45
  * import { CopyButton } from "drab";
46
46
  * </script>
47
47
  *
48
- * <div>
49
48
  * <CopyButton class="btn" text="Text to copy" />
50
- * </div>
51
49
  * ```
52
50
  */
53
51
  export default class CopyButton extends SvelteComponent<CopyButtonProps, CopyButtonEvents, CopyButtonSlots> {
@@ -1,15 +1,15 @@
1
- <!--
2
- @component
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
3
9
 
4
- ### DataTable
5
-
6
- Data table to display an array of JS objects. Click a column header to sort.
7
-
8
- @props
9
-
10
- - `ascending` - default sort order
10
+ - `ascending` - current sort order
11
11
  - `classButton` - `button` class
12
- - `classFooter` - `footer` class
12
+ - `classFooter` - footer class
13
13
  - `classNoscript` - `noscript` class
14
14
  - `classPageControls` - class of `div` that wraps the "Previous" and "Next" buttons
15
15
  - `classPageNumber` - class of `div` wrapping page numbers
@@ -32,20 +32,20 @@ Data table to display an array of JS objects. Click a column header to sort.
32
32
  - `sortBy` - column to sort by, defaults to first column
33
33
  - `transition` - fades the rows in, set to `false` to disable
34
34
 
35
- @slots
36
-
37
- | name | purpose | default value | slot props |
38
- | ------------ | ------------------------ | -------------------------------- | ------------------------------ |
39
- | `next` | next button contents | `Next` | `currentPage` |
40
- | `pageNumber` | page numbers | `currentPage` / `numberOfPages` | `currentPage`, `numberOfPages` |
41
- | `previous` | previous button contents | `Previous` | `currentPage` |
42
- | `td` | td contents | `Previous` | `column`, `row` |
43
- | `th` | th contents | `Previous` | `column` |
44
-
45
- @example
35
+ @slots
36
+
37
+ | name | purpose | default value | slot props |
38
+ | ------------ | ------------------------ | -------------------------------- | ------------------------------ |
39
+ | `next` | next button contents | `Next` | `currentPage` |
40
+ | `pageNumber` | page numbers | `currentPage` / `numberOfPages` | `currentPage`, `numberOfPages` |
41
+ | `previous` | previous button contents | `Previous` | `currentPage` |
42
+ | `td` | td contents | `Previous` | `column`, `row` |
43
+ | `th` | th contents | `Previous` | `column` |
44
+
45
+ @example
46
46
 
47
47
  ```svelte
48
- <script>
48
+ <script lang="ts">
49
49
  import { DataTable } from "drab";
50
50
  </script>
51
51
 
@@ -106,10 +106,10 @@ Data table to display an array of JS objects. Click a column header to sort.
106
106
  </svelte:fragment>
107
107
  </DataTable>
108
108
  ```
109
- -->
110
-
111
- <script context="module"></script>
112
-
109
+ -->
110
+
111
+ <script context="module"></script>
112
+
113
113
  <script>import { onMount } from "svelte";
114
114
  import { fade } from "svelte/transition";
115
115
  import { duration } from "../util/transition";
@@ -195,67 +195,67 @@ onMount(() => {
195
195
  }
196
196
  clientJs = true;
197
197
  });
198
- </script>
199
-
200
- <div class={className} {id}>
201
- <table class={classTable} id={idTable}>
202
- <thead class={classThead}>
203
- <tr class={classTheadTr}>
204
- {#each columns as column}
205
- <th
206
- class="{classTh} {sortBy === column ? classThSorted : ''}"
207
- on:click={() => sort(column)}
208
- >
209
- <slot name="th" {column}>{column}</slot>
210
- </th>
211
- {/each}
212
- </tr>
213
- </thead>
214
- <tbody class={classTbody}>
215
- {#each data as row, i}
216
- {#if showRow(i, currentPage)}
217
- <tr
218
- in:fade={transition ? transition : { duration: 0 }}
219
- class={classTbodyTr}
220
- >
221
- {#each columns as column}
222
- <td class="{classTd} {sortBy === column ? classTdSorted : ''}">
223
- <slot name="td" {row} {column}>{row[column]}</slot>
224
- </td>
225
- {/each}
226
- </tr>
227
- {/if}
228
- {/each}
229
- </tbody>
230
- </table>
231
-
232
- {#if maxRows}
233
- <div class={classFooter}>
234
- <div class={classPageNumber}>
235
- <slot name="pageNumber" {currentPage} {numberOfPages}>
236
- {currentPage} / {numberOfPages}
237
- </slot>
238
- </div>
239
- <div class={classPageControls}>
240
- <button
241
- type="button"
242
- class={classButton}
243
- disabled={!clientJs || currentPage < 2}
244
- on:click={() => currentPage--}
245
- >
246
- <slot name="previous" {currentPage}>Previous</slot>
247
- </button>
248
- <button
249
- type="button"
250
- class={classButton}
251
- disabled={!clientJs || currentPage >= numberOfPages}
252
- on:click={() => currentPage++}
253
- >
254
- <slot name="next" {currentPage}>Next</slot>
255
- </button>
256
- </div>
257
- </div>
258
-
259
- <noscript><div class={classNoscript}>{messageNoScript}</div></noscript>
260
- {/if}
261
- </div>
198
+ </script>
199
+
200
+ <div class={className} {id}>
201
+ <table class={classTable} id={idTable}>
202
+ <thead class={classThead}>
203
+ <tr class={classTheadTr}>
204
+ {#each columns as column}
205
+ <th
206
+ class="{classTh} {sortBy === column ? classThSorted : ''}"
207
+ on:click={() => sort(column)}
208
+ >
209
+ <slot name="th" {column}>{column}</slot>
210
+ </th>
211
+ {/each}
212
+ </tr>
213
+ </thead>
214
+ <tbody class={classTbody}>
215
+ {#each data as row, i}
216
+ {#if showRow(i, currentPage)}
217
+ <tr
218
+ in:fade={transition ? transition : { duration: 0 }}
219
+ class={classTbodyTr}
220
+ >
221
+ {#each columns as column}
222
+ <td class="{classTd} {sortBy === column ? classTdSorted : ''}">
223
+ <slot name="td" {row} {column}>{row[column]}</slot>
224
+ </td>
225
+ {/each}
226
+ </tr>
227
+ {/if}
228
+ {/each}
229
+ </tbody>
230
+ </table>
231
+
232
+ {#if maxRows}
233
+ <div class={classFooter}>
234
+ <div class={classPageNumber}>
235
+ <slot name="pageNumber" {currentPage} {numberOfPages}>
236
+ {currentPage} / {numberOfPages}
237
+ </slot>
238
+ </div>
239
+ <div class={classPageControls}>
240
+ <button
241
+ type="button"
242
+ class={classButton}
243
+ disabled={!clientJs || currentPage < 2}
244
+ on:click={() => currentPage--}
245
+ >
246
+ <slot name="previous" {currentPage}>Previous</slot>
247
+ </button>
248
+ <button
249
+ type="button"
250
+ class={classButton}
251
+ disabled={!clientJs || currentPage >= numberOfPages}
252
+ on:click={() => currentPage++}
253
+ >
254
+ <slot name="next" {currentPage}>Next</slot>
255
+ </button>
256
+ </div>
257
+ </div>
258
+
259
+ <noscript><div class={classNoscript}>{messageNoScript}</div></noscript>
260
+ {/if}
261
+ </div>
@@ -10,7 +10,7 @@ declare const __propDef: {
10
10
  /** a list of objects to render in the table */ data: DataTableRow<any>[];
11
11
  /** table columns, in order */ columns?: string[] | undefined;
12
12
  /** column to sort by, defaults to first column */ sortBy?: string | undefined;
13
- /** default sort order */ ascending?: boolean | undefined;
13
+ /** current sort order */ ascending?: boolean | undefined;
14
14
  /** `table` class */ classTable?: string | undefined;
15
15
  /** `table` id */ idTable?: string | undefined;
16
16
  /** `thead` class */ classThead?: string | undefined;
@@ -22,7 +22,7 @@ declare const __propDef: {
22
22
  /** currently sorted `th` */ classThSorted?: string | undefined;
23
23
  /** currently sorted `td` */ classTdSorted?: string | undefined;
24
24
  /** `button` class */ classButton?: string | undefined;
25
- /** `footer` class */ classFooter?: string | undefined;
25
+ /** footer class */ classFooter?: string | undefined;
26
26
  /** class of `div` wrapping page numbers */ classPageNumber?: string | undefined;
27
27
  /** class of `div` that wraps the "Previous" and "Next" buttons */ classPageControls?: string | undefined;
28
28
  /** maximum number of rows to show on each page, defaults to `0` - no pagination */ maxRows?: number | undefined;
@@ -59,13 +59,13 @@ export type DataTableSlots = typeof __propDef.slots;
59
59
  /**
60
60
  * ### DataTable
61
61
  *
62
- * Data table to display an array of JS objects. Click a column header to sort.
62
+ * 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.
63
63
  *
64
64
  * @props
65
65
  *
66
- * - `ascending` - default sort order
66
+ * - `ascending` - current sort order
67
67
  * - `classButton` - `button` class
68
- * - `classFooter` - `footer` class
68
+ * - `classFooter` - footer class
69
69
  * - `classNoscript` - `noscript` class
70
70
  * - `classPageControls` - class of `div` that wraps the "Previous" and "Next" buttons
71
71
  * - `classPageNumber` - class of `div` wrapping page numbers
@@ -101,7 +101,7 @@ export type DataTableSlots = typeof __propDef.slots;
101
101
  * @example
102
102
  *
103
103
  * ```svelte
104
- * <script>
104
+ * <script lang="ts">
105
105
  * import { DataTable } from "drab";
106
106
  * </script>
107
107
  *
@@ -3,7 +3,9 @@
3
3
 
4
4
  ### Editor
5
5
 
6
- Text editor with controls to add elements and keyboard shortcuts.
6
+ `textarea` element with controls to add content and keyboard shortcuts. Compared to other WYSIWYG editors, this component's value 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)
7
9
 
8
10
  @props
9
11
 
@@ -17,13 +19,13 @@ Text editor with controls to add elements and keyboard shortcuts.
17
19
  - `keyPairs` - keys that will auto-close if typed, value is their closing character
18
20
  - `nameTextarea` - `name` of the `textarea` element
19
21
  - `placeholderTextarea` - `placeholder` of the `textarea` element
20
- - `selectionStart` - `selectionStart` value of the text area
22
+ - `selectionStartTextarea` - `selectionStart` value of the `textarea`
21
23
  - `valueTextarea` - `value` of the `textarea` element
22
24
 
23
25
  @example
24
26
 
25
27
  ```svelte
26
- <script>
28
+ <script lang="ts">
27
29
  import { Editor } from "drab";
28
30
  </script>
29
31
 
@@ -72,7 +74,7 @@ export let nameTextarea = "";
72
74
  export let classButton = "";
73
75
  export let classControls = "";
74
76
  export let idControls = "";
75
- export let selectionStart = 0;
77
+ export let selectionStartTextarea = 0;
76
78
  export let keyPairs = {
77
79
  "(": ")",
78
80
  "{": "}",
@@ -95,11 +97,11 @@ const insertChar = (str, char, index) => {
95
97
  const removeChar = (str, index) => {
96
98
  return str.slice(0, index) + str.slice(index + 1);
97
99
  };
98
- const insertText = async (el, selectionStart2, selectionEnd) => {
100
+ const insertText = async (el, selectionStart, selectionEnd) => {
99
101
  if (el.display === "inline") {
100
102
  valueTextarea = `${valueTextarea.slice(0, selectionEnd)}${el.text}${valueTextarea.slice(selectionEnd)}`;
101
103
  } else if (el.display === "wrap") {
102
- valueTextarea = insertChar(valueTextarea, el.text, selectionStart2);
104
+ valueTextarea = insertChar(valueTextarea, el.text, selectionStart);
103
105
  valueTextarea = insertChar(
104
106
  valueTextarea,
105
107
  keyPairs[el.text],
@@ -117,7 +119,7 @@ const insertText = async (el, selectionStart2, selectionEnd) => {
117
119
  valueTextarea = lines.join("\n");
118
120
  }
119
121
  };
120
- const setCaretPosition = async (text, selectionStart2, selectionEnd) => {
122
+ const setCaretPosition = async (text, selectionStart, selectionEnd) => {
121
123
  let startPos = 0;
122
124
  let endPos = 0;
123
125
  if (/[a-z]/i.test(text)) {
@@ -133,7 +135,7 @@ const setCaretPosition = async (text, selectionStart2, selectionEnd) => {
133
135
  }
134
136
  }
135
137
  } else {
136
- startPos = selectionStart2 + text.length;
138
+ startPos = selectionStart + text.length;
137
139
  endPos = selectionEnd + text.length;
138
140
  }
139
141
  textArea.setSelectionRange(startPos, endPos);
@@ -141,9 +143,9 @@ const setCaretPosition = async (text, selectionStart2, selectionEnd) => {
141
143
  };
142
144
  const addContent = async (el) => {
143
145
  const selectionEnd = textArea.selectionEnd;
144
- const selectionStart2 = textArea.selectionStart;
145
- await insertText(el, selectionStart2, selectionEnd);
146
- setCaretPosition(el.text, selectionStart2, selectionEnd);
146
+ const selectionStart = textArea.selectionStart;
147
+ await insertText(el, selectionStart, selectionEnd);
148
+ setCaretPosition(el.text, selectionStart, selectionEnd);
147
149
  };
148
150
  const onKeyDown = async (e) => {
149
151
  const resetKeys = ["ArrowUp", "ArrowDown", "Delete"];
@@ -267,7 +269,7 @@ const trimSelection = () => {
267
269
  }
268
270
  };
269
271
  const updateSelectionStart = () => {
270
- selectionStart = textArea.selectionStart;
272
+ selectionStartTextarea = textArea.selectionStart;
271
273
  };
272
274
  const getLineInfo = () => {
273
275
  const lines = valueTextarea.split("\n");
@@ -30,7 +30,7 @@ declare const __propDef: {
30
30
  /** `class` of all the `button` elements */ classButton?: string | undefined;
31
31
  /** `class` of the `div` that wraps the controls */ classControls?: string | undefined;
32
32
  /** `id` of the `div` that wraps the controls */ idControls?: string | undefined;
33
- /** `selectionStart` value of the text area */ selectionStart?: number | undefined;
33
+ /** `selectionStart` value of the `textarea` */ selectionStartTextarea?: number | undefined;
34
34
  /** keys that will auto-close if typed, value is their closing character */ keyPairs?: {
35
35
  [key: string]: string;
36
36
  } | undefined;
@@ -49,7 +49,9 @@ export type EditorSlots = typeof __propDef.slots;
49
49
  /**
50
50
  * ### Editor
51
51
  *
52
- * Text editor with controls to add elements and keyboard shortcuts.
52
+ * `textarea` element with controls to add content and keyboard shortcuts. Compared to other WYSIWYG editors, this component's value is just a `string` so you can easily store it in a database or manipulate it without learning a separate API.
53
+ *
54
+ * - This component is used to create [Typo](https://typo.robino.dev)
53
55
  *
54
56
  * @props
55
57
  *
@@ -63,13 +65,13 @@ export type EditorSlots = typeof __propDef.slots;
63
65
  * - `keyPairs` - keys that will auto-close if typed, value is their closing character
64
66
  * - `nameTextarea` - `name` of the `textarea` element
65
67
  * - `placeholderTextarea` - `placeholder` of the `textarea` element
66
- * - `selectionStart` - `selectionStart` value of the text area
68
+ * - `selectionStartTextarea` - `selectionStart` value of the `textarea`
67
69
  * - `valueTextarea` - `value` of the `textarea` element
68
70
  *
69
71
  * @example
70
72
  *
71
73
  * ```svelte
72
- * <script>
74
+ * <script lang="ts">
73
75
  * import { Editor } from "drab";
74
76
  * </script>
75
77
  *
@@ -9,7 +9,7 @@ Make the document or a specific element fullscreen.
9
9
 
10
10
  - `classNoscript` - `noscript` class
11
11
  - `class`
12
- - `confirmMessage` - message to display in the `confirm` popup, set this to empty string `""` to disable `confirm`
12
+ - `confirmMessage` - message to display in the `confirm` popup, defaults to empty string `""` -- disabled
13
13
  - `id`
14
14
  - `target` - element to make fullscreen (defaults to `document.documentElement` upon mount)
15
15
  - `title`
@@ -24,22 +24,17 @@ Make the document or a specific element fullscreen.
24
24
  @example
25
25
 
26
26
  ```svelte
27
- <script>
27
+ <script lang="ts">
28
28
  import { FullscreenButton } from "drab";
29
29
 
30
- let fullscreenDiv;
30
+ let target: HTMLDivElement;
31
31
  </script>
32
32
 
33
- <div>
34
- <FullscreenButton class="btn" />
35
- </div>
33
+ <FullscreenButton class="btn" />
36
34
 
37
- <div
38
- bind:this={fullscreenDiv}
39
- class="mt-4 rounded bg-neutral-800 p-4 text-neutral-50"
40
- >
35
+ <div bind:this={target} class="mt-8 rounded bg-neutral-800 p-4 text-neutral-50">
41
36
  <div class="mb-2">Target element</div>
42
- <FullscreenButton target={fullscreenDiv} class="btn btn-s bg-neutral-50">
37
+ <FullscreenButton {target} class="btn btn-s bg-neutral-50">
43
38
  Enable Element Fullscreen
44
39
  </FullscreenButton>
45
40
  </div>
@@ -55,7 +50,7 @@ export let title = "";
55
50
  export let target = null;
56
51
  export let confirmMessage = "";
57
52
  export let classNoscript = "";
58
- let supported = false;
53
+ let disabled = true;
59
54
  let fullscreen = false;
60
55
  const onClick = () => {
61
56
  if (fullscreen) {
@@ -74,7 +69,7 @@ const onClick = () => {
74
69
  };
75
70
  onMount(() => {
76
71
  if (document.documentElement.requestFullscreen)
77
- supported = true;
72
+ disabled = false;
78
73
  if (!target)
79
74
  target = document.documentElement;
80
75
  });
@@ -84,7 +79,7 @@ onMount(() => {
84
79
 
85
80
  <button
86
81
  type="button"
87
- disabled={!supported}
82
+ {disabled}
88
83
  on:click={onClick}
89
84
  class={className}
90
85
  {id}
@@ -97,8 +92,4 @@ onMount(() => {
97
92
  {/if}
98
93
  </button>
99
94
 
100
- <noscript>
101
- <div class={classNoscript}>
102
- {messageNoScript}
103
- </div>
104
- </noscript>
95
+ <noscript><div class={classNoscript}>{messageNoScript}</div></noscript>
@@ -5,7 +5,7 @@ declare const __propDef: {
5
5
  id?: string | undefined;
6
6
  title?: string | undefined;
7
7
  /** element to make fullscreen (defaults to `document.documentElement` upon mount) */ target?: HTMLElement | null | undefined;
8
- /** message to display in the `confirm` popup, set this to empty string `""` to disable `confirm` */ confirmMessage?: string | undefined;
8
+ /** message to display in the `confirm` popup, defaults to empty string `""` -- disabled */ confirmMessage?: string | undefined;
9
9
  /** `noscript` class */ classNoscript?: string | undefined;
10
10
  };
11
11
  events: {
@@ -28,7 +28,7 @@ export type FullscreenButtonSlots = typeof __propDef.slots;
28
28
  *
29
29
  * - `classNoscript` - `noscript` class
30
30
  * - `class`
31
- * - `confirmMessage` - message to display in the `confirm` popup, set this to empty string `""` to disable `confirm`
31
+ * - `confirmMessage` - message to display in the `confirm` popup, defaults to empty string `""` -- disabled
32
32
  * - `id`
33
33
  * - `target` - element to make fullscreen (defaults to `document.documentElement` upon mount)
34
34
  * - `title`
@@ -43,22 +43,17 @@ export type FullscreenButtonSlots = typeof __propDef.slots;
43
43
  * @example
44
44
  *
45
45
  * ```svelte
46
- * <script>
46
+ * <script lang="ts">
47
47
  * import { FullscreenButton } from "drab";
48
48
  *
49
- * let fullscreenDiv;
49
+ * let target: HTMLDivElement;
50
50
  * </script>
51
51
  *
52
- * <div>
53
52
  * <FullscreenButton class="btn" />
54
- * </div>
55
53
  *
56
- * <div
57
- * bind:this={fullscreenDiv}
58
- * class="mt-4 rounded bg-neutral-800 p-4 text-neutral-50"
59
- * >
54
+ * <div bind:this={target} class="mt-8 rounded bg-neutral-800 p-4 text-neutral-50">
60
55
  * <div class="mb-2">Target element</div>
61
- * <FullscreenButton target={fullscreenDiv} class="btn btn-s bg-neutral-50">
56
+ * <FullscreenButton {target} class="btn btn-s bg-neutral-50">
62
57
  * Enable Element Fullscreen
63
58
  * </FullscreenButton>
64
59
  * </div>
@@ -3,23 +3,22 @@
3
3
 
4
4
  ### Popover
5
5
 
6
- Displays a popover relatively positioned to the target.
6
+ Displays a popover relatively positioned to the target. Does not require the target to be `position: relative;`.
7
7
 
8
8
  @props
9
9
 
10
10
  - `class`
11
- - `display` - if `eventType="click"`, controls the display
11
+ - `display` - shows / hides the popover
12
12
  - `id`
13
- - `position` - where the popover is displayed in relation to the target
13
+ - `position` - where the popover is displayed in relation to the `target`
14
14
  - `target` - target element to position the popover in relation to
15
- - `transition` - fades in and out, set to false to disable
15
+ - `transition` - scales the popover, set to `false` to disable
16
16
 
17
17
  @slots
18
18
 
19
19
  | name | purpose | default value |
20
20
  | ---------- | ------------------------------- | ------------- |
21
21
  | `default` | default | Popover |
22
- | `button` | button contents | Open |
23
22
 
24
23
  @example
25
24
 
@@ -27,8 +26,7 @@ Displays a popover relatively positioned to the target.
27
26
  <script lang="ts">
28
27
  import { Popover } from "drab";
29
28
 
30
- let button: HTMLButtonElement;
31
- let hoverButton: HTMLButtonElement;
29
+ let target: HTMLButtonElement;
32
30
 
33
31
  let display = false;
34
32
  </script>
@@ -36,13 +34,13 @@ Displays a popover relatively positioned to the target.
36
34
  <button
37
35
  class="btn"
38
36
  type="button"
39
- bind:this={button}
37
+ bind:this={target}
40
38
  on:click={() => (display = !display)}
41
39
  >
42
40
  {display ? "Close" : "Open"}
43
41
  </button>
44
42
 
45
- <Popover target={button} bind:display class="p-2">
43
+ <Popover {target} bind:display class="p-2">
46
44
  <div class="flex w-48 flex-col gap-2 rounded border bg-white p-2 shadow">
47
45
  <div class="font-bold">Bottom</div>
48
46
  <button class="btn">Button</button>
@@ -54,16 +52,16 @@ Displays a popover relatively positioned to the target.
54
52
  -->
55
53
 
56
54
  <script>import { prefersReducedMotion } from "../util/accessibility";
57
- import { duration } from "../util/transition";
55
+ import { duration, start } from "../util/transition";
58
56
  import { onMount, tick } from "svelte";
59
- import { fade } from "svelte/transition";
57
+ import { scale } from "svelte/transition";
60
58
  let className = "";
61
59
  export { className as class };
62
60
  export let id = "";
63
61
  export let display = true;
64
62
  export let position = "bottom";
65
63
  export let target;
66
- export let transition = { duration };
64
+ export let transition = { duration, start };
67
65
  let popover;
68
66
  let coordinates = { x: 0, y: 0 };
69
67
  const setPosition = async () => {
@@ -124,7 +122,7 @@ onMount(() => {
124
122
  {id}
125
123
  style:top="{coordinates.y}px"
126
124
  style:left="{coordinates.x}px"
127
- transition:fade={transition ? transition : { duration: 0 }}
125
+ transition:scale={transition ? transition : { duration: 0 }}
128
126
  >
129
127
  <slot>Popover</slot>
130
128
  </div>
@@ -1,13 +1,13 @@
1
1
  import { SvelteComponent } from "svelte";
2
- import { type FadeParams } from "svelte/transition";
2
+ import { type ScaleParams } from "svelte/transition";
3
3
  declare const __propDef: {
4
4
  props: {
5
5
  class?: string | undefined;
6
6
  id?: string | undefined;
7
- /** if `eventType="click"`, controls the display */ display?: boolean | undefined;
8
- /** where the popover is displayed in relation to the target */ position?: "top" | "bottom" | "left" | "right" | undefined;
7
+ /** shows / hides the popover */ display?: boolean | undefined;
8
+ /** where the popover is displayed in relation to the `target` */ position?: "top" | "bottom" | "left" | "right" | undefined;
9
9
  /** target element to position the popover in relation to */ target: HTMLElement;
10
- /** fades in and out, set to false to disable */ transition?: false | FadeParams | undefined;
10
+ /** scales the popover, set to `false` to disable */ transition?: false | ScaleParams | undefined;
11
11
  };
12
12
  events: {
13
13
  [evt: string]: CustomEvent<any>;
@@ -22,23 +22,22 @@ export type PopoverSlots = typeof __propDef.slots;
22
22
  /**
23
23
  * ### Popover
24
24
  *
25
- * Displays a popover relatively positioned to the target.
25
+ * Displays a popover relatively positioned to the target. Does not require the target to be `position: relative;`.
26
26
  *
27
27
  * @props
28
28
  *
29
29
  * - `class`
30
- * - `display` - if `eventType="click"`, controls the display
30
+ * - `display` - shows / hides the popover
31
31
  * - `id`
32
- * - `position` - where the popover is displayed in relation to the target
32
+ * - `position` - where the popover is displayed in relation to the `target`
33
33
  * - `target` - target element to position the popover in relation to
34
- * - `transition` - fades in and out, set to false to disable
34
+ * - `transition` - scales the popover, set to `false` to disable
35
35
  *
36
36
  * @slots
37
37
  *
38
38
  * | name | purpose | default value |
39
39
  * | ---------- | ------------------------------- | ------------- |
40
40
  * | `default` | default | Popover |
41
- * | `button` | button contents | Open |
42
41
  *
43
42
  * @example
44
43
  *
@@ -46,8 +45,7 @@ export type PopoverSlots = typeof __propDef.slots;
46
45
  * <script lang="ts">
47
46
  * import { Popover } from "drab";
48
47
  *
49
- * let button: HTMLButtonElement;
50
- * let hoverButton: HTMLButtonElement;
48
+ * let target: HTMLButtonElement;
51
49
  *
52
50
  * let display = false;
53
51
  * </script>
@@ -55,13 +53,13 @@ export type PopoverSlots = typeof __propDef.slots;
55
53
  * <button
56
54
  * class="btn"
57
55
  * type="button"
58
- * bind:this={button}
56
+ * bind:this={target}
59
57
  * on:click={() => (display = !display)}
60
58
  * >
61
59
  * {display ? "Close" : "Open"}
62
60
  * </button>
63
61
  *
64
- * <Popover target={button} bind:display class="p-2">
62
+ * <Popover {target} bind:display class="p-2">
65
63
  * <div class="flex w-48 flex-col gap-2 rounded border bg-white p-2 shadow">
66
64
  * <div class="font-bold">Bottom</div>
67
65
  * <button class="btn">Button</button>
@@ -10,7 +10,7 @@ Uses the [Navigator API](https://developer.mozilla.org/en-US/docs/Web/API/Naviga
10
10
  - `files` - uses a hidden `anchor` element to download the first file in the `files` array
11
11
  - If no JavaScript:
12
12
  - `button` is disabled
13
- - `url` - displayed after the button
13
+ - `url` - displayed after the `button`
14
14
 
15
15
  @props
16
16
 
@@ -28,7 +28,7 @@ export type ShareButtonSlots = typeof __propDef.slots;
28
28
  * - `files` - uses a hidden `anchor` element to download the first file in the `files` array
29
29
  * - If no JavaScript:
30
30
  * - `button` is disabled
31
- * - `url` - displayed after the button
31
+ * - `url` - displayed after the `button`
32
32
  *
33
33
  * @props
34
34
  *
@@ -3,7 +3,7 @@
3
3
 
4
4
  ### Sheet
5
5
 
6
- Creates a sheet element based on the `position` provided.
6
+ Creates a sheet element based on the `position` provided. `maxSize` is set to width or height of the sheet depending on the `position` provided. The `transition` is calculated based on the `position` and `maxSize` of the sheet.
7
7
 
8
8
  @props
9
9
 
@@ -13,7 +13,7 @@ Creates a sheet element based on the `position` provided.
13
13
  - `id`
14
14
  - `maxSize` - max width/height of sheet based on the `side` - can also use css instead
15
15
  - `position` - determines the position of sheet
16
- - `transitionSheet` - slides the sheet, set to `false` to remove
16
+ - `transitionSheet` - flys the sheet, set to `false` to remove
17
17
  - `transition` - blurs the entire component, set to `false` to remove
18
18
 
19
19
  @slots
@@ -31,16 +31,15 @@ Creates a sheet element based on the `position` provided.
31
31
  let display = false;
32
32
  </script>
33
33
 
34
- <div>
35
- <button type="button" class="btn" on:click={() => (display = true)}>
36
- Open
37
- </button>
38
- </div>
34
+ <button type="button" class="btn" on:click={() => (display = true)}>
35
+ Open
36
+ </button>
39
37
 
40
38
  <Sheet
41
39
  bind:display
42
40
  class="bg-neutral-50/80 backdrop-blur"
43
41
  classSheet="p-4 shadow bg-white"
42
+ position="right"
44
43
  >
45
44
  <div class="mb-4 flex items-center justify-between">
46
45
  <h2 class="my-0">Sheet</h2>
@@ -9,7 +9,7 @@ declare const __propDef: {
9
9
  /** blurs the entire component, set to `false` to remove */ transition?: false | BlurParams | undefined;
10
10
  /** determines the position of sheet */ position?: "top" | "bottom" | "left" | "right" | undefined;
11
11
  /** max width/height of sheet based on the `side` - can also use css instead */ maxSize?: number | undefined;
12
- /** slides the sheet, set to `false` to remove */ transitionSheet?: false | FlyParams | undefined;
12
+ /** flys the sheet, set to `false` to remove */ transitionSheet?: false | FlyParams | undefined;
13
13
  };
14
14
  events: {
15
15
  [evt: string]: CustomEvent<any>;
@@ -24,7 +24,7 @@ export type SheetSlots = typeof __propDef.slots;
24
24
  /**
25
25
  * ### Sheet
26
26
  *
27
- * Creates a sheet element based on the `position` provided.
27
+ * Creates a sheet element based on the `position` provided. `maxSize` is set to width or height of the sheet depending on the `position` provided. The `transition` is calculated based on the `position` and `maxSize` of the sheet.
28
28
  *
29
29
  * @props
30
30
  *
@@ -34,7 +34,7 @@ export type SheetSlots = typeof __propDef.slots;
34
34
  * - `id`
35
35
  * - `maxSize` - max width/height of sheet based on the `side` - can also use css instead
36
36
  * - `position` - determines the position of sheet
37
- * - `transitionSheet` - slides the sheet, set to `false` to remove
37
+ * - `transitionSheet` - flys the sheet, set to `false` to remove
38
38
  * - `transition` - blurs the entire component, set to `false` to remove
39
39
  *
40
40
  * @slots
@@ -52,16 +52,15 @@ export type SheetSlots = typeof __propDef.slots;
52
52
  * let display = false;
53
53
  * </script>
54
54
  *
55
- * <div>
56
55
  * <button type="button" class="btn" on:click={() => (display = true)}>
57
- * Open
56
+ * Open
58
57
  * </button>
59
- * </div>
60
58
  *
61
59
  * <Sheet
62
60
  * bind:display
63
61
  * class="bg-neutral-50/80 backdrop-blur"
64
62
  * classSheet="p-4 shadow bg-white"
63
+ * position="right"
65
64
  * >
66
65
  * <div class="mb-4 flex items-center justify-between">
67
66
  * <h2 class="my-0">Sheet</h2>
@@ -3,7 +3,7 @@
3
3
 
4
4
  ### Tabs
5
5
 
6
- Displays tabs and the selected tab's content.
6
+ Displays tabs and the selected tab's content. Use `TabsTab.data` to send additional data through the slot props.
7
7
 
8
8
  @props
9
9
 
@@ -29,7 +29,7 @@ Displays tabs and the selected tab's content.
29
29
  @example
30
30
 
31
31
  ```svelte
32
- <script>
32
+ <script lang="ts">
33
33
  import { Tabs } from "drab";
34
34
  import { FullscreenButton } from "drab";
35
35
  </script>
@@ -42,7 +42,7 @@ export type TabsSlots = typeof __propDef.slots;
42
42
  /**
43
43
  * ### Tabs
44
44
  *
45
- * Displays tabs and the selected tab's content.
45
+ * Displays tabs and the selected tab's content. Use `TabsTab.data` to send additional data through the slot props.
46
46
  *
47
47
  * @props
48
48
  *
@@ -68,7 +68,7 @@ export type TabsSlots = typeof __propDef.slots;
68
68
  * @example
69
69
  *
70
70
  * ```svelte
71
- * <script>
71
+ * <script lang="ts">
72
72
  * import { Tabs } from "drab";
73
73
  * import { FullscreenButton } from "drab";
74
74
  * </script>
@@ -3,7 +3,7 @@
3
3
 
4
4
  ### YouTube
5
5
 
6
- Embeds a YouTube video into a website with the video `uid`, using [www.youtube-nocookie.com](https://support.google.com/youtube/answer/171780?hl=en#zippy=%2Cturn-on-privacy-enhanced-mode).
6
+ Embeds a YouTube video `iframe` into a website with the video `uid`, using [www.youtube-nocookie.com](https://support.google.com/youtube/answer/171780?hl=en#zippy=%2Cturn-on-privacy-enhanced-mode).
7
7
 
8
8
  @props
9
9
 
@@ -17,7 +17,7 @@ Embeds a YouTube video into a website with the video `uid`, using [www.youtube-n
17
17
  @example
18
18
 
19
19
  ```svelte
20
- <script>
20
+ <script lang="ts">
21
21
  import { YouTube } from "drab";
22
22
  </script>
23
23
 
@@ -19,7 +19,7 @@ export type YouTubeSlots = typeof __propDef.slots;
19
19
  /**
20
20
  * ### YouTube
21
21
  *
22
- * Embeds a YouTube video into a website with the video `uid`, using [www.youtube-nocookie.com](https://support.google.com/youtube/answer/171780?hl=en#zippy=%2Cturn-on-privacy-enhanced-mode).
22
+ * Embeds a YouTube video `iframe` into a website with the video `uid`, using [www.youtube-nocookie.com](https://support.google.com/youtube/answer/171780?hl=en#zippy=%2Cturn-on-privacy-enhanced-mode).
23
23
  *
24
24
  * @props
25
25
  *
@@ -33,7 +33,7 @@ export type YouTubeSlots = typeof __propDef.slots;
33
33
  * @example
34
34
  *
35
35
  * ```svelte
36
- * <script>
36
+ * <script lang="ts">
37
37
  * import { YouTube } from "drab";
38
38
  * </script>
39
39
  *
@@ -1 +1,2 @@
1
1
  export declare const duration = 150;
2
+ export declare const start = 0.95;
@@ -1 +1,2 @@
1
1
  export const duration = 150;
2
+ export const start = 0.95;
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "drab",
3
- "version": "2.8.0",
3
+ "version": "2.8.2",
4
4
  "description": "An unstyled Svelte component library",
5
5
  "keywords": [
6
6
  "components",
7
7
  "Svelte",
8
8
  "SvelteKit",
9
9
  "Accordion",
10
+ "Breakpoint",
10
11
  "Chord",
11
12
  "ContextMenu",
12
13
  "Copy",
@@ -15,6 +16,7 @@
15
16
  "Fullscreen",
16
17
  "Popover",
17
18
  "Share",
19
+ "Sheet",
18
20
  "Tabs",
19
21
  "YouTube"
20
22
  ],