drab 2.3.0 → 2.4.1

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.
@@ -21,20 +21,26 @@ Displays a list of `details` elements.
21
21
 
22
22
  @slots
23
23
 
24
- | name | purpose | default value |
25
- | ---------- | ----------------------------- | -------------- |
26
- | `summary` | summary element | `item.summary` |
27
- | `icon` | icon element | `icon` prop |
28
- | `content` | content of the accordion item | `item.content` |
24
+ | name | purpose | default value | slot props |
25
+ | --------- | ----------------------------- | -------------- | --------------- |
26
+ | `summary` | summary element | `item.summary` | `item`, `index` |
27
+ | `icon` | icon element | `icon` prop | `item`, `index` |
28
+ | `content` | content of the accordion item | `item.content` | `item`, `index` |
29
29
 
30
30
  @example
31
31
 
32
32
  ```svelte
33
33
  <script>
34
- import { Accordion } from "drab";
34
+ import Accordion from "./Accordion.svelte";
35
+ import FullscreenButton from "./FullscreenButton.svelte";
36
+ import Chevron from "../../site/svg/Chevron.svelte";
35
37
  </script>
36
38
 
37
- <Accordion
39
+ <Accordion
40
+ icon={Chevron}
41
+ classDetails="border-b"
42
+ classHeader="flex gap-8 cursor-pointer items-center justify-between py-4 font-bold hover:underline"
43
+ classContent="pb-4"
38
44
  items={[
39
45
  { summary: "Is it accessible?", content: "Yes." },
40
46
  {
@@ -46,38 +52,22 @@ Displays a list of `details` elements.
46
52
  content: "Yes, with the transition prop.",
47
53
  },
48
54
  { summary: "Does it work without Javascript?", content: "Yes." },
49
- ]}
50
- >
51
- <div slot="content" let:item let:index>
52
- <span>{index + 1}.</span>
53
- <span>{item.content}</span>
54
- </div>
55
- </Accordion>
56
- ```
57
-
58
- To render a component on unique items, pass it into the `data` prop and utilize `<svelte:component this={item.data.component}>` inside of the slot.
59
-
60
- ```svelte
61
- <script>
62
- import { Accordion, FullScreenButton } from "drab";
63
- </script>
64
-
65
- <Accordion
66
- items={[
67
55
  {
68
- summary: "A Component",
56
+ summary: "Component",
69
57
  content: "Rendered only on this item.",
70
58
  data: { component: FullscreenButton },
71
59
  },
72
- { summary: "Summary", content: "Some other content" },
73
60
  ]}
74
61
  >
75
- <div slot="content" let:item>
76
- {item.content}
62
+ <svelte:fragment slot="content" let:item let:index>
63
+ <div class="mb-4">
64
+ <span>{index + 1}.</span>
65
+ <span>{item.content}</span>
66
+ </div>
77
67
  {#if item.data?.component}
78
- <svelte:component this={item.data.component} />
68
+ <svelte:component class="btn" this={item.data.component} />
79
69
  {/if}
80
- </div>
70
+ </svelte:fragment>
81
71
  </Accordion>
82
72
  ```
83
73
  -->
@@ -1,5 +1,4 @@
1
1
  import { SvelteComponent } from "svelte";
2
- /** use `data` to pass anything back to the parent */
3
2
  export interface AccordionItem<T = any> {
4
3
  /** text summary of the item */
5
4
  summary?: string;
@@ -7,7 +6,7 @@ export interface AccordionItem<T = any> {
7
6
  content?: string;
8
7
  /** controls whether the content is displayed */
9
8
  open?: boolean;
10
- /** any data to pass back */
9
+ /** any data to pass back to the parent */
11
10
  data?: T;
12
11
  }
13
12
  import { type ComponentType } from "svelte";
@@ -68,20 +67,26 @@ export type AccordionSlots = typeof __propDef.slots;
68
67
  *
69
68
  * @slots
70
69
  *
71
- * | name | purpose | default value |
72
- * | ---------- | ----------------------------- | -------------- |
73
- * | `summary` | summary element | `item.summary` |
74
- * | `icon` | icon element | `icon` prop |
75
- * | `content` | content of the accordion item | `item.content` |
70
+ * | name | purpose | default value | slot props |
71
+ * | --------- | ----------------------------- | -------------- | --------------- |
72
+ * | `summary` | summary element | `item.summary` | `item`, `index` |
73
+ * | `icon` | icon element | `icon` prop | `item`, `index` |
74
+ * | `content` | content of the accordion item | `item.content` | `item`, `index` |
76
75
  *
77
76
  * @example
78
77
  *
79
78
  * ```svelte
80
79
  * <script>
81
- * import { Accordion } from "drab";
80
+ * import Accordion from "./Accordion.svelte";
81
+ * import FullscreenButton from "./FullscreenButton.svelte";
82
+ * import Chevron from "../../site/svg/Chevron.svelte";
82
83
  * </script>
83
84
  *
84
85
  * <Accordion
86
+ * icon={Chevron}
87
+ * classDetails="border-b"
88
+ * classHeader="flex gap-8 cursor-pointer items-center justify-between py-4 font-bold hover:underline"
89
+ * classContent="pb-4"
85
90
  * items={[
86
91
  * { summary: "Is it accessible?", content: "Yes." },
87
92
  * {
@@ -93,38 +98,22 @@ export type AccordionSlots = typeof __propDef.slots;
93
98
  * content: "Yes, with the transition prop.",
94
99
  * },
95
100
  * { summary: "Does it work without Javascript?", content: "Yes." },
96
- * ]}
97
- * >
98
- * <div slot="content" let:item let:index>
99
- * <span>{index + 1}.</span>
100
- * <span>{item.content}</span>
101
- * </div>
102
- * </Accordion>
103
- * ```
104
- *
105
- * To render a component on unique items, pass it into the `data` prop and utilize `<svelte:component this={item.data.component}>` inside of the slot.
106
- *
107
- * ```svelte
108
- * <script>
109
- * import { Accordion, FullScreenButton } from "drab";
110
- * </script>
111
- *
112
- * <Accordion
113
- * items={[
114
101
  * {
115
- * summary: "A Component",
102
+ * summary: "Component",
116
103
  * content: "Rendered only on this item.",
117
104
  * data: { component: FullscreenButton },
118
105
  * },
119
- * { summary: "Summary", content: "Some other content" },
120
106
  * ]}
121
107
  * >
122
- * <div slot="content" let:item>
123
- * {item.content}
108
+ * <svelte:fragment slot="content" let:item let:index>
109
+ * <div class="mb-4">
110
+ * <span>{index + 1}.</span>
111
+ * <span>{item.content}</span>
112
+ * </div>
124
113
  * {#if item.data?.component}
125
- * <svelte:component this={item.data.component} />
114
+ * <svelte:component class="btn" this={item.data.component} />
126
115
  * {/if}
127
- * </div>
116
+ * </svelte:fragment>
128
117
  * </Accordion>
129
118
  * ```
130
119
  */
@@ -21,7 +21,10 @@ Generates a guitar chord `svg`.
21
21
  import { Chord } from "drab";
22
22
  </script>
23
23
 
24
- <Chord name="D" notes={[
24
+ <Chord
25
+ class="text-gray-950"
26
+ name="D"
27
+ notes={[
25
28
  {
26
29
  finger: 0,
27
30
  string: 4,
@@ -45,7 +45,10 @@ export type ChordSlots = typeof __propDef.slots;
45
45
  * import { Chord } from "drab";
46
46
  * </script>
47
47
  *
48
- * <Chord name="D" notes={[
48
+ * <Chord
49
+ * class="text-gray-950"
50
+ * name="D"
51
+ * notes={[
49
52
  * {
50
53
  * finger: 0,
51
54
  * string: 4,
@@ -25,14 +25,14 @@ Displays when the parent element is right clicked.
25
25
  import { ContextMenu } from "drab";
26
26
  </script>
27
27
 
28
- <div>
28
+ <div class="flex justify-center rounded border border-dashed p-12">
29
29
  <div>Right click here</div>
30
- <ContextMenu>
31
- <div>
32
- <div>Context Menu</div>
33
- <button>Button</button>
34
- <button>Button</button>
35
- <button>Button</button>
30
+ <ContextMenu class="transition">
31
+ <div class="card flex w-48 flex-col gap-2">
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>
36
36
  </div>
37
37
  </ContextMenu>
38
38
  </div>
@@ -41,14 +41,14 @@ export type ContextMenuSlots = typeof __propDef.slots;
41
41
  * import { ContextMenu } from "drab";
42
42
  * </script>
43
43
  *
44
- * <div>
44
+ * <div class="flex justify-center rounded border border-dashed p-12">
45
45
  * <div>Right click here</div>
46
- * <ContextMenu>
47
- * <div>
48
- * <div>Context Menu</div>
49
- * <button>Button</button>
50
- * <button>Button</button>
51
- * <button>Button</button>
46
+ * <ContextMenu class="transition">
47
+ * <div class="card flex w-48 flex-col gap-2">
48
+ * <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>
52
52
  * </div>
53
53
  * </ContextMenu>
54
54
  * </div>
@@ -27,7 +27,9 @@ Uses the navigator api to copy text to the clipboard.
27
27
  import { CopyButton } from "drab";
28
28
  </script>
29
29
 
30
- <CopyButton text="Text to copy" />
30
+ <div>
31
+ <CopyButton class="btn" text="Text to copy" />
32
+ </div>
31
33
  ```
32
34
  -->
33
35
 
@@ -42,10 +42,12 @@ export type CopyButtonSlots = typeof __propDef.slots;
42
42
  *
43
43
  * ```svelte
44
44
  * <script>
45
- * import { CopyButton } from "drab";
45
+ * import { CopyButton } from "drab";
46
46
  * </script>
47
47
  *
48
- * <CopyButton text="Text to copy" />
48
+ * <div>
49
+ * <CopyButton class="btn" text="Text to copy" />
50
+ * </div>
49
51
  * ```
50
52
  */
51
53
  export default class CopyButton extends SvelteComponent<CopyButtonProps, CopyButtonEvents, CopyButtonSlots> {
@@ -22,10 +22,12 @@ Data table to display an array of JS objects. Click a column header to sort.
22
22
  - `classTh` - `th` class
23
23
  - `classTheadTr` - `thead tr` class
24
24
  - `classThead` - `thead` class
25
+ - `class`
25
26
  - `columns` - table columns, in order
26
27
  - `currentPage` - current page, defaults to `1`
27
28
  - `data` - a list of objects to render in the table
28
29
  - `idTable` - `table` id
30
+ - `id`
29
31
  - `paginate` - number of rows to show on each page, defaults to `0` - no pagination
30
32
  - `sortBy` - column to sort by--defaults to first column
31
33
 
@@ -43,15 +45,26 @@ Data table to display an array of JS objects. Click a column header to sort.
43
45
  import { DataTable } from "drab";
44
46
  </script>
45
47
 
46
- <DataTable
48
+ <DataTable
47
49
  data={[
48
50
  { make: "Honda", model: "CR-V", year: 2011, awd: true },
49
51
  { make: "Volvo", model: "XC-40", year: 2024, awd: true },
50
52
  { make: "Ferrari", model: "458 Italia", year: 2015, awd: false },
51
53
  { make: "Chevrolet", model: "Silverado", year: 2022, awd: true },
52
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 },
53
59
  ]}
54
60
  sortBy="make"
61
+ paginate={4}
62
+ class="tabular-nums"
63
+ classTh="cursor-pointer uppercase"
64
+ classThSorted="underline"
65
+ classTbodyTr="transition hover:bg-gray-50"
66
+ classFooter="flex justify-between items-center"
67
+ classButton="btn"
55
68
  />
56
69
  ```
57
70
  -->
@@ -60,6 +73,9 @@ Data table to display an array of JS objects. Click a column header to sort.
60
73
 
61
74
  <script>import { messageNoScript } from "../util/messages";
62
75
  import { onMount } from "svelte";
76
+ let className = "";
77
+ export { className as class };
78
+ export let id = "";
63
79
  export let data;
64
80
  export let columns = [];
65
81
  if (!columns.length && data[0]) {
@@ -125,60 +141,62 @@ sort(sortBy, false);
125
141
  onMount(() => clientJs = true);
126
142
  </script>
127
143
 
128
- <table class={classTable} id={idTable}>
129
- <thead class={classThead}>
130
- <tr class={classTheadTr}>
131
- {#each columns as column}
132
- <th
133
- class="{classTh} {sortBy === column ? classThSorted : ''}"
134
- on:click={() => sort(column)}
135
- >
136
- {column}
137
- </th>
144
+ <div class={className} {id}>
145
+ <table class={classTable} id={idTable}>
146
+ <thead class={classThead}>
147
+ <tr class={classTheadTr}>
148
+ {#each columns as column}
149
+ <th
150
+ class="{classTh} {sortBy === column ? classThSorted : ''}"
151
+ on:click={() => sort(column)}
152
+ >
153
+ {column}
154
+ </th>
155
+ {/each}
156
+ </tr>
157
+ </thead>
158
+ <tbody class={classTbody}>
159
+ {#each data as row, i}
160
+ {@const showRow =
161
+ i < paginate * currentPage && i >= paginate * (currentPage - 1)}
162
+ {#if paginate ? showRow : true}
163
+ <tr class={classTbodyTr}>
164
+ {#each columns as column}
165
+ <td class="{classTd} {sortBy === column ? classTdSorted : ''}">
166
+ {row[column]}
167
+ </td>
168
+ {/each}
169
+ </tr>
170
+ {/if}
138
171
  {/each}
139
- </tr>
140
- </thead>
141
- <tbody class={classTbody}>
142
- {#each data as row, i}
143
- {@const showRow =
144
- i < paginate * currentPage && i >= paginate * (currentPage - 1)}
145
- {#if paginate ? showRow : true}
146
- <tr class={classTbodyTr}>
147
- {#each columns as column}
148
- <td class="{classTd} {sortBy === column ? classTdSorted : ''}">
149
- {row[column]}
150
- </td>
151
- {/each}
152
- </tr>
153
- {/if}
154
- {/each}
155
- </tbody>
156
- </table>
172
+ </tbody>
173
+ </table>
157
174
 
158
- {#if paginate}
159
- <div class={classFooter}>
160
- <div class={classPageNumber}>{currentPage} / {numberOfPages}</div>
161
- <div class={classPageControls}>
162
- <button
163
- class={classButton}
164
- disabled={!clientJs || currentPage < 2}
165
- on:click={() => currentPage--}
166
- >
167
- <slot name="previous">Previous</slot>
168
- </button>
169
- <button
170
- class={classButton}
171
- disabled={!clientJs || currentPage >= numberOfPages}
172
- on:click={() => currentPage++}
173
- >
174
- <slot name="next">Next</slot>
175
- </button>
175
+ {#if paginate}
176
+ <div class={classFooter}>
177
+ <div class={classPageNumber}>{currentPage} / {numberOfPages}</div>
178
+ <div class={classPageControls}>
179
+ <button
180
+ class={classButton}
181
+ disabled={!clientJs || currentPage < 2}
182
+ on:click={() => currentPage--}
183
+ >
184
+ <slot name="previous">Previous</slot>
185
+ </button>
186
+ <button
187
+ class={classButton}
188
+ disabled={!clientJs || currentPage >= numberOfPages}
189
+ on:click={() => currentPage++}
190
+ >
191
+ <slot name="next">Next</slot>
192
+ </button>
193
+ </div>
176
194
  </div>
177
- </div>
178
195
 
179
- <noscript>
180
- <div class={classNoscript}>
181
- {messageNoScript}
182
- </div>
183
- </noscript>
184
- {/if}
196
+ <noscript>
197
+ <div class={classNoscript}>
198
+ {messageNoScript}
199
+ </div>
200
+ </noscript>
201
+ {/if}
202
+ </div>
@@ -4,6 +4,8 @@ export type DataTableRow<T> = {
4
4
  };
5
5
  declare const __propDef: {
6
6
  props: {
7
+ class?: string | undefined;
8
+ id?: string | undefined;
7
9
  /** a list of objects to render in the table */ data: DataTableRow<any>[];
8
10
  /** table columns, in order */ columns?: string[] | undefined;
9
11
  /** column to sort by--defaults to first column */ sortBy?: string | undefined;
@@ -59,10 +61,12 @@ export type DataTableSlots = typeof __propDef.slots;
59
61
  * - `classTh` - `th` class
60
62
  * - `classTheadTr` - `thead tr` class
61
63
  * - `classThead` - `thead` class
64
+ * - `class`
62
65
  * - `columns` - table columns, in order
63
66
  * - `currentPage` - current page, defaults to `1`
64
67
  * - `data` - a list of objects to render in the table
65
68
  * - `idTable` - `table` id
69
+ * - `id`
66
70
  * - `paginate` - number of rows to show on each page, defaults to `0` - no pagination
67
71
  * - `sortBy` - column to sort by--defaults to first column
68
72
  *
@@ -87,8 +91,19 @@ export type DataTableSlots = typeof __propDef.slots;
87
91
  * { make: "Ferrari", model: "458 Italia", year: 2015, awd: false },
88
92
  * { make: "Chevrolet", model: "Silverado", year: 2022, awd: true },
89
93
  * { make: "Ford", model: "Model A", year: 1931, awd: false },
94
+ * { make: "Subaru", model: "Outback", year: 2021, awd: true },
95
+ * { make: "Ford", model: "Bronco", year: 1970, awd: true },
96
+ * { make: "GMC", model: "Acadia", year: 2008, awd: true },
97
+ * { make: "BMW", model: "X3", year: 2023, awd: true },
90
98
  * ]}
91
99
  * sortBy="make"
100
+ * paginate={4}
101
+ * class="tabular-nums"
102
+ * classTh="cursor-pointer uppercase"
103
+ * classThSorted="underline"
104
+ * classTbodyTr="transition hover:bg-gray-50"
105
+ * classFooter="flex justify-between items-center"
106
+ * classButton="btn"
92
107
  * />
93
108
  * ```
94
109
  */
@@ -27,7 +27,12 @@ Text editor with controls to add elements and keyboard shortcuts.
27
27
  import { Editor } from "drab";
28
28
  </script>
29
29
 
30
- <Editor contentElements={[
30
+ <Editor
31
+ classButton="btn"
32
+ classControls="flex gap-2"
33
+ classTextarea="border w-full h-36 p-2 rounded"
34
+ placeholderTextarea="asterisk: ctrl+i, anchor: ctrl+["
35
+ contentElements={[
31
36
  {
32
37
  name: "Bullet",
33
38
  text: "- ",
@@ -73,7 +73,12 @@ export type EditorSlots = typeof __propDef.slots;
73
73
  * import { Editor } from "drab";
74
74
  * </script>
75
75
  *
76
- * <Editor contentElements={[
76
+ * <Editor
77
+ * classButton="btn"
78
+ * classControls="flex gap-2"
79
+ * classTextarea="border w-full h-36 p-2 rounded"
80
+ * placeholderTextarea="asterisk: ctrl+i, anchor: ctrl+["
81
+ * contentElements={[
77
82
  * {
78
83
  * name: "Bullet",
79
84
  * text: "- ",
@@ -30,13 +30,18 @@ Make the document or a specific element fullscreen.
30
30
  let fullscreenDiv;
31
31
  </script>
32
32
 
33
- <FullscreenButton />
33
+ <div>
34
+ <FullscreenButton class="btn" />
35
+ </div>
34
36
 
35
- <div bind:this={fullscreenDiv}>
36
- <div>Target element fullscreen</div>
37
- <FullscreenButton targetElement={fullscreenDiv}>
37
+ <div
38
+ bind:this={fullscreenDiv}
39
+ class="mt-4 rounded bg-gray-800 p-4 text-gray-50"
40
+ >
41
+ <div class="mb-2">Target element fullscreen</div>
42
+ <FullscreenButton targetElement={fullscreenDiv} class="btn">
43
+ <span>Enable Element Fullscreen</span>
38
44
  <span slot="enabled">Exit Element Fullscreen</span>
39
- <span slot="disabled">Enable Element Fullscreen</span>
40
45
  </FullscreenButton>
41
46
  </div>
42
47
  ```
@@ -49,13 +49,18 @@ export type FullscreenButtonSlots = typeof __propDef.slots;
49
49
  * let fullscreenDiv;
50
50
  * </script>
51
51
  *
52
- * <FullscreenButton />
52
+ * <div>
53
+ * <FullscreenButton class="btn" />
54
+ * </div>
53
55
  *
54
- * <div bind:this={fullscreenDiv}>
55
- * <div>Target element fullscreen</div>
56
- * <FullscreenButton targetElement={fullscreenDiv}>
56
+ * <div
57
+ * bind:this={fullscreenDiv}
58
+ * class="mt-4 rounded bg-gray-800 p-4 text-gray-50"
59
+ * >
60
+ * <div class="mb-2">Target element fullscreen</div>
61
+ * <FullscreenButton targetElement={fullscreenDiv} class="btn">
62
+ * <span>Enable Element Fullscreen</span>
57
63
  * <span slot="enabled">Exit Element Fullscreen</span>
58
- * <span slot="disabled">Enable Element Fullscreen</span>
59
64
  * </FullscreenButton>
60
65
  * </div>
61
66
  * ```
@@ -31,13 +31,27 @@ Displays a popover relatively positioned to the button.
31
31
  import { Popover } from "drab";
32
32
  </script>
33
33
 
34
- <Popover>
34
+ <Popover classButton="btn" classPopover="p-2 transition">
35
35
  <span slot="button">Hover</span>
36
- <div>
37
- <div>Popover</div>
38
- <button>Button</button>
39
- <button>Button</button>
40
- <button>Button</button>
36
+ <div class="card flex w-48 flex-col gap-2">
37
+ <div class="font-bold">Bottom</div>
38
+ <button class="btn">Button</button>
39
+ <button class="btn">Button</button>
40
+ <button class="btn">Button</button>
41
+ </div>
42
+ </Popover>
43
+ <Popover
44
+ classButton="btn"
45
+ classPopover="p-2 transition"
46
+ eventType="click"
47
+ position="right"
48
+ >
49
+ <span slot="button">Click</span>
50
+ <div class="card flex w-48 flex-col gap-2">
51
+ <div class="font-bold">Right</div>
52
+ <button class="btn">Button</button>
53
+ <button class="btn">Button</button>
54
+ <button class="btn">Button</button>
41
55
  </div>
42
56
  </Popover>
43
57
  ```
@@ -53,13 +53,27 @@ export type PopoverSlots = typeof __propDef.slots;
53
53
  * import { Popover } from "drab";
54
54
  * </script>
55
55
  *
56
- * <Popover>
56
+ * <Popover classButton="btn" classPopover="p-2 transition">
57
57
  * <span slot="button">Hover</span>
58
- * <div>
59
- * <div>Popover</div>
60
- * <button>Button</button>
61
- * <button>Button</button>
62
- * <button>Button</button>
58
+ * <div class="card flex w-48 flex-col gap-2">
59
+ * <div class="font-bold">Bottom</div>
60
+ * <button class="btn">Button</button>
61
+ * <button class="btn">Button</button>
62
+ * <button class="btn">Button</button>
63
+ * </div>
64
+ * </Popover>
65
+ * <Popover
66
+ * classButton="btn"
67
+ * classPopover="p-2 transition"
68
+ * eventType="click"
69
+ * position="right"
70
+ * >
71
+ * <span slot="button">Click</span>
72
+ * <div class="card flex w-48 flex-col gap-2">
73
+ * <div class="font-bold">Right</div>
74
+ * <button class="btn">Button</button>
75
+ * <button class="btn">Button</button>
76
+ * <button class="btn">Button</button>
63
77
  * </div>
64
78
  * </Popover>
65
79
  * ```
@@ -28,11 +28,14 @@ Uses the navigator api to share or copy a url link depending on browser support.
28
28
  import { ShareButton } from "drab";
29
29
  </script>
30
30
 
31
- <ShareButton
32
- text="Check out this page: "
33
- title="drab"
34
- url="https://drab.robino.dev"
35
- />
31
+ <div>
32
+ <ShareButton
33
+ class="btn"
34
+ text="Check out this page: "
35
+ title="drab"
36
+ url="https://drab.robino.dev"
37
+ />
38
+ </div>
36
39
  ```
37
40
  -->
38
41
 
@@ -47,11 +47,14 @@ export type ShareButtonSlots = typeof __propDef.slots;
47
47
  * import { ShareButton } from "drab";
48
48
  * </script>
49
49
  *
50
+ * <div>
50
51
  * <ShareButton
51
- * text="Check out this page: "
52
- * title="drab"
53
- * url="https://drab.robino.dev"
52
+ * class="btn"
53
+ * text="Check out this page: "
54
+ * title="drab"
55
+ * url="https://drab.robino.dev"
54
56
  * />
57
+ * </div>
55
58
  * ```
56
59
  */
57
60
  export default class ShareButton extends SvelteComponent<ShareButtonProps, ShareButtonEvents, ShareButtonSlots> {
@@ -3,100 +3,115 @@
3
3
 
4
4
  ### Tabs
5
5
 
6
- Displays tabs and slotted content.
6
+ Displays tabs and the active tab's content.
7
7
 
8
8
  @props
9
9
 
10
- - `classButtonActive` - class of the active tab's `button`
11
- - `classButtonInactive` - class of all the inactive tabs' `button`s
12
- - `classButton` - `button` class
10
+ - `activeIndex` - index of active tab, defaults to 0
11
+ - `classContent` - class of `div` that wraps the slotted content
13
12
  - `classHeader` - class of the `div` that wraps the `button`s
14
13
  - `classNoscript` - `noscript` class
15
- - `classSlot` - class of `div` that wraps the slotted content
14
+ - `classTitleActive` - class of the active tab's `button`
15
+ - `classTitleInactive` - class of all the inactive tabs' `button`s
16
+ - `classTitle` - class of all title `button`s
16
17
  - `class`
17
- - `content` - array of `TabContent` elements
18
18
  - `id`
19
- - `transition` - fades the content in, defaults to empty object, set to false to remove
19
+ - `tabs` - array of tabs
20
20
 
21
21
  @slots
22
22
 
23
- Pass components into the `content` prop if needed. `TabsContent` has a `slot` attribute of type `string | ComponentType`.
23
+ | name | purpose | default value | slot props |
24
+ | --------- | --------------------- | -------------------- | --------------- |
25
+ | `default` | active item's content | `activeItem.content` | `activeItem` |
26
+ | `title` | title of each tab | `item.title` | `item`, `index` |
24
27
 
25
28
  @example
26
29
 
27
30
  ```svelte
28
31
  <script>
29
32
  import { Tabs } from "drab";
30
- import { FullscreenButton } from "drab";
33
+ import FullscreenButton from "./FullscreenButton.svelte";
31
34
  </script>
32
35
 
33
- <Tabs content={[
34
- { name: "String", slot: "Slot" },
35
- { name: "Component", slot: FullscreenButton },
36
+ <Tabs
37
+ classHeader="grid grid-flow-col grid-rows-1 gap-1 rounded bg-gray-200 p-1"
38
+ classTitle="btn rounded-sm p-0.5"
39
+ classTitleActive="bg-white text-gray-950"
40
+ classTitleInactive="bg-gray-200 text-gray-500"
41
+ classContent="py-2"
42
+ tabs={[
43
+ { title: "Tab 1", content: "Content 1" },
44
+ { title: "Tab 2", content: "Content 2" },
36
45
  ]}
37
46
  />
47
+
48
+ <Tabs
49
+ classHeader="grid grid-flow-col grid-rows-1 gap-1 rounded bg-gray-200 p-1"
50
+ classTitle="btn rounded-sm p-0.5"
51
+ classTitleActive="bg-white text-gray-950"
52
+ classTitleInactive="bg-gray-200 text-gray-500"
53
+ classContent="py-2"
54
+ tabs={[
55
+ { title: "Tab", content: "Generated indexes" },
56
+ {
57
+ title: "Tab",
58
+ content: "A tab with a component",
59
+ data: { component: FullscreenButton },
60
+ },
61
+ ]}
62
+ let:activeTab
63
+ >
64
+ <svelte:fragment slot="title" let:item let:index>
65
+ {item.title}
66
+ {index + 1}
67
+ </svelte:fragment>
68
+ <div>{activeTab.content}</div>
69
+ {#if activeTab.data?.component}
70
+ <svelte:component class="btn" this={activeTab.data.component} />
71
+ {/if}
72
+ </Tabs>
38
73
  ```
39
74
  -->
40
75
 
41
76
  <script context="module"></script>
42
77
 
43
78
  <script>import { onMount } from "svelte";
44
- import { fade } from "svelte/transition";
45
79
  import { messageNoScript } from "../util/messages";
46
80
  let className = "";
47
81
  export { className as class };
48
82
  export let id = "";
49
83
  export let classHeader = "";
50
- export let classButton = "";
51
- export let classButtonActive = "";
52
- export let classButtonInactive = "";
84
+ export let classTitle = "";
85
+ export let classTitleActive = "";
86
+ export let classTitleInactive = "";
53
87
  export let classNoscript = "";
54
- export let classSlot = "";
55
- export let content;
56
- export let transition = {};
57
- const fadeTransition = transition ? transition : { duration: 0 };
88
+ export let classContent = "";
89
+ export let tabs;
90
+ export let activeIndex = 0;
58
91
  let clientJs = false;
59
- let activeIndex = 0;
60
- for (const item of content) {
61
- if (!item.classContentSlot)
62
- item.classContentSlot = "";
63
- }
64
92
  $:
65
- activeTab = content[activeIndex];
93
+ activeTab = tabs[activeIndex];
66
94
  onMount(() => clientJs = true);
67
95
  </script>
68
96
 
69
97
  <div class={className} {id}>
70
98
  <div class={classHeader}>
71
- {#each content as { name }, i}
99
+ {#each tabs as item, index}
72
100
  <button
73
101
  disabled={!clientJs}
74
- class="{classButton} {activeIndex === i
75
- ? classButtonActive
76
- : ''} {activeIndex !== i ? classButtonInactive : ''}"
77
- on:click={() => (activeIndex = i)}
102
+ class="{classTitle} {activeIndex === index
103
+ ? classTitleActive
104
+ : ''} {activeIndex !== index ? classTitleInactive : ''}"
105
+ on:click={() => (activeIndex = index)}
78
106
  >
79
- {name}
107
+ <slot name="title" {item} {index}>{item.title}</slot>
80
108
  </button>
81
109
  {/each}
82
110
  </div>
83
- <div class={classSlot}>
84
- {#if typeof activeTab.slot !== "string"}
85
- <div in:fade={fadeTransition}>
86
- <svelte:component
87
- this={activeTab.slot}
88
- class={activeTab.classContentSlot}
89
- />
90
- </div>
91
- {:else}
92
- <div in:fade={fadeTransition} class={activeTab.classContentSlot}>
93
- {activeTab.slot}
94
- </div>
95
- {/if}
111
+ <div class={classContent}>
112
+ <slot {activeTab}>{activeTab.content}</slot>
96
113
  </div>
97
114
  <noscript>
98
- <div class={classNoscript}>
99
- {messageNoScript}
100
- </div>
115
+ <div class={classNoscript}>{messageNoScript}</div>
101
116
  </noscript>
102
117
  </div>
@@ -1,31 +1,37 @@
1
1
  import { SvelteComponent } from "svelte";
2
- import type { ComponentType } from "svelte";
3
- export interface TabsContent {
4
- /** tab name, displayed in `button` element */
5
- name: string;
2
+ export interface TabsTab<T = any> {
3
+ /** tab title, displayed in `button` element */
4
+ title?: string;
6
5
  /** slotted content, displayed once tab is clicked */
7
- slot: string | ComponentType;
8
- /** class of the slotted content */
9
- classContentSlot?: string;
6
+ content?: string;
7
+ /** any data to pass back to the parent */
8
+ data?: T;
10
9
  }
11
- import { type FadeParams } from "svelte/transition";
12
10
  declare const __propDef: {
13
11
  props: {
14
12
  class?: string | undefined;
15
13
  id?: string | undefined;
16
14
  /** class of the `div` that wraps the `button`s */ classHeader?: string | undefined;
17
- /** `button` class */ classButton?: string | undefined;
18
- /** class of the active tab's `button` */ classButtonActive?: string | undefined;
19
- /** class of all the inactive tabs' `button`s */ classButtonInactive?: string | undefined;
15
+ /** class of all title `button`s */ classTitle?: string | undefined;
16
+ /** class of the active tab's `button` */ classTitleActive?: string | undefined;
17
+ /** class of all the inactive tabs' `button`s */ classTitleInactive?: string | undefined;
20
18
  /** `noscript` class */ classNoscript?: string | undefined;
21
- /** class of `div` that wraps the slotted content */ classSlot?: string | undefined;
22
- /** array of `TabContent` elements */ content: TabsContent[];
23
- /** fades the content in, defaults to empty object, set to false to remove */ transition?: false | FadeParams | undefined;
19
+ /** class of `div` that wraps the slotted content */ classContent?: string | undefined;
20
+ /** array of tabs */ tabs: TabsTab[];
21
+ /** index of active tab, defaults to 0 */ activeIndex?: number | undefined;
24
22
  };
25
23
  events: {
26
24
  [evt: string]: CustomEvent<any>;
27
25
  };
28
- slots: {};
26
+ slots: {
27
+ title: {
28
+ item: TabsTab<any>;
29
+ index: any;
30
+ };
31
+ default: {
32
+ activeTab: TabsTab<any>;
33
+ };
34
+ };
29
35
  };
30
36
  export type TabsProps = typeof __propDef.props;
31
37
  export type TabsEvents = typeof __propDef.events;
@@ -33,38 +39,73 @@ export type TabsSlots = typeof __propDef.slots;
33
39
  /**
34
40
  * ### Tabs
35
41
  *
36
- * Displays tabs and slotted content.
42
+ * Displays tabs and the active tab's content.
37
43
  *
38
44
  * @props
39
45
  *
40
- * - `classButtonActive` - class of the active tab's `button`
41
- * - `classButtonInactive` - class of all the inactive tabs' `button`s
42
- * - `classButton` - `button` class
46
+ * - `activeIndex` - index of active tab, defaults to 0
47
+ * - `classContent` - class of `div` that wraps the slotted content
43
48
  * - `classHeader` - class of the `div` that wraps the `button`s
44
49
  * - `classNoscript` - `noscript` class
45
- * - `classSlot` - class of `div` that wraps the slotted content
50
+ * - `classTitleActive` - class of the active tab's `button`
51
+ * - `classTitleInactive` - class of all the inactive tabs' `button`s
52
+ * - `classTitle` - class of all title `button`s
46
53
  * - `class`
47
- * - `content` - array of `TabContent` elements
48
54
  * - `id`
49
- * - `transition` - fades the content in, defaults to empty object, set to false to remove
55
+ * - `tabs` - array of tabs
50
56
  *
51
57
  * @slots
52
58
  *
53
- * Pass components into the `content` prop if needed. `TabsContent` has a `slot` attribute of type `string | ComponentType`.
59
+ * | name | purpose | default value | slot props |
60
+ * | --------- | --------------------- | -------------------- | --------------- |
61
+ * | `default` | active item's content | `activeItem.content` | `activeItem` |
62
+ * | `title` | title of each tab | `item.title` | `item`, `index` |
54
63
  *
55
64
  * @example
56
65
  *
57
66
  * ```svelte
58
67
  * <script>
59
68
  * import { Tabs } from "drab";
60
- * import { FullscreenButton } from "drab";
69
+ * import FullscreenButton from "./FullscreenButton.svelte";
61
70
  * </script>
62
71
  *
63
- * <Tabs content={[
64
- * { name: "String", slot: "Slot" },
65
- * { name: "Component", slot: FullscreenButton },
72
+ * <Tabs
73
+ * classHeader="grid grid-flow-col grid-rows-1 gap-1 rounded bg-gray-200 p-1"
74
+ * classTitle="btn rounded-sm p-0.5"
75
+ * classTitleActive="bg-white text-gray-950"
76
+ * classTitleInactive="bg-gray-200 text-gray-500"
77
+ * classContent="py-2"
78
+ * tabs={[
79
+ * { title: "Tab 1", content: "Content 1" },
80
+ * { title: "Tab 2", content: "Content 2" },
66
81
  * ]}
67
82
  * />
83
+ *
84
+ * <Tabs
85
+ * classHeader="grid grid-flow-col grid-rows-1 gap-1 rounded bg-gray-200 p-1"
86
+ * classTitle="btn rounded-sm p-0.5"
87
+ * classTitleActive="bg-white text-gray-950"
88
+ * classTitleInactive="bg-gray-200 text-gray-500"
89
+ * classContent="py-2"
90
+ * tabs={[
91
+ * { title: "Tab", content: "Generated indexes" },
92
+ * {
93
+ * title: "Tab",
94
+ * content: "A tab with a component",
95
+ * data: { component: FullscreenButton },
96
+ * },
97
+ * ]}
98
+ * let:activeTab
99
+ * >
100
+ * <svelte:fragment slot="title" let:item let:index>
101
+ * {item.title}
102
+ * {index + 1}
103
+ * </svelte:fragment>
104
+ * <div>{activeTab.content}</div>
105
+ * {#if activeTab.data?.component}
106
+ * <svelte:component class="btn" this={activeTab.data.component} />
107
+ * {/if}
108
+ * </Tabs>
68
109
  * ```
69
110
  */
70
111
  export default class Tabs extends SvelteComponent<TabsProps, TabsEvents, TabsSlots> {
@@ -21,7 +21,11 @@ Embeds a YouTube video into a website with the video `uid`, using [www.youtube-n
21
21
  import { YouTube } from "drab";
22
22
  </script>
23
23
 
24
- <YouTube title="Video Title" uid="youtube_uid" />
24
+ <YouTube
25
+ class="aspect-video w-full rounded"
26
+ title="Renegade - Kevin Olusola"
27
+ uid="gouiY85kD2o"
28
+ />
25
29
  ```
26
30
  -->
27
31
 
@@ -37,7 +37,11 @@ export type YouTubeSlots = typeof __propDef.slots;
37
37
  * import { YouTube } from "drab";
38
38
  * </script>
39
39
  *
40
- * <YouTube title="Video Title" uid="youtube_uid" />
40
+ * <YouTube
41
+ * class="aspect-video w-full rounded"
42
+ * title="Renegade - Kevin Olusola"
43
+ * uid="gouiY85kD2o"
44
+ * />
41
45
  * ```
42
46
  */
43
47
  export default class YouTube extends SvelteComponent<YouTubeProps, YouTubeEvents, YouTubeSlots> {
package/dist/index.d.ts CHANGED
@@ -11,5 +11,7 @@ import type { EditorContentElement } from "./components/Editor.svelte";
11
11
  import FullscreenButton from "./components/FullscreenButton.svelte";
12
12
  import Popover from "./components/Popover.svelte";
13
13
  import ShareButton from "./components/ShareButton.svelte";
14
+ import Tabs from "./components/Tabs.svelte";
15
+ import type { TabsTab } from "./components/Tabs.svelte";
14
16
  import YouTube from "./components/YouTube.svelte";
15
- export { Accordion, type AccordionItem, Chord, type ChordNote, ContextMenu, CopyButton, DataTable, type DataTableRow, Editor, type EditorContentElement, FullscreenButton, Popover, ShareButton, YouTube, };
17
+ export { Accordion, type AccordionItem, Chord, type ChordNote, ContextMenu, CopyButton, DataTable, type DataTableRow, Editor, type EditorContentElement, FullscreenButton, Popover, ShareButton, Tabs, type TabsTab, YouTube, };
package/dist/index.js CHANGED
@@ -7,5 +7,6 @@ import Editor from "./components/Editor.svelte";
7
7
  import FullscreenButton from "./components/FullscreenButton.svelte";
8
8
  import Popover from "./components/Popover.svelte";
9
9
  import ShareButton from "./components/ShareButton.svelte";
10
+ import Tabs from "./components/Tabs.svelte";
10
11
  import YouTube from "./components/YouTube.svelte";
11
- export { Accordion, Chord, ContextMenu, CopyButton, DataTable, Editor, FullscreenButton, Popover, ShareButton, YouTube, };
12
+ export { Accordion, Chord, ContextMenu, CopyButton, DataTable, Editor, FullscreenButton, Popover, ShareButton, Tabs, YouTube, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drab",
3
- "version": "2.3.0",
3
+ "version": "2.4.1",
4
4
  "description": "An unstyled Svelte component library",
5
5
  "keywords": [
6
6
  "components",
@@ -15,6 +15,7 @@
15
15
  "Fullscreen",
16
16
  "Popover",
17
17
  "Share",
18
+ "Tabs",
18
19
  "YouTube"
19
20
  ],
20
21
  "homepage": "https://drab.robino.dev",
@@ -35,7 +36,7 @@
35
36
  "lint": "prettier --check . && eslint .",
36
37
  "format": "prettier --write . --plugin=prettier-plugin-svelte --plugin=prettier-plugin-tailwindcss",
37
38
  "pub": "npm publish --access public",
38
- "doc": "node scripts/buildDocs.js"
39
+ "doc": "node src/scripts/buildDocs.js"
39
40
  },
40
41
  "exports": {
41
42
  ".": {