drab 2.1.1 → 2.2.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/dist/components/Accordion.svelte +164 -0
- package/dist/components/Accordion.svelte.d.ts +86 -0
- package/dist/components/ContextMenu.svelte +1 -0
- package/dist/components/Editor.svelte.d.ts +1 -1
- package/dist/components/Popover.svelte +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/util/accessibility.d.ts +6 -0
- package/dist/util/accessibility.js +11 -0
- package/package.json +3 -2
- package/dist/util/buildDocs.d.ts +0 -1
- package/dist/util/buildDocs.js +0 -3
- package/dist/util/documentProps.d.ts +0 -2
- package/dist/util/documentProps.js +0 -86
@@ -0,0 +1,164 @@
|
|
1
|
+
<!--
|
2
|
+
@component
|
3
|
+
|
4
|
+
### Accordion
|
5
|
+
|
6
|
+
Displays a list of `details` elements.
|
7
|
+
|
8
|
+
@props
|
9
|
+
|
10
|
+
- `autoClose` - if true, other items close when a new one is opened
|
11
|
+
- `classDetails` - class of the `div` around each `details` element
|
12
|
+
- `classIcon` - class of the `div` around the icon
|
13
|
+
- `classSlot` - class of all the `slot` elements
|
14
|
+
- `classSummary` - class of all the `summary` elements
|
15
|
+
- `class`
|
16
|
+
- `content` - array of `AccordionContent` elements
|
17
|
+
- `icon`
|
18
|
+
- `id`
|
19
|
+
- `transition` - rotates the icon, slides the content, defaults to empty object, set to false to remove
|
20
|
+
|
21
|
+
@slots
|
22
|
+
|
23
|
+
Pass components into the `content` prop if needed. `AccordionContent` has `summary` and `slot` attributes of type `string | ComponentType`.
|
24
|
+
|
25
|
+
@example
|
26
|
+
|
27
|
+
```svelte
|
28
|
+
<script>
|
29
|
+
import { Accordion } from "drab";
|
30
|
+
</script>
|
31
|
+
|
32
|
+
<Accordion
|
33
|
+
content={[
|
34
|
+
{ summary: "Is it accessible?", slot: "Yes." },
|
35
|
+
{
|
36
|
+
summary: "Is it styled?",
|
37
|
+
slot: "Nope, style with global styles.",
|
38
|
+
},
|
39
|
+
{
|
40
|
+
summary: "Is it animated?",
|
41
|
+
slot: "Yes, with the transition prop.",
|
42
|
+
},
|
43
|
+
{ summary: "Does it work without Javascript?", slot: "Yes." },
|
44
|
+
]}
|
45
|
+
/>
|
46
|
+
```
|
47
|
+
-->
|
48
|
+
|
49
|
+
<script context="module"></script>
|
50
|
+
|
51
|
+
<script>import { onMount } from "svelte";
|
52
|
+
import { slide } from "svelte/transition";
|
53
|
+
import { prefersReducedMotion } from "../util/accessibility";
|
54
|
+
let className = "";
|
55
|
+
export { className as class };
|
56
|
+
export let id = "";
|
57
|
+
export let content;
|
58
|
+
export let icon = "";
|
59
|
+
export let classDetails = "";
|
60
|
+
export let classSummary = "";
|
61
|
+
export let classSlot = "";
|
62
|
+
export let classIcon = "";
|
63
|
+
export let transition = {};
|
64
|
+
export let autoClose = true;
|
65
|
+
let clientJs = false;
|
66
|
+
for (const item of content) {
|
67
|
+
if (!item.classContentDetails)
|
68
|
+
item.classContentDetails = "";
|
69
|
+
if (!item.classContentSlot)
|
70
|
+
item.classContentSlot = "";
|
71
|
+
if (!item.classContentSummary)
|
72
|
+
item.classContentSummary = "";
|
73
|
+
}
|
74
|
+
const toggleOpen = (i) => {
|
75
|
+
content[i].open = !content[i].open;
|
76
|
+
if (autoClose) {
|
77
|
+
for (let j = 0; j < content.length; j++) {
|
78
|
+
const item = content[j];
|
79
|
+
if (j !== i)
|
80
|
+
item.open = false;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
};
|
84
|
+
onMount(() => {
|
85
|
+
clientJs = true;
|
86
|
+
if (prefersReducedMotion())
|
87
|
+
transition = false;
|
88
|
+
});
|
89
|
+
</script>
|
90
|
+
|
91
|
+
<div class="transition"></div>
|
92
|
+
|
93
|
+
<div class={className} {id}>
|
94
|
+
{#each content as { classContentDetails, summary, classContentSummary, slot, classContentSlot, open }, i}
|
95
|
+
<div class="{classDetails} {classContentDetails}">
|
96
|
+
<details bind:open>
|
97
|
+
<!-- svelte-ignore a11y-no-redundant-roles -->
|
98
|
+
<summary
|
99
|
+
role="button"
|
100
|
+
tabindex="0"
|
101
|
+
class={classSummary}
|
102
|
+
on:click={(e) => {
|
103
|
+
e.preventDefault();
|
104
|
+
toggleOpen(i);
|
105
|
+
}}
|
106
|
+
on:keydown={(e) => {
|
107
|
+
if (e.key === "Enter") {
|
108
|
+
e.preventDefault();
|
109
|
+
toggleOpen(i);
|
110
|
+
}
|
111
|
+
}}
|
112
|
+
>
|
113
|
+
{#if typeof summary !== "string"}
|
114
|
+
<svelte:component this={summary} class={classContentSummary} />
|
115
|
+
{:else}
|
116
|
+
<span class={classContentSummary}>{summary}</span>
|
117
|
+
{/if}
|
118
|
+
{#if icon}
|
119
|
+
<div
|
120
|
+
class={classIcon}
|
121
|
+
class:db-rotate-180={open}
|
122
|
+
class:db-transition={transition}
|
123
|
+
>
|
124
|
+
{#if typeof icon !== "string"}
|
125
|
+
<svelte:component this={icon} />
|
126
|
+
{:else}
|
127
|
+
<span>{icon}</span>
|
128
|
+
{/if}
|
129
|
+
</div>
|
130
|
+
{/if}
|
131
|
+
</summary>
|
132
|
+
{#if !clientJs || !transition}
|
133
|
+
<div class={classSlot}>
|
134
|
+
{#if typeof slot !== "string"}
|
135
|
+
<svelte:component this={slot} class={classContentSlot} />
|
136
|
+
{:else}
|
137
|
+
<div class={classContentSlot}>{slot}</div>
|
138
|
+
{/if}
|
139
|
+
</div>
|
140
|
+
{/if}
|
141
|
+
</details>
|
142
|
+
{#if clientJs && open && transition}
|
143
|
+
<div transition:slide={transition} class={classSlot}>
|
144
|
+
{#if typeof slot !== "string"}
|
145
|
+
<svelte:component this={slot} class={classContentSlot} />
|
146
|
+
{:else}
|
147
|
+
<div class={classContentSlot}>{slot}</div>
|
148
|
+
{/if}
|
149
|
+
</div>
|
150
|
+
{/if}
|
151
|
+
</div>
|
152
|
+
{/each}
|
153
|
+
</div>
|
154
|
+
|
155
|
+
<style>
|
156
|
+
.db-rotate-180 {
|
157
|
+
transform: rotate(180deg);
|
158
|
+
}
|
159
|
+
.db-transition {
|
160
|
+
transition-property: transform;
|
161
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
162
|
+
transition-duration: 150ms;
|
163
|
+
}
|
164
|
+
</style>
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
import type { ComponentType } from "svelte";
|
3
|
+
export interface AccordionContent {
|
4
|
+
/** `details` element class */
|
5
|
+
classContentDetails?: string;
|
6
|
+
/** content of the `summary` element */
|
7
|
+
summary: string | ComponentType;
|
8
|
+
/** `summary` element class */
|
9
|
+
classContentSummary?: string;
|
10
|
+
/** content of the `slot` */
|
11
|
+
slot: string | ComponentType;
|
12
|
+
/** `slot` element class */
|
13
|
+
classContentSlot?: string;
|
14
|
+
/** controls whether the slotted content is displayed */
|
15
|
+
open?: boolean;
|
16
|
+
}
|
17
|
+
import { type SlideParams } from "svelte/transition";
|
18
|
+
declare const __propDef: {
|
19
|
+
props: {
|
20
|
+
class?: string | undefined;
|
21
|
+
id?: string | undefined;
|
22
|
+
/** array of `AccordionContent` elements */ content: AccordionContent[];
|
23
|
+
icon?: string | ComponentType | undefined;
|
24
|
+
/** class of the `div` around each `details` element */ classDetails?: string | undefined;
|
25
|
+
/** class of all the `summary` elements */ classSummary?: string | undefined;
|
26
|
+
/** class of all the `slot` elements */ classSlot?: string | undefined;
|
27
|
+
/** class of the `div` around the icon */ classIcon?: string | undefined;
|
28
|
+
/** rotates the icon, slides the content, defaults to empty object, set to false to remove */ transition?: false | SlideParams | undefined;
|
29
|
+
/** if true, other items close when a new one is opened */ autoClose?: boolean | undefined;
|
30
|
+
};
|
31
|
+
events: {
|
32
|
+
[evt: string]: CustomEvent<any>;
|
33
|
+
};
|
34
|
+
slots: {};
|
35
|
+
};
|
36
|
+
export type AccordionProps = typeof __propDef.props;
|
37
|
+
export type AccordionEvents = typeof __propDef.events;
|
38
|
+
export type AccordionSlots = typeof __propDef.slots;
|
39
|
+
/**
|
40
|
+
* ### Accordion
|
41
|
+
*
|
42
|
+
* Displays a list of `details` elements.
|
43
|
+
*
|
44
|
+
* @props
|
45
|
+
*
|
46
|
+
* - `autoClose` - if true, other items close when a new one is opened
|
47
|
+
* - `classDetails` - class of the `div` around each `details` element
|
48
|
+
* - `classIcon` - class of the `div` around the icon
|
49
|
+
* - `classSlot` - class of all the `slot` elements
|
50
|
+
* - `classSummary` - class of all the `summary` elements
|
51
|
+
* - `class`
|
52
|
+
* - `content` - array of `AccordionContent` elements
|
53
|
+
* - `icon`
|
54
|
+
* - `id`
|
55
|
+
* - `transition` - rotates the icon, slides the content, defaults to empty object, set to false to remove
|
56
|
+
*
|
57
|
+
* @slots
|
58
|
+
*
|
59
|
+
* Pass components into the `content` prop if needed. `AccordionContent` has `summary` and `slot` attributes of type `string | ComponentType`.
|
60
|
+
*
|
61
|
+
* @example
|
62
|
+
*
|
63
|
+
* ```svelte
|
64
|
+
* <script>
|
65
|
+
* import { Accordion } from "drab";
|
66
|
+
* </script>
|
67
|
+
*
|
68
|
+
* <Accordion
|
69
|
+
* content={[
|
70
|
+
* { summary: "Is it accessible?", slot: "Yes." },
|
71
|
+
* {
|
72
|
+
* summary: "Is it styled?",
|
73
|
+
* slot: "Nope, style with global styles.",
|
74
|
+
* },
|
75
|
+
* {
|
76
|
+
* summary: "Is it animated?",
|
77
|
+
* slot: "Yes, with the transition prop.",
|
78
|
+
* },
|
79
|
+
* { summary: "Does it work without Javascript?", slot: "Yes." },
|
80
|
+
* ]}
|
81
|
+
* />
|
82
|
+
* ```
|
83
|
+
*/
|
84
|
+
export default class Accordion extends SvelteComponent<AccordionProps, AccordionEvents, AccordionSlots> {
|
85
|
+
}
|
86
|
+
export {};
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
2
|
+
import type { ComponentType } from "svelte";
|
2
3
|
/**
|
3
4
|
* - `EditorContentElement` to pass into the `contentElements` array prop
|
4
5
|
* - `contentElements` prop creates a list of button controls to insert
|
@@ -18,7 +19,6 @@ export interface EditorContentElement {
|
|
18
19
|
/** class to apply to the specific button */
|
19
20
|
class?: string;
|
20
21
|
}
|
21
|
-
import { type ComponentType } from "svelte";
|
22
22
|
declare const __propDef: {
|
23
23
|
props: {
|
24
24
|
/** an array of content elements for the controls */ contentElements?: EditorContentElement[] | undefined;
|
package/dist/index.d.ts
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
import Accordion from "./components/Accordion.svelte";
|
2
|
+
import type { AccordionContent } from "./components/Accordion.svelte";
|
1
3
|
import Chord from "./components/Chord.svelte";
|
2
4
|
import type { ChordNote } from "./components/Chord.svelte";
|
3
5
|
import ContextMenu from "./components/ContextMenu.svelte";
|
@@ -10,4 +12,4 @@ import FullscreenButton from "./components/FullscreenButton.svelte";
|
|
10
12
|
import Popover from "./components/Popover.svelte";
|
11
13
|
import ShareButton from "./components/ShareButton.svelte";
|
12
14
|
import YouTube from "./components/YouTube.svelte";
|
13
|
-
export { Chord, type ChordNote, ContextMenu, CopyButton, DataTable, type DataTableRow, Editor, type EditorContentElement, FullscreenButton, Popover, ShareButton, YouTube, };
|
15
|
+
export { Accordion, type AccordionContent, Chord, type ChordNote, ContextMenu, CopyButton, DataTable, type DataTableRow, Editor, type EditorContentElement, FullscreenButton, Popover, ShareButton, YouTube, };
|
package/dist/index.js
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import Accordion from "./components/Accordion.svelte";
|
1
2
|
import Chord from "./components/Chord.svelte";
|
2
3
|
import ContextMenu from "./components/ContextMenu.svelte";
|
3
4
|
import CopyButton from "./components/CopyButton.svelte";
|
@@ -7,4 +8,4 @@ import FullscreenButton from "./components/FullscreenButton.svelte";
|
|
7
8
|
import Popover from "./components/Popover.svelte";
|
8
9
|
import ShareButton from "./components/ShareButton.svelte";
|
9
10
|
import YouTube from "./components/YouTube.svelte";
|
10
|
-
export { Chord, ContextMenu, CopyButton, DataTable, Editor, FullscreenButton, Popover, ShareButton, YouTube, };
|
11
|
+
export { Accordion, Chord, ContextMenu, CopyButton, DataTable, Editor, FullscreenButton, Popover, ShareButton, YouTube, };
|
@@ -0,0 +1,11 @@
|
|
1
|
+
/**
|
2
|
+
* Checks to see if the user prefers reduced motion
|
3
|
+
*
|
4
|
+
* @returns `true` if the user prefers reduced motion
|
5
|
+
*/
|
6
|
+
export const prefersReducedMotion = () => {
|
7
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
8
|
+
if (mediaQuery.matches)
|
9
|
+
return true;
|
10
|
+
return false;
|
11
|
+
};
|
package/package.json
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "drab",
|
3
|
-
"version": "2.
|
3
|
+
"version": "2.2.0",
|
4
4
|
"description": "An unstyled Svelte component library",
|
5
5
|
"keywords": [
|
6
6
|
"components",
|
7
7
|
"Svelte",
|
8
8
|
"SvelteKit",
|
9
|
+
"Accordion",
|
9
10
|
"Chord",
|
10
11
|
"ContextMenu",
|
11
12
|
"Copy",
|
@@ -34,7 +35,7 @@
|
|
34
35
|
"lint": "prettier --check . && eslint .",
|
35
36
|
"format": "prettier --write . --plugin=prettier-plugin-svelte --plugin=prettier-plugin-tailwindcss",
|
36
37
|
"pub": "npm publish --access public",
|
37
|
-
"doc": "node
|
38
|
+
"doc": "node scripts/buildDocs.js"
|
38
39
|
},
|
39
40
|
"exports": {
|
40
41
|
".": {
|
package/dist/util/buildDocs.d.ts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export {};
|
package/dist/util/buildDocs.js
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
import fs from "fs/promises";
|
2
|
-
import path from "path";
|
3
|
-
|
4
|
-
/**
|
5
|
-
* Runs `documentProps` over a given directory
|
6
|
-
*
|
7
|
-
* @param {string} dir path to directory
|
8
|
-
*/
|
9
|
-
export const documentPropsDir = async (dir) => {
|
10
|
-
const files = await fs.readdir(dir);
|
11
|
-
for (const file of files) {
|
12
|
-
if (path.extname(file) === ".svelte") {
|
13
|
-
documentProps(path.join(dir, file));
|
14
|
-
}
|
15
|
-
}
|
16
|
-
};
|
17
|
-
|
18
|
-
/**
|
19
|
-
* - Finds "@props" in a component
|
20
|
-
* - If found, overwrites the docs with the generated docs
|
21
|
-
*
|
22
|
-
* @param {string} path path to component
|
23
|
-
*/
|
24
|
-
export const documentProps = async (path) => {
|
25
|
-
const code = await fs.readFile(path, "utf-8");
|
26
|
-
const lines = code.split("\n");
|
27
|
-
const docs = getPropDocs(lines);
|
28
|
-
for (let i = 0; i < lines.length; i++) {
|
29
|
-
const line = lines[i].trim();
|
30
|
-
if (line.startsWith("@props")) {
|
31
|
-
for (let j = i + 1; j < lines.length; j++) {
|
32
|
-
// for the following lines...
|
33
|
-
const checkLine = lines[j].trim();
|
34
|
-
if (
|
35
|
-
checkLine.startsWith("@") ||
|
36
|
-
checkLine.startsWith("-->") ||
|
37
|
-
checkLine.startsWith("#") ||
|
38
|
-
checkLine.startsWith("|")
|
39
|
-
) {
|
40
|
-
// delete current / add new
|
41
|
-
lines.splice(i + 1, j - i - 1, docs);
|
42
|
-
// join back together
|
43
|
-
const documented = lines.join("\n");
|
44
|
-
await fs.writeFile(path, documented);
|
45
|
-
console.log("Documented " + path);
|
46
|
-
break;
|
47
|
-
}
|
48
|
-
}
|
49
|
-
break;
|
50
|
-
}
|
51
|
-
if (i === lines.length - 1) {
|
52
|
-
// last line
|
53
|
-
console.log("No `@props` found for " + path);
|
54
|
-
}
|
55
|
-
}
|
56
|
-
};
|
57
|
-
|
58
|
-
/**
|
59
|
-
* - Finds the props and JSDoc comments
|
60
|
-
*
|
61
|
-
* @param {string[]} lines lines of code
|
62
|
-
*/
|
63
|
-
const getPropDocs = (lines) => {
|
64
|
-
const docs = ["\n"];
|
65
|
-
for (let i = 0; i < lines.length; i++) {
|
66
|
-
const line = lines[i].trim();
|
67
|
-
const isClass = line === "export { className as class };";
|
68
|
-
if (line.startsWith("export let ") || isClass) {
|
69
|
-
let propName = line.split(" ")[2];
|
70
|
-
if (isClass) {
|
71
|
-
propName = "class";
|
72
|
-
}
|
73
|
-
if (propName.endsWith(":")) propName = propName.slice(0, -1);
|
74
|
-
const previousLine = lines[i - 1].trim();
|
75
|
-
let jsDoc = "";
|
76
|
-
if (previousLine.endsWith("*/")) {
|
77
|
-
if (previousLine.startsWith("/**")) {
|
78
|
-
jsDoc = "- " + previousLine.slice(3, -2).trim();
|
79
|
-
}
|
80
|
-
}
|
81
|
-
docs.push(`- \`${propName}\` ${jsDoc}\n`);
|
82
|
-
}
|
83
|
-
}
|
84
|
-
docs.sort();
|
85
|
-
return docs.join("");
|
86
|
-
};
|