drab 2.8.6 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,67 +1,69 @@
1
- # An unstyled Svelte component library
2
-
3
- - [GitHub](https://github.com/rossrobino/drab)
4
- - [npm](https://www.npmjs.com/package/drab)
5
- - [MIT License](https://github.com/rossrobino/drab/blob/main/LICENSE.md)
6
- - One dependency - [Svelte](https://svelte.dev)
7
-
8
- ## About
9
-
10
- **drab** focuses on providing JavaScript functionality where it's most useful, while leaving out components that can be easily created using HTML, such as a label or badge. Whenever possible, components are [progressively enhanced](/docs/ShareButton) or provide a fallback [noscript](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript) message. Additionally, transitions are disabled for users who prefer reduced motion.
11
-
12
- This library takes a more opinionated approach compared to some headless UI libraries by providing the basic HTML structure for every component, as well as default positioning for elements like the [sheet](/docs/Sheet) or [popover](/docs/Popover). However, these components can still be further customized using styles, [slots](https://svelte.dev/tutorial/slots), and [slot props](https://svelte.dev/tutorial/slot-props).
13
-
14
- Components without styles can appear rather drab. You have the freedom to bring your own styles to these components! Using unstyled components allows you to selectively choose what you need, seamlessly integrate with existing designs, and avoid being tied to any specific library.
15
-
16
- To style the components, you can make use of [global styles](https://joyofcode.xyz/global-styles-in-sveltekit). This process can be expedited by utilizing CSS frameworks like [TailwindCSS](https://tailwindcss.com/). TailwindCSS generates a global stylesheet based on the utility classes used in your project. Each component exports `class` and `id` props that can be leveraged for this purpose.
17
-
18
- ## Install
19
-
20
- If you haven't used Svelte before, start with the [tutorial](https://svelte.dev/tutorial/basics).
21
-
22
- - [SvelteKit](https://kit.svelte.dev)
23
- - [Astro](https://docs.astro.build/en/tutorial/1-setup/2/)
24
- - [Vite](https://vitejs.dev/guide/)
25
-
26
- ```bash
27
- npm install -D drab
28
- ```
29
-
30
- ## Documentation
31
-
32
- The library provides inline documentation for each component, allowing you to conveniently access the documentation by hovering over the component in your text editor after importing it. Additionally, every prop is documented using JSDoc and TypeScript. By hovering over a prop, you can retrieve its type and description.
33
-
34
- These docs use the [TailwindCSS Typography plugin](https://tailwindcss.com/docs/typography-plugin) for base styles along with a few custom utility classes you can find [here](https://github.com/rossrobino/drab/blob/main/src/app.postcss). Styles on this site are based on [shadcn/ui](https://ui.shadcn.com/).
35
-
36
- ## Alternatives
37
-
38
- If **drab** isn't what you are looking for, here are some other Svelte UI libraries to check out.
39
-
40
- - [Skeleton](https://skeleton.dev)
41
- - [Melt UI](https://www.melt-ui.com/)
42
- - [shadcn-svelte](https://www.shadcn-svelte.com/)
43
- - [Svelte-HeadlessUI](https://captaincodeman.github.io/svelte-headlessui/)
44
-
45
- ## Contributing
46
-
47
- Find an bug or have an idea? Feel free to create an issue on [GitHub](https://github.com/rossrobino/drab).
48
-
49
- Since this is an unstyled library, simple components like a badge that can be easily created with HTML and CSS are not included.
50
-
51
- ### Local Development
52
-
53
- Contribute to the project, or use **drab** as a template for another component library. This library is built with SvelteKit, TypeScript, and npm. The package contents are located in `src/lib`, the site is contained within `src/routes` and `src/site`. The site is deployed to Vercel using `@sveltejs/adapter-vercel`. If you are using this project as a template, be sure to [update the adapter](https://kit.svelte.dev/docs/adapters) based on how you deploy.
54
-
55
- #### Make changes
56
-
57
- 1. Clone the [repository](https://github.com/rossrobino/drab)
58
- 2. `npm install`
59
- 3. `npm run dev -- --open`
60
-
61
- #### Add or edit a component
62
-
63
- 1. Add/edit the component in `src/lib/components/Component.svelte`, if you're adding a new one, copy and paste an existing one to get started with the conventions
64
- 2. Add/edit the example in `src/routes/docs/Component/+page.svelte`
65
- 3. Document the component with an `@component` comment, include a description, and the `@slots` available. Add a placeholder `@props` and `@example` to the comment. These sections will be generated based on the JSDoc comment above each prop and the example route created upon running `npm run doc`
66
- 4. If new, add the link to `src/site/components/NavItems.svelte`
67
- 5. Run `npm run build` to verify your build
1
+ # An Unstyled Svelte Component Library
2
+
3
+ - [Docs](https://drab.robino.dev)
4
+ - [GitHub](https://github.com/rossrobino/drab)
5
+ - [npm](https://www.npmjs.com/package/drab)
6
+ - [MIT License](https://github.com/rossrobino/drab/blob/main/LICENSE.md)
7
+
8
+ ## About
9
+
10
+ **drab** focuses on providing JavaScript functionality where it's most useful. Many of the components are helpful wrappers around browser APIs. Whenever possible, components are [progressively enhanced](/docs/ShareButton) or provide a fallback [noscript](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript) message. Additionally, transitions are disabled for users who prefer reduced motion.
11
+
12
+ This library takes a more opinionated approach compared to some headless UI libraries by providing the basic HTML structure for every component, as well as default positioning for elements like the [sheet](/docs/Sheet). However, these components can still be further customized using styles, [slots](https://svelte.dev/tutorial/slots), and [slot props](https://svelte.dev/tutorial/slot-props).
13
+
14
+ Components without styles can appear rather drab. You have the freedom to bring your own styles to these components! Using unstyled components allows you to selectively choose what you need, seamlessly integrate with existing designs, and avoid being tied to any specific library.
15
+
16
+ To style the components, you can make use of [global styles](https://joyofcode.xyz/global-styles-in-sveltekit). Each component exports `class` and `id` props that can be leveraged for this purpose. This process can be expedited by utilizing CSS frameworks like [TailwindCSS](https://tailwindcss.com/). Tailwind generates a global stylesheet based on the utility classes used in your project. The examples in this documentation are styled with Tailwind classes, but Tailwind does not have to be used with this library.
17
+
18
+ ## Install
19
+
20
+ If you haven't used Svelte before, start with the [tutorial](https://svelte.dev/tutorial/basics). **drab** works anywhere Svelte does.
21
+
22
+ - [SvelteKit](https://kit.svelte.dev)
23
+ - [Astro](https://docs.astro.build/en/tutorial/1-setup/2/)
24
+ - [Vite](https://vitejs.dev/guide/)
25
+
26
+ ```bash
27
+ npm install -D drab
28
+ ```
29
+
30
+ ## Documentation
31
+
32
+ The library provides inline documentation for each component, allowing you to conveniently access the documentation by hovering over the component in your text editor after importing it. Additionally, every prop is documented using JSDoc and TypeScript. By hovering over a prop, you can retrieve its type and description.
33
+
34
+ These docs use the [TailwindCSS Typography plugin](https://tailwindcss.com/docs/typography-plugin) for base styles along with a few custom utility classes you can find [here](https://github.com/rossrobino/drab/blob/main/src/app.postcss). Styles on this site are based on [shadcn/ui](https://ui.shadcn.com/).
35
+
36
+ ## Alternatives
37
+
38
+ If **drab** isn't what you are looking for, here are some other Svelte UI libraries to check out.
39
+
40
+ - [Skeleton](https://skeleton.dev)
41
+ - [Melt UI](https://www.melt-ui.com/)
42
+ - [shadcn-svelte](https://www.shadcn-svelte.com/)
43
+ - [Svelte-HeadlessUI](https://captaincodeman.github.io/svelte-headlessui/)
44
+
45
+ ## Contributing
46
+
47
+ Find an bug or have an idea? Feel free to create an issue on [GitHub](https://github.com/rossrobino/drab). **drab** is meant to house all kinds of components including ones outside of the standard UI elements.
48
+
49
+ Currently, **drab** has only one dependency - Svelte. Not to say it will never have another, but please consider this when adding in additional functionality. **drab** is meant to make the web platform easier to use with Svelte.
50
+
51
+ Since this is an unstyled library, simple components like a badge that can be easily created with HTML and CSS are not included.
52
+
53
+ ### Local Development
54
+
55
+ Contribute to the project, or use **drab** as a template for another component library. This library is built with SvelteKit, TypeScript, and npm. The package contents are located in `src/lib`, the site is contained within `src/routes` and `src/site`. The site is deployed to Vercel using `@sveltejs/adapter-vercel`. If you are using this project as a template, be sure to [update the adapter](https://kit.svelte.dev/docs/adapters) based on how you deploy.
56
+
57
+ #### Make changes
58
+
59
+ 1. Clone the [repository](https://github.com/rossrobino/drab)
60
+ 2. `npm install`
61
+ 3. `npm run dev -- --open`
62
+
63
+ #### Add or edit a component
64
+
65
+ 1. Add or edit the component in `src/lib/components/Component.svelte` - if you're adding a new one, copy and paste an existing one to get started with the conventions
66
+ 2. Add or edit the example in `src/routes/docs/Component/+page.svelte`
67
+ 3. Document the component with an `@component` comment, include a description, and the `@slots` available. Add a placeholder `@props` and `@example` to the comment. These sections will be generated based on the JSDoc comment above each prop and the example route upon running `npm run doc`
68
+ 4. If new, add the link to `src/site/components/NavItems.svelte`
69
+ 5. Run `npm run build` to verify your build
@@ -16,13 +16,13 @@ With SvelteKit, this component can be wrapped in an `{#if dev}` block that check
16
16
  @example
17
17
 
18
18
  ```svelte
19
- <script lang="ts">
20
- import { Breakpoint } from "drab";
21
- </script>
22
-
23
- <Breakpoint
24
- class="inline-block rounded border px-2 py-1 font-mono tabular-nums shadow"
25
- />
19
+ <script lang="ts">
20
+ import { Breakpoint } from "drab";
21
+ </script>
22
+
23
+ <Breakpoint
24
+ class="inline-block rounded border bg-white px-2 py-1 font-mono tabular-nums shadow"
25
+ />
26
26
  ```
27
27
  -->
28
28
 
@@ -37,7 +37,7 @@ export type BreakpointSlots = typeof __propDef.slots;
37
37
  * </script>
38
38
  *
39
39
  * <Breakpoint
40
- * class="inline-block rounded border px-2 py-1 font-mono tabular-nums shadow"
40
+ * class="inline-block rounded border bg-white px-2 py-1 font-mono tabular-nums shadow"
41
41
  * />
42
42
  * ```
43
43
  */
@@ -1,11 +1,11 @@
1
- <!--
2
- @component
3
-
4
- ### ContextMenu
5
-
6
- Displays when the `target` element is right clicked, or long pressed on mobile.
7
-
8
- @props
1
+ <!--
2
+ @component
3
+
4
+ ### ContextMenu
5
+
6
+ Displays when the `target` element is right clicked, or long pressed on mobile.
7
+
8
+ @props
9
9
 
10
10
  - `class`
11
11
  - `display` - shows / hides the menu
@@ -13,45 +13,45 @@ Displays when the `target` element is right clicked, or long pressed on mobile.
13
13
  - `target` - target element to right click, defaults to the parent element
14
14
  - `transition` - scales the menu, set to `false` to disable
15
15
 
16
- @slots
17
-
18
- | name | purpose | default value |
19
- | ---------- | ------------------------------- | ------------- |
20
- | `default` | default | Context Menu |
21
-
22
- @example
16
+ @slots
17
+
18
+ | name | purpose | default value |
19
+ | ---------- | ------------------------------- | ------------- |
20
+ | `default` | default | Context Menu |
21
+
22
+ @example
23
23
 
24
24
  ```svelte
25
- <script lang="ts">
26
- import { ContextMenu } from "drab";
27
-
28
- let target: HTMLButtonElement;
29
- </script>
30
-
31
- <div class="mb-8 flex justify-center rounded border border-dashed p-12">
32
- <div>Parent right click</div>
33
- <ContextMenu>
34
- <div class="flex w-48 flex-col gap-2 rounded border bg-white p-2 shadow">
35
- <div class="font-bold">Context Menu</div>
36
- <button role="menuitem" class="btn">Button</button>
37
- <button role="menuitem" class="btn">Button</button>
38
- <button role="menuitem" class="btn">Button</button>
39
- </div>
40
- </ContextMenu>
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>
25
+ <script lang="ts">
26
+ import { ContextMenu } from "drab";
27
+
28
+ let target: HTMLButtonElement;
29
+ </script>
30
+
31
+ <div class="mb-8 flex justify-center rounded border border-dashed p-12">
32
+ <div>Parent right click</div>
33
+ <ContextMenu>
34
+ <div class="flex w-48 flex-col gap-2 rounded border bg-white p-2 shadow">
35
+ <div class="font-bold">Context Menu</div>
36
+ <button role="menuitem" class="btn">Button</button>
37
+ <button role="menuitem" class="btn">Button</button>
38
+ <button role="menuitem" class="btn">Button</button>
39
+ </div>
40
+ </ContextMenu>
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">Target</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>
52
52
  ```
53
- -->
54
-
53
+ -->
54
+
55
55
  <script>import { onMount, tick } from "svelte";
56
56
  import { scale } from "svelte/transition";
57
57
  import { duration, start } from "../util/transition";
@@ -116,34 +116,33 @@ onMount(async () => {
116
116
  target.addEventListener("touchcancel", onTouchEnd);
117
117
  }
118
118
  });
119
- </script>
120
-
121
- <svelte:body on:click={hide} on:keydown={onKeyDown} />
122
-
123
- <span bind:this={base} role="presentation"></span>
124
-
125
- {#if display}
126
- <div
127
- role="menu"
128
- class={className}
129
- {id}
130
- bind:this={contextMenu}
131
- style:top="{coordinates.y}px"
132
- style:left="{coordinates.x}px"
133
- transition:scale={transition ? transition : { duration: 0 }}
134
- >
135
- <slot>Context Menu</slot>
136
- </div>
137
- {/if}
138
-
139
- <style>
140
- span {
141
- width: 0;
142
- height: 0;
143
- opacity: 0;
144
- }
145
- div {
146
- position: absolute;
147
- z-index: 10;
148
- }
149
- </style>
119
+ </script>
120
+
121
+ <svelte:body on:click={hide} on:keydown={onKeyDown} />
122
+
123
+ <span bind:this={base} role="presentation"></span>
124
+
125
+ {#if display}
126
+ <div
127
+ role="menu"
128
+ class={className}
129
+ {id}
130
+ bind:this={contextMenu}
131
+ style:top="{coordinates.y}px"
132
+ style:left="{coordinates.x}px"
133
+ transition:scale={transition ? transition : { duration: 0 }}
134
+ >
135
+ <slot>Context Menu</slot>
136
+ </div>
137
+ {/if}
138
+
139
+ <style>
140
+ span {
141
+ width: 0;
142
+ height: 0;
143
+ opacity: 0;
144
+ }
145
+ div {
146
+ position: absolute;
147
+ }
148
+ </style>
@@ -61,7 +61,7 @@ export type ContextMenuSlots = typeof __propDef.slots;
61
61
  * <button type="button" class="btn" bind:this={target}>Target Right Click</button>
62
62
  * <ContextMenu {target}>
63
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>
64
+ * <div class="font-bold">Target</div>
65
65
  * <button role="menuitem" class="btn">Button</button>
66
66
  * <button role="menuitem" class="btn">Button</button>
67
67
  * <button role="menuitem" class="btn">Button</button>
@@ -3,14 +3,16 @@
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. If JavaScript is disabled, the button is disabled and `text` is displayed after the button.
6
+ Uses the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard) to copy data to the clipboard. Falls back to `writeText` if `write` is not supported and `blob.type` is text. If JavaScript is disabled, the button is disabled and `blobParts.join()` is displayed after the button if `blob.type` is text.
7
7
 
8
8
  @props
9
9
 
10
+ - `blobParts` - array of `BlobParts` to copy - [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#parameters)
11
+ - `blob` - use a blob in instead of `blobParts` and `options`, defaults to `new Blob(blobParts, options)`
10
12
  - `classNoscript` - `noscript` class
11
13
  - `class`
12
14
  - `id`
13
- - `text` - text to copy
15
+ - `options` - defaults to `{ type: "text/plain" }` - [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#parameters)
14
16
  - `title`
15
17
 
16
18
  @slots
@@ -23,11 +25,11 @@ Uses the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipbo
23
25
  @example
24
26
 
25
27
  ```svelte
26
- <script lang="ts">
27
- import { CopyButton } from "drab";
28
- </script>
29
-
30
- <CopyButton class="btn" text="Text to copy" />
28
+ <script lang="ts">
29
+ import { CopyButton } from "drab";
30
+ </script>
31
+
32
+ <CopyButton class="btn" blobParts={["Text to copy"]} />
31
33
  ```
32
34
  -->
33
35
 
@@ -37,25 +39,41 @@ let className = "";
37
39
  export { className as class };
38
40
  export let id = "";
39
41
  export let title = "";
40
- export let text;
42
+ export let options = { type: "text/plain" };
43
+ export let blobParts = void 0;
44
+ export let blob = new Blob(blobParts, options);
41
45
  export let classNoscript = "";
42
- let clientJs = false;
46
+ let disabled = true;
43
47
  let complete = false;
48
+ const writeSupport = () => "write" in navigator.clipboard;
49
+ const typeText = blob.type.startsWith("text");
50
+ const canWriteText = () => {
51
+ const writeTextSupport = "writeText" in navigator.clipboard;
52
+ return writeTextSupport && typeText;
53
+ };
44
54
  const copyText = async () => {
45
55
  try {
46
- await navigator.clipboard.writeText(text);
56
+ if (writeSupport()) {
57
+ const data = [new ClipboardItem({ [blob.type]: blob })];
58
+ await navigator.clipboard.write(data);
59
+ } else if (canWriteText()) {
60
+ await navigator.clipboard.writeText(blobParts ? blobParts.join() : "");
61
+ }
47
62
  complete = true;
48
63
  setTimeout(() => complete = false, delay);
49
64
  } catch (error) {
50
65
  console.error(error);
51
66
  }
52
67
  };
53
- onMount(() => clientJs = true);
68
+ onMount(() => {
69
+ if (writeSupport() || canWriteText())
70
+ disabled = false;
71
+ });
54
72
  </script>
55
73
 
56
74
  <button
57
75
  type="button"
58
- disabled={!clientJs}
76
+ {disabled}
59
77
  on:click={copyText}
60
78
  class={className}
61
79
  {id}
@@ -68,4 +86,8 @@ onMount(() => clientJs = true);
68
86
  {/if}
69
87
  </button>
70
88
 
71
- <noscript><span class={classNoscript}>{text}</span></noscript>
89
+ {#if typeText}
90
+ <noscript>
91
+ <span class={classNoscript}>{blobParts ? blobParts.join() : ""}</span>
92
+ </noscript>
93
+ {/if}
@@ -4,7 +4,9 @@ declare const __propDef: {
4
4
  class?: string | undefined;
5
5
  id?: string | undefined;
6
6
  title?: string | undefined;
7
- /** text to copy */ text: string;
7
+ /** defaults to `{ type: "text/plain" }` - [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#parameters) */ options?: BlobPropertyBag | undefined;
8
+ /** array of `BlobParts` to copy - [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#parameters) */ blobParts?: BlobPart[] | undefined;
9
+ /** use a blob in instead of `blobParts` and `options`, defaults to `new Blob(blobParts, options)` */ blob?: Blob | undefined;
8
10
  /** `noscript` class */ classNoscript?: string | undefined;
9
11
  };
10
12
  events: {
@@ -21,14 +23,16 @@ export type CopyButtonSlots = typeof __propDef.slots;
21
23
  /**
22
24
  * ### CopyButton
23
25
  *
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.
26
+ * Uses the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard) to copy data to the clipboard. Falls back to `writeText` if `write` is not supported and `blob.type` is text. If JavaScript is disabled, the button is disabled and `blobParts.join()` is displayed after the button if `blob.type` is text.
25
27
  *
26
28
  * @props
27
29
  *
30
+ * - `blobParts` - array of `BlobParts` to copy - [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#parameters)
31
+ * - `blob` - use a blob in instead of `blobParts` and `options`, defaults to `new Blob(blobParts, options)`
28
32
  * - `classNoscript` - `noscript` class
29
33
  * - `class`
30
34
  * - `id`
31
- * - `text` - text to copy
35
+ * - `options` - defaults to `{ type: "text/plain" }` - [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#parameters)
32
36
  * - `title`
33
37
  *
34
38
  * @slots
@@ -45,7 +49,7 @@ export type CopyButtonSlots = typeof __propDef.slots;
45
49
  * import { CopyButton } from "drab";
46
50
  * </script>
47
51
  *
48
- * <CopyButton class="btn" text="Text to copy" />
52
+ * <CopyButton class="btn" blobParts={["Text to copy"]} />
49
53
  * ```
50
54
  */
51
55
  export default class CopyButton extends SvelteComponent<CopyButtonProps, CopyButtonEvents, CopyButtonSlots> {