slexkit 0.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.
Files changed (221) hide show
  1. package/AGENTS.slexkit.md +29 -0
  2. package/CHANGELOG.md +90 -0
  3. package/LICENSE +21 -0
  4. package/README.md +165 -0
  5. package/README.zh-CN.md +165 -0
  6. package/dist/ai/llms-authoring.txt +44 -0
  7. package/dist/ai/llms-components.txt +669 -0
  8. package/dist/ai/llms-full.txt +6586 -0
  9. package/dist/ai/llms-runtime.txt +1475 -0
  10. package/dist/ai/llms-toolhost.txt +295 -0
  11. package/dist/ai/llms.txt +69 -0
  12. package/dist/ai/slexkit-ai-manifest.json +2922 -0
  13. package/dist/base.css +621 -0
  14. package/dist/chunks/accordion-5f0nvjjm.js +376 -0
  15. package/dist/chunks/accordion-830dw78f.js +221 -0
  16. package/dist/chunks/accordion-cfjyxw93.js +630 -0
  17. package/dist/chunks/accordion-cw5r75jm.js +424 -0
  18. package/dist/chunks/accordion-ehnhpeca.js +492 -0
  19. package/dist/chunks/accordion-hzyrngd6.js +2377 -0
  20. package/dist/chunks/accordion-nw12ytps.js +6823 -0
  21. package/dist/components/accordion.js +163 -0
  22. package/dist/components/badge.js +80 -0
  23. package/dist/components/button.css +114 -0
  24. package/dist/components/button.js +16 -0
  25. package/dist/components/callout.js +154 -0
  26. package/dist/components/card.js +95 -0
  27. package/dist/components/checkbox.js +114 -0
  28. package/dist/components/choice.css +165 -0
  29. package/dist/components/code-block.js +264 -0
  30. package/dist/components/collapsible.js +111 -0
  31. package/dist/components/column.js +49 -0
  32. package/dist/components/content.css +474 -0
  33. package/dist/components/disclosure.css +162 -0
  34. package/dist/components/display.css +259 -0
  35. package/dist/components/divider.js +98 -0
  36. package/dist/components/feedback.css +219 -0
  37. package/dist/components/grid.js +67 -0
  38. package/dist/components/index.js +13364 -0
  39. package/dist/components/input.css +1247 -0
  40. package/dist/components/input.js +384 -0
  41. package/dist/components/link.js +77 -0
  42. package/dist/components/progress.js +111 -0
  43. package/dist/components/radio-group.js +189 -0
  44. package/dist/components/row.js +200 -0
  45. package/dist/components/section.js +161 -0
  46. package/dist/components/select.css +260 -0
  47. package/dist/components/select.js +16 -0
  48. package/dist/components/slider.css +125 -0
  49. package/dist/components/slider.js +175 -0
  50. package/dist/components/specs.js +1090 -0
  51. package/dist/components/stat.js +178 -0
  52. package/dist/components/submit.css +9 -0
  53. package/dist/components/submit.js +77 -0
  54. package/dist/components/switch.css +114 -0
  55. package/dist/components/switch.js +114 -0
  56. package/dist/components/table.js +157 -0
  57. package/dist/components/tabs.css +192 -0
  58. package/dist/components/tabs.js +17 -0
  59. package/dist/components/text-input.css +245 -0
  60. package/dist/components/text.js +50 -0
  61. package/dist/components/toast.js +240 -0
  62. package/dist/components/tooling.css +1009 -0
  63. package/dist/components/tooling.js +48951 -0
  64. package/dist/runtime.cjs +3728 -0
  65. package/dist/runtime.js +3686 -0
  66. package/dist/slexkit.cjs +18539 -0
  67. package/dist/slexkit.css +4776 -0
  68. package/dist/slexkit.js +18497 -0
  69. package/dist/tooling.js +59141 -0
  70. package/dist/types/components/accordion.d.ts +2 -0
  71. package/dist/types/components/badge.d.ts +2 -0
  72. package/dist/types/components/button.d.ts +2 -0
  73. package/dist/types/components/callout.d.ts +2 -0
  74. package/dist/types/components/card.d.ts +2 -0
  75. package/dist/types/components/checkbox.d.ts +2 -0
  76. package/dist/types/components/code-block.d.ts +2 -0
  77. package/dist/types/components/collapsible.d.ts +2 -0
  78. package/dist/types/components/column.d.ts +2 -0
  79. package/dist/types/components/divider.d.ts +2 -0
  80. package/dist/types/components/entries/accordion.d.ts +3 -0
  81. package/dist/types/components/entries/badge.d.ts +3 -0
  82. package/dist/types/components/entries/button.d.ts +3 -0
  83. package/dist/types/components/entries/callout.d.ts +3 -0
  84. package/dist/types/components/entries/card.d.ts +3 -0
  85. package/dist/types/components/entries/checkbox.d.ts +3 -0
  86. package/dist/types/components/entries/code-block.d.ts +3 -0
  87. package/dist/types/components/entries/collapsible.d.ts +3 -0
  88. package/dist/types/components/entries/column.d.ts +3 -0
  89. package/dist/types/components/entries/divider.d.ts +3 -0
  90. package/dist/types/components/entries/grid.d.ts +3 -0
  91. package/dist/types/components/entries/input.d.ts +3 -0
  92. package/dist/types/components/entries/link.d.ts +3 -0
  93. package/dist/types/components/entries/progress.d.ts +3 -0
  94. package/dist/types/components/entries/radio-group.d.ts +3 -0
  95. package/dist/types/components/entries/row.d.ts +3 -0
  96. package/dist/types/components/entries/section.d.ts +3 -0
  97. package/dist/types/components/entries/select.d.ts +3 -0
  98. package/dist/types/components/entries/slider.d.ts +3 -0
  99. package/dist/types/components/entries/specs.d.ts +1 -0
  100. package/dist/types/components/entries/stat.d.ts +3 -0
  101. package/dist/types/components/entries/submit.d.ts +3 -0
  102. package/dist/types/components/entries/switch.d.ts +3 -0
  103. package/dist/types/components/entries/table.d.ts +3 -0
  104. package/dist/types/components/entries/tabs.d.ts +3 -0
  105. package/dist/types/components/entries/text.d.ts +3 -0
  106. package/dist/types/components/entries/toast.d.ts +3 -0
  107. package/dist/types/components/entries/tooling.d.ts +1 -0
  108. package/dist/types/components/grid.d.ts +2 -0
  109. package/dist/types/components/index.d.ts +6 -0
  110. package/dist/types/components/input.d.ts +2 -0
  111. package/dist/types/components/link.d.ts +2 -0
  112. package/dist/types/components/progress.d.ts +2 -0
  113. package/dist/types/components/radio-group.d.ts +2 -0
  114. package/dist/types/components/row.d.ts +2 -0
  115. package/dist/types/components/section.d.ts +2 -0
  116. package/dist/types/components/select.d.ts +2 -0
  117. package/dist/types/components/slider.d.ts +2 -0
  118. package/dist/types/components/spec-helpers.d.ts +23 -0
  119. package/dist/types/components/spec-registry.d.ts +12 -0
  120. package/dist/types/components/spec-schema.d.ts +74 -0
  121. package/dist/types/components/specs.d.ts +2 -0
  122. package/dist/types/components/stat.d.ts +2 -0
  123. package/dist/types/components/submit.d.ts +2 -0
  124. package/dist/types/components/svelte/adapter.d.ts +3 -0
  125. package/dist/types/components/svelte/bindProps.d.ts +2 -0
  126. package/dist/types/components/svelte/helpers.d.ts +33 -0
  127. package/dist/types/components/svelte/layout/balancedTiles.d.ts +14 -0
  128. package/dist/types/components/svelte/types.d.ts +12 -0
  129. package/dist/types/components/switch.d.ts +2 -0
  130. package/dist/types/components/table.d.ts +2 -0
  131. package/dist/types/components/tabs.d.ts +2 -0
  132. package/dist/types/components/text.d.ts +2 -0
  133. package/dist/types/components/toast.d.ts +2 -0
  134. package/dist/types/components/tooling.d.ts +2 -0
  135. package/dist/types/components-svelte.d.ts +5 -0
  136. package/dist/types/engine/component-scope.d.ts +14 -0
  137. package/dist/types/engine/component-state.d.ts +9 -0
  138. package/dist/types/engine/diagnostics.d.ts +24 -0
  139. package/dist/types/engine/engineering.d.ts +11 -0
  140. package/dist/types/engine/eval.d.ts +5 -0
  141. package/dist/types/engine/index.d.ts +26 -0
  142. package/dist/types/engine/markdown-runtime.d.ts +33 -0
  143. package/dist/types/engine/merge.d.ts +1 -0
  144. package/dist/types/engine/reactive.d.ts +11 -0
  145. package/dist/types/engine/registry.d.ts +4 -0
  146. package/dist/types/engine/renderer.d.ts +6 -0
  147. package/dist/types/engine/sandbox-runner.d.ts +2 -0
  148. package/dist/types/engine/secure-runtime.d.ts +214 -0
  149. package/dist/types/engine/store.d.ts +12 -0
  150. package/dist/types/engine/types.d.ts +58 -0
  151. package/dist/types/icons/manager.d.ts +17 -0
  152. package/dist/types/icons/phosphor.d.ts +45 -0
  153. package/dist/types/index.d.ts +61 -0
  154. package/dist/types/runtime.d.ts +32 -0
  155. package/dist/types/toolhost/index.d.ts +78 -0
  156. package/dist/types/tooling-umd.d.ts +47 -0
  157. package/dist/types/version.d.ts +8 -0
  158. package/dist/umd/slexkit.tooling.umd.js +66553 -0
  159. package/dist/umd/slexkit.umd.js +18552 -0
  160. package/package.json +136 -0
  161. package/scripts/cli.mjs +47 -0
  162. package/skills/slexkit/SKILL.md +27 -0
  163. package/skills/slexkit-author/SKILL.md +50 -0
  164. package/skills/slexkit-host-integration/SKILL.md +33 -0
  165. package/skills/slexkit-secure-runtime/SKILL.md +31 -0
  166. package/skills/slexkit-toolhost/SKILL.md +38 -0
  167. package/skills/slexkit-update/SKILL.md +23 -0
  168. package/src/components/svelte/InlineIcon.svelte +66 -0
  169. package/src/components/svelte/adapter.ts +76 -0
  170. package/src/components/svelte/bindProps.ts +9 -0
  171. package/src/components/svelte/content/Badge.svelte +19 -0
  172. package/src/components/svelte/content/Callout.svelte +57 -0
  173. package/src/components/svelte/content/CodeBlock.svelte +130 -0
  174. package/src/components/svelte/content/Divider.svelte +21 -0
  175. package/src/components/svelte/content/Link.svelte +21 -0
  176. package/src/components/svelte/content/Section.svelte +24 -0
  177. package/src/components/svelte/content/Table.svelte +44 -0
  178. package/src/components/svelte/disclosure/Accordion.svelte +100 -0
  179. package/src/components/svelte/disclosure/Collapsible.svelte +45 -0
  180. package/src/components/svelte/display/Stat.svelte +102 -0
  181. package/src/components/svelte/display/Text.svelte +11 -0
  182. package/src/components/svelte/feedback/Progress.svelte +34 -0
  183. package/src/components/svelte/feedback/Toast.svelte +105 -0
  184. package/src/components/svelte/helpers.ts +148 -0
  185. package/src/components/svelte/input/Button.svelte +78 -0
  186. package/src/components/svelte/input/Checkbox.svelte +52 -0
  187. package/src/components/svelte/input/Input.svelte +202 -0
  188. package/src/components/svelte/input/RadioGroup.svelte +71 -0
  189. package/src/components/svelte/input/Select.svelte +220 -0
  190. package/src/components/svelte/input/Slider.svelte +96 -0
  191. package/src/components/svelte/input/Submit.svelte +32 -0
  192. package/src/components/svelte/input/Switch.svelte +53 -0
  193. package/src/components/svelte/input/Tabs.svelte +188 -0
  194. package/src/components/svelte/layout/Card.svelte +17 -0
  195. package/src/components/svelte/layout/Column.svelte +15 -0
  196. package/src/components/svelte/layout/Grid.svelte +26 -0
  197. package/src/components/svelte/layout/Row.svelte +105 -0
  198. package/src/components/svelte/layout/balancedTiles.ts +85 -0
  199. package/src/components/svelte/tooling/CodeMirror.svelte +91 -0
  200. package/src/components/svelte/tooling/Playground.svelte +765 -0
  201. package/src/components/svelte/tooling/PlaygroundMarkdown.svelte +26 -0
  202. package/src/components/svelte/tooling/PlaygroundSlexCode.svelte +76 -0
  203. package/src/components/svelte/types.ts +17 -0
  204. package/src/styles/animation.css +98 -0
  205. package/src/styles/components/button.css +114 -0
  206. package/src/styles/components/choice.css +165 -0
  207. package/src/styles/components/select.css +260 -0
  208. package/src/styles/components/slider.css +125 -0
  209. package/src/styles/components/submit.css +9 -0
  210. package/src/styles/components/switch.css +114 -0
  211. package/src/styles/components/tabs.css +192 -0
  212. package/src/styles/components/text-input.css +245 -0
  213. package/src/styles/content.css +474 -0
  214. package/src/styles/disclosure.css +162 -0
  215. package/src/styles/display.css +259 -0
  216. package/src/styles/entry.css +34 -0
  217. package/src/styles/feedback.css +219 -0
  218. package/src/styles/input.css +8 -0
  219. package/src/styles/layout.css +365 -0
  220. package/src/styles/theme.css +31 -0
  221. package/src/styles/tooling.css +1009 -0
@@ -0,0 +1,130 @@
1
+ <script lang="ts">
2
+ import { bindPropStore } from "../bindProps";
3
+ import { text } from "../helpers";
4
+ import InlineIcon from "../InlineIcon.svelte";
5
+ import type { PropValues, SvelteComponentProps } from "../types";
6
+
7
+ type CodeToken = {
8
+ text: string;
9
+ kind?: "comment" | "keyword" | "literal" | "number" | "string";
10
+ };
11
+
12
+ const supportedLanguages = new Set([
13
+ "bash",
14
+ "css",
15
+ "html",
16
+ "javascript",
17
+ "js",
18
+ "json",
19
+ "markdown",
20
+ "md",
21
+ "python",
22
+ "py",
23
+ "sh",
24
+ "shell",
25
+ "sql",
26
+ "ts",
27
+ "tsx",
28
+ "typescript",
29
+ "xml",
30
+ "yaml",
31
+ "yml",
32
+ ]);
33
+
34
+ const keywordLanguages = new Set(["javascript", "js", "ts", "tsx", "typescript"]);
35
+ const keywords = new Set([
36
+ "as",
37
+ "async",
38
+ "await",
39
+ "break",
40
+ "case",
41
+ "catch",
42
+ "class",
43
+ "const",
44
+ "continue",
45
+ "default",
46
+ "do",
47
+ "else",
48
+ "export",
49
+ "extends",
50
+ "finally",
51
+ "for",
52
+ "from",
53
+ "function",
54
+ "if",
55
+ "import",
56
+ "in",
57
+ "interface",
58
+ "let",
59
+ "new",
60
+ "of",
61
+ "return",
62
+ "switch",
63
+ "throw",
64
+ "try",
65
+ "type",
66
+ "var",
67
+ "while",
68
+ ]);
69
+ const literals = new Set(["false", "null", "true", "undefined"]);
70
+ const tokenPattern = /\/\/[^\n]*|\/\*[\s\S]*?\*\/|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|\r\n|\r|\n|\b\d+(?:\.\d+)?\b|\b[$A-Z_a-z][$\w]*\b|./g;
71
+
72
+ let { props }: SvelteComponentProps = $props();
73
+ let p = $state<PropValues>({});
74
+ let code = $derived(text(p.code ?? p.content ?? p.source));
75
+ let languageName = $derived(text(p.language).trim().toLowerCase());
76
+ let languageClass = $derived(languageName === "js" ? "javascript" : languageName || "");
77
+ let lines = $derived(highlightCode(code, languageName) ?? plainCodeLines(code));
78
+ let highlighted = $derived(supportedLanguages.has(languageName));
79
+ let showLineNumbers = $derived(p.lineNumbers !== false && p.lineNumbers !== "false");
80
+ $effect(() => bindPropStore(props, (next) => (p = next)));
81
+
82
+ function highlightCode(source: string, language: string): CodeToken[][] | null {
83
+ if (!supportedLanguages.has(language)) return null;
84
+
85
+ const tokens = Array.from(source.matchAll(tokenPattern), ([value]) => {
86
+ if (value.startsWith("//") || value.startsWith("/*")) return { text: value, kind: "comment" };
87
+ if (/^["'`]/.test(value)) return { text: value, kind: "string" };
88
+ if (/^\d/.test(value)) return { text: value, kind: "number" };
89
+ if (literals.has(value)) return { text: value, kind: "literal" };
90
+ if (keywordLanguages.has(language) && keywords.has(value)) return { text: value, kind: "keyword" };
91
+ return { text: value };
92
+ });
93
+ return splitTokenLines(tokens);
94
+ }
95
+
96
+ function plainCodeLines(source: string): CodeToken[][] {
97
+ return splitTokenLines([{ text: source }]);
98
+ }
99
+
100
+ function splitTokenLines(tokens: CodeToken[]): CodeToken[][] {
101
+ const lines: CodeToken[][] = [[]];
102
+ for (const token of tokens) {
103
+ for (const part of token.text.split(/(\r\n|\r|\n)/)) {
104
+ if (!part) continue;
105
+ if (part === "\n" || part === "\r" || part === "\r\n") {
106
+ lines.push([]);
107
+ } else {
108
+ lines[lines.length - 1].push({ ...token, text: part });
109
+ }
110
+ }
111
+ }
112
+ return lines;
113
+ }
114
+ </script>
115
+
116
+ <figure class="slex-code-block">
117
+ {#if p.title || p.language || p.icon}
118
+ <figcaption class="slex-code-block-header">
119
+ <span class="slex-code-block-title">
120
+ {#if p.icon}<InlineIcon name={p.icon} className="slex-code-block-icon" />{/if}
121
+ {#if p.title}<span>{text(p.title)}</span>{/if}
122
+ </span>
123
+ {#if p.language}<span class="slex-code-block-language">{text(p.language)}</span>{/if}
124
+ </figcaption>
125
+ {/if}
126
+ <pre class="slex-code-block-pre"><code
127
+ class={`${highlighted ? "slex-code-highlight" : ""} slex-code-lines${p.language ? ` language-${highlighted ? languageClass : text(p.language)}` : ""}`.trim()}
128
+ data-line-numbers={showLineNumbers ? "true" : "false"}
129
+ >{#each lines as line}<span class="slex-code-line"><span class="slex-code-line-content">{#each line as token}{#if token.kind}<span class={`slex-code-token slex-code-token--${token.kind}`}>{token.text}</span>{:else}{token.text}{/if}{/each}</span></span>{/each}</code></pre>
130
+ </figure>
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import { bindPropStore } from "../bindProps";
3
+ import { text } from "../helpers";
4
+ import InlineIcon from "../InlineIcon.svelte";
5
+ import type { PropValues, SvelteComponentProps } from "../types";
6
+
7
+ let { props }: SvelteComponentProps = $props();
8
+ let p = $state<PropValues>({});
9
+ $effect(() => bindPropStore(props, (next) => (p = next)));
10
+ </script>
11
+
12
+ {#if p.label || p.icon}
13
+ <div class="slex-divider slex-divider--labeled" role="separator">
14
+ <span class="slex-divider-label">
15
+ {#if p.icon}<InlineIcon name={p.icon} className="slex-divider-icon" />{/if}
16
+ {#if p.label}<span>{text(p.label)}</span>{/if}
17
+ </span>
18
+ </div>
19
+ {:else}
20
+ <hr class="slex-divider" />
21
+ {/if}
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import { bindPropStore } from "../bindProps";
3
+ import { text } from "../helpers";
4
+ import InlineIcon from "../InlineIcon.svelte";
5
+ import type { PropValues, SvelteComponentProps } from "../types";
6
+
7
+ let { props }: SvelteComponentProps = $props();
8
+ let p = $state<PropValues>({});
9
+ $effect(() => bindPropStore(props, (next) => (p = next)));
10
+ </script>
11
+
12
+ <a
13
+ class="slex-link"
14
+ data-variant={text(p.variant)}
15
+ href={text(p.href, "#")}
16
+ target={text(p.target) || undefined}
17
+ rel={p.target === "_blank" ? "noreferrer" : undefined}
18
+ >
19
+ {#if p.icon}<InlineIcon name={p.icon} className="slex-link-icon" />{/if}
20
+ <span>{text(p.text ?? p.label ?? p.content ?? p.href)}</span>
21
+ </a>
@@ -0,0 +1,24 @@
1
+ <script lang="ts">
2
+ import { bindPropStore } from "../bindProps";
3
+ import { renderChildren, text } from "../helpers";
4
+ import InlineIcon from "../InlineIcon.svelte";
5
+ import type { PropValues, SvelteComponentProps } from "../types";
6
+
7
+ let { props, ctx, componentName }: SvelteComponentProps = $props();
8
+ let p = $state<PropValues>({});
9
+ $effect(() => bindPropStore(props, (next) => (p = next)));
10
+ </script>
11
+
12
+ <section class="slex-section" id={text(p.id ?? componentName) || undefined}>
13
+ {#if p.title || p.subtitle || p.eyebrow}
14
+ <header class="slex-section-header">
15
+ {#if p.eyebrow}<div class="slex-section-eyebrow">{text(p.eyebrow)}</div>{/if}
16
+ {#if p.title || p.icon}
17
+ <h2 class="slex-section-title">{#if p.icon}<InlineIcon name={p.icon} className="slex-section-icon" />{/if}{#if p.title}<span>{text(p.title)}</span>{/if}</h2>
18
+ {/if}
19
+ {#if p.subtitle}<p class="slex-section-subtitle">{text(p.subtitle)}</p>{/if}
20
+ {#if p.actionLabel}<a href={text(p.actionHref, "#")} class="slex-section-action">{text(p.actionLabel)}</a>{/if}
21
+ </header>
22
+ {/if}
23
+ <div class="slex-section-body" use:renderChildren={ctx}></div>
24
+ </section>
@@ -0,0 +1,44 @@
1
+ <script lang="ts">
2
+ import { bindPropStore } from "../bindProps";
3
+ import { readCell, readColumnLabel, readColumns, rows, text } from "../helpers";
4
+ import InlineIcon from "../InlineIcon.svelte";
5
+ import type { PropValues, SvelteComponentProps } from "../types";
6
+
7
+ let { props }: SvelteComponentProps = $props();
8
+ let p = $state<PropValues>({});
9
+ $effect(() => bindPropStore(props, (next) => (p = next)));
10
+ </script>
11
+
12
+ <div class="slex-table-wrap">
13
+ <table class="slex-table">
14
+ {#if readColumns(p.columns).length}
15
+ <thead>
16
+ <tr>
17
+ {#each rows(p.columns) as column, index}
18
+ <th scope="col">
19
+ <span class="slex-table-column-label">
20
+ {#if column && typeof column === "object" && "icon" in column}
21
+ <InlineIcon name={(column as Record<string, unknown>).icon} className="slex-table-column-icon" />
22
+ {/if}
23
+ <span>{readColumnLabel(column, readColumns(p.columns)[index] ?? "")}</span>
24
+ </span>
25
+ </th>
26
+ {/each}
27
+ </tr>
28
+ </thead>
29
+ {/if}
30
+ <tbody>
31
+ {#each rows(p.rows ?? p.items) as row}
32
+ <tr>
33
+ {#if readColumns(p.columns).length}
34
+ {#each readColumns(p.columns) as column}<td>{readCell(row, column)}</td>{/each}
35
+ {:else if Array.isArray(row)}
36
+ {#each row as cell}<td>{text(cell)}</td>{/each}
37
+ {:else}
38
+ <td>{text(row)}</td>
39
+ {/if}
40
+ </tr>
41
+ {/each}
42
+ </tbody>
43
+ </table>
44
+ </div>
@@ -0,0 +1,100 @@
1
+ <script lang="ts">
2
+ import { bindPropStore } from "../bindProps";
3
+ import { emit, list, text } from "../helpers";
4
+ import InlineIcon from "../InlineIcon.svelte";
5
+ import type { PropValues, SvelteComponentProps } from "../types";
6
+
7
+ let { props, ctx }: SvelteComponentProps = $props();
8
+ let p = $state<PropValues>({});
9
+ let value = $state<unknown>(undefined);
10
+ let openStates = $state<Record<string, boolean>>({});
11
+ const items = $derived(list(p.items));
12
+
13
+ function keyFor(item: Record<string, unknown>) {
14
+ return text(item.value ?? item.label);
15
+ }
16
+
17
+ function valueForKey(key: string) {
18
+ const item = items.find((entry) => keyFor(entry) === key);
19
+ return item ? (item.value ?? item.label) : undefined;
20
+ }
21
+
22
+ function selectedKeys(nextValue: unknown, multiple: boolean) {
23
+ if (multiple && Array.isArray(nextValue)) return nextValue.map((entry) => text(entry));
24
+ const selected = text(nextValue);
25
+ return selected ? [selected] : [];
26
+ }
27
+
28
+ function openValue(states = openStates) {
29
+ const keys = Object.keys(states).filter((key) => states[key]);
30
+ if (p.multiple) return keys.map((key) => valueForKey(key)).filter((entry) => entry !== undefined);
31
+ return keys.length ? valueForKey(keys[0]) : undefined;
32
+ }
33
+
34
+ $effect(() => bindPropStore(props, (next) => {
35
+ p = next;
36
+ value = next.value;
37
+ const selected = selectedKeys(next.value, next.multiple === true);
38
+ const nextOpenStates: Record<string, boolean> = {};
39
+ for (const item of list(next.items)) {
40
+ const key = keyFor(item);
41
+ nextOpenStates[key] = selected.includes(key);
42
+ }
43
+ openStates = nextOpenStates;
44
+ }));
45
+
46
+ function toggleItem(key: string) {
47
+ const open = !!openStates[key];
48
+ let nextStates: Record<string, boolean>;
49
+ if (p.multiple) {
50
+ nextStates = { ...openStates, [key]: !open };
51
+ } else {
52
+ nextStates = {};
53
+ for (const item of items) nextStates[keyFor(item)] = false;
54
+ nextStates[key] = !open;
55
+ }
56
+
57
+ openStates = nextStates;
58
+ const nextValue = openValue(nextStates);
59
+ value = nextValue;
60
+ emit(ctx, "change", nextValue);
61
+ }
62
+ </script>
63
+
64
+ <div class="slex-accordion" data-scope="accordion">
65
+ <div class="slex-accordion-inner">
66
+ {#each items as item}
67
+ {@const key = keyFor(item)}
68
+ {@const open = !!openStates[key]}
69
+ <section class="slex-accordion-item" data-state={open ? "open" : "closed"}>
70
+ <h3 class="slex-accordion-heading">
71
+ <button
72
+ type="button"
73
+ class="slex-accordion-trigger"
74
+ data-state={open ? "open" : "closed"}
75
+ aria-expanded={open}
76
+ aria-controls={`${ctx.id}-${key}-content`}
77
+ onclick={() => toggleItem(key)}
78
+ disabled={item.disabled === true}
79
+ >
80
+ <span class="slex-accordion-label">
81
+ {#if item.icon}<InlineIcon name={item.icon} selected={open} className="slex-accordion-icon" />{/if}
82
+ <span>{text(item.label)}</span>
83
+ </span>
84
+ <span class="slex-accordion-indicator" aria-hidden="true"></span>
85
+ </button>
86
+ </h3>
87
+ <div
88
+ id={`${ctx.id}-${key}-content`}
89
+ class="slex-accordion-content"
90
+ data-state={open ? "open" : "closed"}
91
+ aria-hidden={!open}
92
+ >
93
+ <div class="slex-accordion-content-inner">
94
+ {text(item.content)}
95
+ </div>
96
+ </div>
97
+ </section>
98
+ {/each}
99
+ </div>
100
+ </div>
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import { bindPropStore } from "../bindProps";
3
+ import { emit, label, renderChildren, text } from "../helpers";
4
+ import InlineIcon from "../InlineIcon.svelte";
5
+ import type { PropValues, SvelteComponentProps } from "../types";
6
+
7
+ let { props, ctx }: SvelteComponentProps = $props();
8
+ let p = $state<PropValues>({});
9
+ let open = $state(false);
10
+ $effect(() => bindPropStore(props, (next) => {
11
+ p = next;
12
+ open = !!(next.open ?? next.value);
13
+ }));
14
+
15
+ function toggle(): void {
16
+ open = !open;
17
+ emit(ctx, open ? "open" : "close", open);
18
+ emit(ctx, "change", open);
19
+ }
20
+ </script>
21
+
22
+ <div class="slex-collapsible" data-state={open ? "open" : "closed"}>
23
+ <button
24
+ type="button"
25
+ class="slex-collapsible-trigger"
26
+ data-state={open ? "open" : "closed"}
27
+ aria-expanded={open}
28
+ aria-controls={`${ctx.id}-content`}
29
+ onclick={toggle}
30
+ >
31
+ <span class="slex-collapsible-label">
32
+ {#if p.icon}<InlineIcon name={p.icon} selected={open} className="slex-collapsible-icon" />{/if}
33
+ <span>{label(ctx, "collapsible.trigger", p.trigger, "Toggle")}</span>
34
+ </span>
35
+ <span class="slex-collapsible-indicator" aria-hidden="true"></span>
36
+ </button>
37
+ <div
38
+ id={`${ctx.id}-content`}
39
+ class="slex-collapsible-content"
40
+ data-state={open ? "open" : "closed"}
41
+ aria-hidden={!open}
42
+ >
43
+ <div class="slex-collapsible-content-inner" use:renderChildren={ctx}>{text(p.content)}</div>
44
+ </div>
45
+ </div>
@@ -0,0 +1,102 @@
1
+ <script lang="ts">
2
+ import { bindPropStore } from "../bindProps";
3
+ import { text } from "../helpers";
4
+ import InlineIcon from "../InlineIcon.svelte";
5
+ import type { PropValues, SvelteComponentProps } from "../types";
6
+
7
+ let { props, componentName }: SvelteComponentProps = $props();
8
+ let p = $state<PropValues>({});
9
+ let previousDisplayValue = $state("");
10
+ let currentDisplayValue = $state("");
11
+ let valueChange = $state<"up" | "down" | "changed">("changed");
12
+ let hasDisplayValue = false;
13
+ let shouldAnimateValue = $state(false);
14
+ let valueAnimationPhase = $state<"initial" | "update">("update");
15
+ const valueText = $derived(text(p.value));
16
+ const valueSegments = $derived(statSegments(previousDisplayValue, currentDisplayValue));
17
+
18
+ $effect(() => bindPropStore(props, (next) => (p = next)));
19
+
20
+ function numericValue(value: string): number | null {
21
+ const parsed = Number.parseFloat(value.replaceAll(",", ""));
22
+ return Number.isFinite(parsed) ? parsed : null;
23
+ }
24
+
25
+ function changeDirection(previous: string, next: string): "up" | "down" | "changed" {
26
+ const previousNumber = numericValue(previous);
27
+ const nextNumber = numericValue(next);
28
+ if (previousNumber === null || nextNumber === null) return "changed";
29
+ if (nextNumber > previousNumber) return "up";
30
+ if (nextNumber < previousNumber) return "down";
31
+ return "changed";
32
+ }
33
+
34
+ function booleanProp(value: unknown): boolean {
35
+ return value === true || value === "true" || value === 1 || value === "1";
36
+ }
37
+
38
+ function initialPreviousValue(value: string): string {
39
+ return Array.from(value).map((char) => isDigit(char) ? "0" : char).join("");
40
+ }
41
+
42
+ $effect(() => {
43
+ const next = valueText;
44
+ if (!hasDisplayValue) {
45
+ shouldAnimateValue = booleanProp(p.animateInitial);
46
+ previousDisplayValue = shouldAnimateValue ? initialPreviousValue(next) : next;
47
+ currentDisplayValue = next;
48
+ valueChange = "up";
49
+ valueAnimationPhase = "initial";
50
+ hasDisplayValue = true;
51
+ return;
52
+ }
53
+ if (next === currentDisplayValue) return;
54
+ valueChange = changeDirection(currentDisplayValue, next);
55
+ previousDisplayValue = currentDisplayValue;
56
+ currentDisplayValue = next;
57
+ shouldAnimateValue = true;
58
+ valueAnimationPhase = "update";
59
+ });
60
+
61
+ function isDigit(char: string): boolean {
62
+ return /^[0-9]$/.test(char);
63
+ }
64
+
65
+ function statSegments(previous: string, current: string) {
66
+ const previousChars = Array.from(previous);
67
+ const currentChars = Array.from(current);
68
+ const currentOffset = Math.max(0, currentChars.length - previousChars.length);
69
+ const previousOffset = Math.max(0, previousChars.length - currentChars.length);
70
+ return currentChars.map((char, index) => {
71
+ const previousChar = previousChars[index - currentOffset + previousOffset] ?? "";
72
+ return {
73
+ char,
74
+ previousChar,
75
+ changed: previousChar !== char,
76
+ key: `${index}:${char}`,
77
+ kind: isDigit(char) ? "digit" : "symbol",
78
+ };
79
+ });
80
+ }
81
+ </script>
82
+
83
+ <div class="slex-stat" data-tone={p.tone || p.type ? text(p.tone ?? p.type) : undefined}>
84
+ <div class="slex-stat-label">
85
+ {#if p.icon}<InlineIcon name={p.icon} className="slex-stat-icon" />{/if}
86
+ <span>{text(p.label ?? componentName)}</span>
87
+ </div>
88
+ <div class="slex-stat-value">
89
+ <span class="slex-stat-number">
90
+ {#each valueSegments as segment (segment.key)}
91
+ <span
92
+ class="slex-stat-character"
93
+ data-stat-kind={segment.kind}
94
+ data-stat-change={segment.changed && shouldAnimateValue ? valueChange : undefined}
95
+ data-stat-initial={segment.changed && shouldAnimateValue && valueAnimationPhase === "initial" ? "true" : undefined}
96
+ data-stat-previous={segment.previousChar || undefined}
97
+ ><span class="slex-stat-character-current">{segment.char}</span></span>
98
+ {/each}
99
+ </span>
100
+ {#if p.unit}<span class="slex-stat-unit">{text(p.unit)}</span>{/if}
101
+ </div>
102
+ </div>
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ import { bindPropStore } from "../bindProps";
3
+ import { text } from "../helpers";
4
+ import type { PropValues, SvelteComponentProps } from "../types";
5
+
6
+ let { props }: SvelteComponentProps = $props();
7
+ let p = $state<PropValues>({});
8
+ $effect(() => bindPropStore(props, (next) => (p = next)));
9
+ </script>
10
+
11
+ <div class={`slex-text${p.variant ? ` slex-text--${text(p.variant)}` : ""}${p.class ? ` ${text(p.class)}` : ""}`}>{text(p.content ?? p.text ?? p.label)}</div>
@@ -0,0 +1,34 @@
1
+ <script lang="ts">
2
+ import { bindPropStore } from "../bindProps";
3
+ import { text } from "../helpers";
4
+ import InlineIcon from "../InlineIcon.svelte";
5
+ import type { PropValues, SvelteComponentProps } from "../types";
6
+
7
+ let { props }: SvelteComponentProps = $props();
8
+ let p = $state<PropValues>({});
9
+ $effect(() => bindPropStore(props, (next) => (p = next)));
10
+
11
+ function value(): number {
12
+ const next = Number(p.value ?? 0);
13
+ return Number.isFinite(next) ? Math.min(100, Math.max(0, next)) : 0;
14
+ }
15
+ </script>
16
+
17
+ <div
18
+ class="slex-progress"
19
+ data-scope="progress"
20
+ data-state={p.indeterminate ? "indeterminate" : "determinate"}
21
+ role="progressbar"
22
+ aria-label={text(p["aria-label"] ?? p.ariaLabel ?? p.label) || undefined}
23
+ aria-valuemin={p.indeterminate ? undefined : 0}
24
+ aria-valuemax={p.indeterminate ? undefined : 100}
25
+ aria-valuenow={p.indeterminate ? undefined : value()}
26
+ >
27
+ {#if p.label || p.icon}
28
+ <div class="slex-progress-label">
29
+ {#if p.icon}<InlineIcon name={p.icon} className="slex-progress-icon" />{/if}
30
+ {#if p.label}<span>{text(p.label)}</span>{/if}
31
+ </div>
32
+ {/if}
33
+ <div class="slex-progress-track"><div class="slex-progress-range" style:width={`${value()}%`}></div></div>
34
+ </div>
@@ -0,0 +1,105 @@
1
+ <script lang="ts">
2
+ import FlowbiteToast from "../../../../node_modules/flowbite-svelte/dist/toast/Toast.svelte";
3
+ import { bindPropStore } from "../bindProps";
4
+ import { bool, emit, label, text } from "../helpers";
5
+ import InlineIcon from "../InlineIcon.svelte";
6
+ import type { PropValues, SvelteComponentProps } from "../types";
7
+
8
+ let { props, ctx }: SvelteComponentProps = $props();
9
+ let p = $state<PropValues>({});
10
+ let visible = $state(true);
11
+ $effect(() => bindPropStore(props, (next) => (p = next)));
12
+
13
+ $effect(() => {
14
+ const title = titleText();
15
+ const description = descriptionText();
16
+ const type = tone();
17
+ if (!title && !description && !type) return;
18
+
19
+ visible = true;
20
+ const ms = durationMs();
21
+ if (ms <= 0) return;
22
+
23
+ const timer = setTimeout(() => {
24
+ visible = false;
25
+ }, ms);
26
+ return () => clearTimeout(timer);
27
+ });
28
+
29
+ function tone(): string {
30
+ const raw = text(p.type ?? p.tone ?? "info").toLowerCase();
31
+ if (raw === "error" || raw === "destructive") return "danger";
32
+ if (raw === "neutral" || raw === "default") return "info";
33
+ return raw;
34
+ }
35
+
36
+ function titleText(): string {
37
+ return text(p.title ?? p.label ?? p.heading);
38
+ }
39
+
40
+ function descriptionText(): string {
41
+ return text(p.description ?? p.text ?? p.message ?? p.content);
42
+ }
43
+
44
+ function dismissable(): boolean {
45
+ if (p.dismissable === undefined && p.dismissible === undefined) return true;
46
+ return bool(p.dismissable ?? p.dismissible);
47
+ }
48
+
49
+ function durationMs(): number {
50
+ const value = Number(p.duration ?? 0);
51
+ return Number.isFinite(value) && value > 0 ? value : 0;
52
+ }
53
+
54
+ function close(): void {
55
+ if (!visible) return;
56
+ visible = false;
57
+ emit(ctx, "close", { type: "close", target: ctx.id });
58
+ }
59
+
60
+ function handleGroupClick(event: MouseEvent): void {
61
+ const target = event.target;
62
+ if (!(target instanceof Element)) return;
63
+ if (target.closest(".slex-toast-close")) close();
64
+ }
65
+
66
+ function role(): "alert" | "status" {
67
+ return tone() === "danger" ? "alert" : "status";
68
+ }
69
+
70
+ function color(): "blue" | "green" | "yellow" | "red" | "gray" {
71
+ const kind = tone();
72
+ if (kind === "success") return "green";
73
+ if (kind === "warning") return "yellow";
74
+ if (kind === "danger") return "red";
75
+ if (kind === "muted" || kind === "neutral") return "gray";
76
+ return "blue";
77
+ }
78
+
79
+ function closeLabel(): string {
80
+ return label(ctx, "toast.close", p.closeLabel ?? p.closeAriaLabel, "Close");
81
+ }
82
+ </script>
83
+
84
+ <div class="slex-toast-group" data-scope="toast" onclick={handleGroupClick}>
85
+ {#if visible}
86
+ <FlowbiteToast
87
+ bind:toastStatus={visible}
88
+ class="slex-toast"
89
+ data-tone={tone()}
90
+ color={color()}
91
+ role={role()}
92
+ aria-live={role() === "alert" ? "assertive" : "polite"}
93
+ dismissable={dismissable()}
94
+ closeAriaLabel={closeLabel()}
95
+ classes={{ icon: "slex-toast-mark", content: "slex-toast-content", close: "slex-toast-close" }}
96
+ onclose={close}
97
+ >
98
+ {#snippet icon()}
99
+ <span aria-hidden="true"></span>
100
+ {/snippet}
101
+ {#if titleText()}<div class="slex-toast-title">{#if p.icon}<InlineIcon name={p.icon} className="slex-toast-icon" />{/if}<span>{titleText()}</span></div>{/if}
102
+ {#if descriptionText()}<div class="slex-toast-description">{descriptionText()}</div>{/if}
103
+ </FlowbiteToast>
104
+ {/if}
105
+ </div>