@e280/shiny 0.1.0-15 → 0.1.0-16

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 (277) hide show
  1. package/README.md +34 -80
  2. package/package.json +18 -25
  3. package/s/_archive/demo/utils/lipsum.ts +19 -0
  4. package/s/_archive/demo/views/showcase/style.css.ts +53 -0
  5. package/s/_archive/demo/views/showcase/view.ts +54 -0
  6. package/s/_archive/index.html.ts +34 -0
  7. package/s/_archive/index.ts +19 -0
  8. package/s/_archive/utils/states.ts +15 -0
  9. package/s/demo/main.bundle.ts +16 -0
  10. package/s/demo/main.css +71 -0
  11. package/s/demo/parts/exhibit.ts +15 -0
  12. package/s/demo/utils/lipsum.ts +1 -1
  13. package/s/demo/views/codebox/style.css.ts +43 -0
  14. package/s/demo/views/codebox/use-prism-styles.ts +13 -0
  15. package/s/demo/views/codebox/view.ts +39 -0
  16. package/s/demo/views/showcase/style.css.ts +42 -11
  17. package/s/demo/views/showcase/view.ts +41 -45
  18. package/s/demo/views/stylebox/view.ts +21 -0
  19. package/s/index.html.ts +42 -29
  20. package/s/index.ts +1 -17
  21. package/s/test.ts +5 -0
  22. package/s/theme/parts/core.ts +34 -0
  23. package/s/theme/parts/reset.ts +20 -0
  24. package/s/theme/parts/vars.ts +41 -0
  25. package/s/theme/theme-string.ts +15 -0
  26. package/s/theme/theme.css.ts +7 -0
  27. package/s/views/button/showcase.ts +43 -0
  28. package/s/views/button/style.css.ts +110 -0
  29. package/s/views/button/view.ts +30 -0
  30. package/s/views/copy/parts/copy-status.ts +3 -0
  31. package/s/views/copy/parts/determine-base-status.ts +7 -0
  32. package/s/views/copy/parts/use-copier.ts +20 -0
  33. package/s/views/copy/showcase.ts +54 -0
  34. package/s/views/copy/style.css.ts +46 -0
  35. package/s/views/copy/view.ts +58 -0
  36. package/s/views/drawer/control.ts +31 -0
  37. package/s/views/drawer/showcase.ts +83 -0
  38. package/s/views/drawer/style.css.ts +128 -0
  39. package/s/views/drawer/view.ts +76 -0
  40. package/s/views/tabs/control.ts +31 -0
  41. package/s/views/tabs/showcase.ts +89 -0
  42. package/s/views/tabs/style.css.ts +46 -0
  43. package/s/views/tabs/view.ts +66 -0
  44. package/x/demo/main.bundle.js +13 -0
  45. package/x/demo/main.bundle.js.map +1 -0
  46. package/x/demo/main.bundle.min.js +794 -0
  47. package/x/demo/main.bundle.min.js.map +7 -0
  48. package/x/demo/main.css +71 -0
  49. package/x/demo/parts/exhibit.d.ts +11 -0
  50. package/x/demo/parts/exhibit.js +2 -0
  51. package/x/demo/parts/exhibit.js.map +1 -0
  52. package/x/demo/utils/lipsum.d.ts +1 -1
  53. package/x/demo/utils/lipsum.js +1 -1
  54. package/x/demo/utils/lipsum.js.map +1 -1
  55. package/x/demo/views/codebox/style.css.js +42 -0
  56. package/x/demo/views/codebox/style.css.js.map +1 -0
  57. package/x/demo/views/codebox/use-prism-styles.d.ts +1 -0
  58. package/x/demo/views/codebox/use-prism-styles.js +12 -0
  59. package/x/demo/views/codebox/use-prism-styles.js.map +1 -0
  60. package/x/demo/views/codebox/view.d.ts +2 -0
  61. package/x/demo/views/codebox/view.js +29 -0
  62. package/x/demo/views/codebox/view.js.map +1 -0
  63. package/x/demo/views/showcase/style.css.js +42 -11
  64. package/x/demo/views/showcase/style.css.js.map +1 -1
  65. package/x/demo/views/showcase/view.d.ts +2 -7
  66. package/x/demo/views/showcase/view.js +39 -35
  67. package/x/demo/views/showcase/view.js.map +1 -1
  68. package/x/demo/views/stylebox/view.d.ts +3 -0
  69. package/x/demo/views/stylebox/view.js +13 -0
  70. package/x/demo/views/stylebox/view.js.map +1 -0
  71. package/x/index.d.ts +1 -11
  72. package/x/index.html +212 -62
  73. package/x/index.html.js +40 -26
  74. package/x/index.html.js.map +1 -1
  75. package/x/index.js +1 -11
  76. package/x/index.js.map +1 -1
  77. package/x/test.js +3 -0
  78. package/x/test.js.map +1 -0
  79. package/x/theme/parts/core.d.ts +1 -0
  80. package/x/theme/parts/core.js +33 -0
  81. package/x/theme/parts/core.js.map +1 -0
  82. package/x/theme/parts/reset.d.ts +1 -0
  83. package/x/theme/parts/reset.js +19 -0
  84. package/x/theme/parts/reset.js.map +1 -0
  85. package/x/theme/parts/vars.d.ts +1 -0
  86. package/x/theme/parts/vars.js +34 -0
  87. package/x/theme/parts/vars.js.map +1 -0
  88. package/x/theme/theme-string.d.ts +1 -0
  89. package/x/theme/theme-string.js +14 -0
  90. package/x/theme/theme-string.js.map +1 -0
  91. package/x/theme/theme.css.d.ts +1 -0
  92. package/x/theme/theme.css.js +4 -0
  93. package/x/theme/theme.css.js.map +1 -0
  94. package/x/views/button/showcase.d.ts +1 -0
  95. package/x/views/button/showcase.js +41 -0
  96. package/x/views/button/showcase.js.map +1 -0
  97. package/x/views/button/style.css.js +109 -0
  98. package/x/views/button/style.css.js.map +1 -0
  99. package/x/views/button/view.d.ts +4 -0
  100. package/x/views/button/view.js +22 -0
  101. package/x/views/button/view.js.map +1 -0
  102. package/x/views/copy/parts/copy-status.d.ts +1 -0
  103. package/x/views/copy/parts/copy-status.js +2 -0
  104. package/x/views/copy/parts/copy-status.js.map +1 -0
  105. package/x/views/copy/parts/determine-base-status.d.ts +1 -0
  106. package/x/views/copy/parts/determine-base-status.js +6 -0
  107. package/x/views/copy/parts/determine-base-status.js.map +1 -0
  108. package/x/views/copy/parts/use-copier.d.ts +6 -0
  109. package/x/views/copy/parts/use-copier.js +13 -0
  110. package/x/views/copy/parts/use-copier.js.map +1 -0
  111. package/x/views/copy/showcase.d.ts +1 -0
  112. package/x/views/copy/showcase.js +51 -0
  113. package/x/views/copy/showcase.js.map +1 -0
  114. package/x/views/copy/style.css.js.map +1 -0
  115. package/x/views/copy/view.d.ts +4 -0
  116. package/x/views/copy/view.js +47 -0
  117. package/x/views/copy/view.js.map +1 -0
  118. package/x/{components → views}/drawer/control.d.ts +1 -1
  119. package/x/views/drawer/control.js.map +1 -0
  120. package/x/views/drawer/showcase.d.ts +1 -0
  121. package/x/views/drawer/showcase.js +75 -0
  122. package/x/views/drawer/showcase.js.map +1 -0
  123. package/x/{components → views}/drawer/style.css.js +1 -0
  124. package/x/views/drawer/style.css.js.map +1 -0
  125. package/x/views/drawer/view.d.ts +6 -0
  126. package/x/views/drawer/view.js +60 -0
  127. package/x/views/drawer/view.js.map +1 -0
  128. package/x/{components → views}/tabs/control.d.ts +2 -2
  129. package/x/{components → views}/tabs/control.js +3 -3
  130. package/x/views/tabs/control.js.map +1 -0
  131. package/x/views/tabs/showcase.d.ts +1 -0
  132. package/x/views/tabs/showcase.js +86 -0
  133. package/x/views/tabs/showcase.js.map +1 -0
  134. package/x/{components → views}/tabs/style.css.js +5 -5
  135. package/x/views/tabs/style.css.js.map +1 -0
  136. package/x/views/tabs/view.d.ts +5 -0
  137. package/x/views/tabs/view.js +52 -0
  138. package/x/views/tabs/view.js.map +1 -0
  139. package/x/components/button/component.d.ts +0 -6
  140. package/x/components/button/component.js +0 -25
  141. package/x/components/button/component.js.map +0 -1
  142. package/x/components/button/showcase.d.ts +0 -1
  143. package/x/components/button/showcase.js +0 -116
  144. package/x/components/button/showcase.js.map +0 -1
  145. package/x/components/button/style.css.js +0 -62
  146. package/x/components/button/style.css.js.map +0 -1
  147. package/x/components/copy/component.d.ts +0 -376
  148. package/x/components/copy/component.js +0 -63
  149. package/x/components/copy/component.js.map +0 -1
  150. package/x/components/copy/showcase.d.ts +0 -1
  151. package/x/components/copy/showcase.js +0 -48
  152. package/x/components/copy/showcase.js.map +0 -1
  153. package/x/components/copy/style.css.js.map +0 -1
  154. package/x/components/drawer/component.d.ts +0 -387
  155. package/x/components/drawer/component.js +0 -73
  156. package/x/components/drawer/component.js.map +0 -1
  157. package/x/components/drawer/control.js.map +0 -1
  158. package/x/components/drawer/showcase.d.ts +0 -1
  159. package/x/components/drawer/showcase.js +0 -105
  160. package/x/components/drawer/showcase.js.map +0 -1
  161. package/x/components/drawer/style.css.js.map +0 -1
  162. package/x/components/example/component.d.ts +0 -371
  163. package/x/components/example/component.js +0 -22
  164. package/x/components/example/component.js.map +0 -1
  165. package/x/components/example/showcase.d.ts +0 -1
  166. package/x/components/example/showcase.js +0 -30
  167. package/x/components/example/showcase.js.map +0 -1
  168. package/x/components/example/style.css.js +0 -10
  169. package/x/components/example/style.css.js.map +0 -1
  170. package/x/components/foundation.css.d.ts +0 -1
  171. package/x/components/foundation.css.js +0 -14
  172. package/x/components/foundation.css.js.map +0 -1
  173. package/x/components/framework.d.ts +0 -8
  174. package/x/components/framework.js +0 -5
  175. package/x/components/framework.js.map +0 -1
  176. package/x/components/raw-components.d.ts +0 -12
  177. package/x/components/raw-components.js +0 -13
  178. package/x/components/raw-components.js.map +0 -1
  179. package/x/components/tabs/component.d.ts +0 -374
  180. package/x/components/tabs/component.js +0 -60
  181. package/x/components/tabs/component.js.map +0 -1
  182. package/x/components/tabs/control.js.map +0 -1
  183. package/x/components/tabs/showcase.d.ts +0 -1
  184. package/x/components/tabs/showcase.js +0 -167
  185. package/x/components/tabs/showcase.js.map +0 -1
  186. package/x/components/tabs/style.css.js.map +0 -1
  187. package/x/demo/demo.bundle.js +0 -42
  188. package/x/demo/demo.bundle.js.map +0 -1
  189. package/x/demo/demo.bundle.min.js +0 -893
  190. package/x/demo/demo.bundle.min.js.map +0 -7
  191. package/x/demo/lipsum.d.ts +0 -2
  192. package/x/demo/lipsum.js +0 -4
  193. package/x/demo/lipsum.js.map +0 -1
  194. package/x/demo/views/exhibit/style.css.d.ts +0 -2
  195. package/x/demo/views/exhibit/style.css.js +0 -84
  196. package/x/demo/views/exhibit/style.css.js.map +0 -1
  197. package/x/demo/views/exhibit/view.d.ts +0 -29
  198. package/x/demo/views/exhibit/view.js +0 -40
  199. package/x/demo/views/exhibit/view.js.map +0 -1
  200. package/x/demo/viewsets.d.ts +0 -17
  201. package/x/demo/viewsets.js +0 -9
  202. package/x/demo/viewsets.js.map +0 -1
  203. package/x/install/aura.bundle.js +0 -5
  204. package/x/install/aura.bundle.js.map +0 -1
  205. package/x/install/aura.bundle.min.js +0 -471
  206. package/x/install/aura.bundle.min.js.map +0 -7
  207. package/x/install/plain.bundle.d.ts +0 -1
  208. package/x/install/plain.bundle.js +0 -5
  209. package/x/install/plain.bundle.js.map +0 -1
  210. package/x/install/plain.bundle.min.js +0 -398
  211. package/x/install/plain.bundle.min.js.map +0 -7
  212. package/x/shiny.d.ts +0 -47
  213. package/x/shiny.js +0 -8
  214. package/x/shiny.js.map +0 -1
  215. package/x/tests.test.d.ts +0 -1
  216. package/x/tests.test.js +0 -3
  217. package/x/tests.test.js.map +0 -1
  218. package/x/themes/aura.css.d.ts +0 -1
  219. package/x/themes/aura.css.js +0 -85
  220. package/x/themes/aura.css.js.map +0 -1
  221. package/x/themes/index.barrel.d.ts +0 -2
  222. package/x/themes/index.barrel.js +0 -3
  223. package/x/themes/index.barrel.js.map +0 -1
  224. package/x/themes/index.d.ts +0 -2
  225. package/x/themes/index.js +0 -2
  226. package/x/themes/index.js.map +0 -1
  227. package/x/themes/infra/css-vars.d.ts +0 -15
  228. package/x/themes/infra/css-vars.js +0 -22
  229. package/x/themes/infra/css-vars.js.map +0 -1
  230. package/x/themes/plain.css.d.ts +0 -1
  231. package/x/themes/plain.css.js +0 -10
  232. package/x/themes/plain.css.js.map +0 -1
  233. /package/s/{components → _archive/components}/button/component.ts +0 -0
  234. /package/s/{components → _archive/components}/button/showcase.ts +0 -0
  235. /package/s/{components → _archive/components}/button/style.css.ts +0 -0
  236. /package/s/{components → _archive/components}/copy/component.ts +0 -0
  237. /package/s/{components → _archive/components}/copy/showcase.ts +0 -0
  238. /package/s/{components → _archive/components}/copy/style.css.ts +0 -0
  239. /package/s/{components → _archive/components}/drawer/component.ts +0 -0
  240. /package/s/{components → _archive/components}/drawer/control.ts +0 -0
  241. /package/s/{components → _archive/components}/drawer/showcase.ts +0 -0
  242. /package/s/{components → _archive/components}/drawer/style.css.ts +0 -0
  243. /package/s/{components → _archive/components}/example/component.ts +0 -0
  244. /package/s/{components → _archive/components}/example/showcase.ts +0 -0
  245. /package/s/{components → _archive/components}/example/style.css.ts +0 -0
  246. /package/s/{components → _archive/components}/foundation.css.ts +0 -0
  247. /package/s/{components → _archive/components}/framework.ts +0 -0
  248. /package/s/{components → _archive/components}/raw-components.ts +0 -0
  249. /package/s/{components → _archive/components}/tabs/component.ts +0 -0
  250. /package/s/{components → _archive/components}/tabs/control.ts +0 -0
  251. /package/s/{components → _archive/components}/tabs/showcase.ts +0 -0
  252. /package/s/{components → _archive/components}/tabs/style.css.ts +0 -0
  253. /package/s/{demo → _archive/demo}/demo.bundle.ts +0 -0
  254. /package/s/{demo → _archive/demo}/demo.css +0 -0
  255. /package/s/{demo → _archive/demo}/lipsum.ts +0 -0
  256. /package/s/{demo → _archive/demo}/views/exhibit/style.css.ts +0 -0
  257. /package/s/{demo → _archive/demo}/views/exhibit/view.ts +0 -0
  258. /package/s/{demo → _archive/demo}/viewsets.ts +0 -0
  259. /package/s/{install → _archive/install}/aura.bundle.ts +0 -0
  260. /package/s/{install → _archive/install}/plain.bundle.ts +0 -0
  261. /package/s/{shiny.ts → _archive/shiny.ts} +0 -0
  262. /package/s/{tests.test.ts → _archive/tests.test.ts} +0 -0
  263. /package/s/{themes → _archive/themes}/aura.css.ts +0 -0
  264. /package/s/{themes → _archive/themes}/index.barrel.ts +0 -0
  265. /package/s/{themes → _archive/themes}/index.ts +0 -0
  266. /package/s/{themes → _archive/themes}/infra/css-vars.ts +0 -0
  267. /package/s/{themes → _archive/themes}/plain.css.ts +0 -0
  268. /package/x/{demo → _archive/demo}/demo.css +0 -0
  269. /package/x/demo/{demo.bundle.d.ts → main.bundle.d.ts} +0 -0
  270. /package/x/{components/button → demo/views/codebox}/style.css.d.ts +0 -0
  271. /package/x/{install/aura.bundle.d.ts → test.d.ts} +0 -0
  272. /package/x/{components/copy → views/button}/style.css.d.ts +0 -0
  273. /package/x/{components/drawer → views/copy}/style.css.d.ts +0 -0
  274. /package/x/{components → views}/copy/style.css.js +0 -0
  275. /package/x/{components → views}/drawer/control.js +0 -0
  276. /package/x/{components/example → views/drawer}/style.css.d.ts +0 -0
  277. /package/x/{components → views}/tabs/style.css.d.ts +0 -0
@@ -0,0 +1,54 @@
1
+
2
+ import {css, html} from "lit"
3
+ import {ShinyCopy} from "./view.js"
4
+ import {exhibit} from "../../demo/parts/exhibit.js"
5
+ import {Showcase} from "../../demo/views/showcase/view.js"
6
+
7
+ export const copyShowcase = () => Showcase("ShinyCopy", [
8
+ exhibit({
9
+ name: "normal",
10
+ explain: "click-to-copy button.",
11
+ render: () => ShinyCopy("hello world"),
12
+ styleboxCss: css`
13
+ :host {
14
+ font-size: 5em;
15
+ }
16
+ `,
17
+ js: `
18
+ ShinyCopy("hello world")
19
+ `,
20
+ css: css`
21
+ [view="shiny-copy"] {
22
+ --shiny-happy: #0fa;
23
+ --shiny-angry: #f50;
24
+ --shiny-lame: #8888;
25
+ --shiny-inactive-opacity: 0.5;
26
+ }
27
+ `,
28
+ }),
29
+
30
+ exhibit({
31
+ name: "fail",
32
+ explain: html`
33
+ <p>click-to-copy button. <em>deliberately fails so you can see.</em></p>
34
+ `,
35
+ render: () => ShinyCopy("hello world", {fail: true}),
36
+ styleboxCss: css`
37
+ :host {
38
+ font-size: 4em;
39
+ }
40
+ `,
41
+ js: `
42
+ ShinyCopy("hello world", {fail: true})
43
+ `,
44
+ css: css`
45
+ [view="shiny-copy"] {
46
+ --shiny-happy: #0fa;
47
+ --shiny-angry: #f50;
48
+ --shiny-lame: #8888;
49
+ --shiny-inactive-opacity: 0.5;
50
+ }
51
+ `,
52
+ }),
53
+ ])
54
+
@@ -0,0 +1,46 @@
1
+
2
+ import {css} from "lit"
3
+ export default css`@layer view {
4
+
5
+ button {
6
+ background: transparent;
7
+ border: none;
8
+ font-size: inherit;
9
+ }
10
+
11
+ button {
12
+ opacity: var(--inactive-opacity);
13
+ cursor: pointer;
14
+ cursor: copy;
15
+
16
+ color: inherit;
17
+ transition: all 300ms linear;
18
+
19
+ &:is(:hover, :focus-visible) {
20
+ opacity: 1;
21
+ }
22
+ }
23
+
24
+ [data-status="invalid"] {
25
+ color: var(--lame);
26
+ }
27
+
28
+ [data-status="good"] {
29
+ opacity: 1;
30
+ color: var(--happy);
31
+ filter: drop-shadow(0 0 0.3em color-mix(in oklab, transparent, currentColor 50%));
32
+ }
33
+
34
+ [data-status="bad"] {
35
+ opacity: 1;
36
+ color: var(--angry);
37
+ filter: drop-shadow(0 0 0.3em color-mix(in oklab, transparent, currentColor 50%));
38
+ }
39
+
40
+ svg {
41
+ width: 1em;
42
+ height: 1em;
43
+ }
44
+
45
+ }`
46
+
@@ -0,0 +1,58 @@
1
+
2
+ import {html} from "lit"
3
+ import {shadow, useCss, useName} from "@e280/sly"
4
+
5
+ import styleCss from "./style.css.js"
6
+ import clipboardSvg from "../../icons/tabler/clipboard.svg.js"
7
+ import clipboardXFilledSvg from "../../icons/tabler/clipboard-x-filled.svg.js"
8
+ import clipboardCheckFilledSvg from "../../icons/tabler/clipboard-check-filled.svg.js"
9
+
10
+ import {useCopier} from "./parts/use-copier.js"
11
+ import {themeCss} from "../../theme/theme.css.js"
12
+
13
+ export const ShinyCopy = shadow((
14
+ text: string | undefined,
15
+ options: {ms?: number, fail?: boolean} = {},
16
+ ) => {
17
+
18
+ useName("shiny-copy")
19
+ useCss(themeCss, styleCss)
20
+ const copier = useCopier(text, options.ms ?? 1000)
21
+
22
+ async function click() {
23
+ if (text === undefined) return
24
+ try {
25
+ if (options.fail)
26
+ throw new Error("copy failed on purpose for testing purposes")
27
+ await navigator.clipboard.writeText(text)
28
+ await copier.flash("good")
29
+ }
30
+ catch (error) {
31
+ console.error(error)
32
+ await copier.flash("bad")
33
+ }
34
+ }
35
+
36
+ return html`
37
+ <button data-status="${copier.status}" @click="${click}">
38
+ ${(() => {switch (copier.status) {
39
+ case "neutral":
40
+ return clipboardSvg
41
+
42
+ case "invalid":
43
+ return clipboardSvg
44
+
45
+ case "good":
46
+ return clipboardCheckFilledSvg
47
+
48
+ case "bad":
49
+ return clipboardXFilledSvg
50
+
51
+ default:
52
+ throw new Error(`unknown copy status`)
53
+ }})()}
54
+ <slot></slot>
55
+ </button>
56
+ `
57
+ })
58
+
@@ -0,0 +1,31 @@
1
+
2
+ import {signal} from "@e280/strata"
3
+
4
+ export class DrawerControl {
5
+ $open = signal(false)
6
+
7
+ constructor(startOpen = false) {
8
+ if (startOpen) this.$open.set(true)
9
+ }
10
+
11
+ get isOpen() {
12
+ return this.$open.get()
13
+ }
14
+
15
+ async setOpen(value: boolean) {
16
+ return this.$open.set(value)
17
+ }
18
+
19
+ open = async() => {
20
+ await this.setOpen(true)
21
+ }
22
+
23
+ close = async() => {
24
+ await this.setOpen(false)
25
+ }
26
+
27
+ toggle = async() => {
28
+ return this.setOpen(!this.isOpen)
29
+ }
30
+ }
31
+
@@ -0,0 +1,83 @@
1
+
2
+ import {css, html} from "lit"
3
+ import {ShinyDrawer} from "./view.js"
4
+ import {lipsum} from "../../demo/utils/lipsum.js"
5
+ import {exhibit} from "../../demo/parts/exhibit.js"
6
+ import {Showcase} from "../../demo/views/showcase/view.js"
7
+
8
+ const styleboxCss = css`
9
+ :host {
10
+ font-size: 1em;
11
+ width: 100%;
12
+ min-height: 100%;
13
+ }
14
+ p + p {
15
+ margin-top: var(--padding);
16
+ }
17
+ `
18
+
19
+ const customCss = css`
20
+ [slot=plate] { padding-top: 2em; }
21
+ [view="shiny-drawer"] {
22
+ --slate-bg: color-mix(in oklch, var(--calm), #444);
23
+ &::part(slate) { padding: 1em; }
24
+ &:state(left)::part(slate) { border-bottom-right-radius: 0.5em; }
25
+ &:state(right)::part(slate) { border-bottom-left-radius: 0.5em; }
26
+ }
27
+ `
28
+
29
+ const render = (side: "left" | "right") => (
30
+ ShinyDrawer.with({
31
+ props: [{button: true, side}],
32
+ children: html`
33
+ <header>${lipsum.takeFirst()}</header>
34
+ <section slot=plate>
35
+ <p>${lipsum.takeFirst()}</p>
36
+ <p>${lipsum.takeFirst()}</p>
37
+ </section>
38
+ `,
39
+ })
40
+ )
41
+
42
+ export const drawerShowcase = () => Showcase("ShinyDrawer", [
43
+ exhibit({
44
+ name: "left",
45
+ explain: "slide-out panel. button optional.",
46
+ styleboxCss,
47
+ css: customCss,
48
+ render: () => render("left"),
49
+ js: `
50
+ ShinyDrawer.with({
51
+ props: [{button: true, side: "left"}],
52
+ children: html\`
53
+ <header>lorem kettlebell..</header>
54
+ <section slot=plate>
55
+ <p>lorem protein..</p>
56
+ <p>lorem caffeine..</p>
57
+ </section>
58
+ \`,
59
+ })
60
+ `,
61
+ }),
62
+
63
+ exhibit({
64
+ name: "right",
65
+ explain: "slide-out panel. button optional.",
66
+ styleboxCss,
67
+ css: customCss,
68
+ render: () => render("right"),
69
+ js: `
70
+ ShinyDrawer.with({
71
+ props: [{button: true, side: "right"}],
72
+ children: html\`
73
+ <header>lorem kettlebell..</header>
74
+ <section slot=plate>
75
+ <p>lorem protein..</p>
76
+ <p>lorem caffeine..</p>
77
+ </section>
78
+ \`,
79
+ })
80
+ `,
81
+ }),
82
+ ])
83
+
@@ -0,0 +1,128 @@
1
+
2
+ import {css} from "lit"
3
+ export default css`@layer view {
4
+
5
+ :host {
6
+ display: block;
7
+ width: 100%;
8
+ height: 100%;
9
+
10
+ --slate-bg: transparent;
11
+ --button-size: 2em;
12
+ --blanket-backdrop-filter: blur(0.5em);
13
+ --slate-hidden-opacity: 1;
14
+ --blanket-bg: color-mix(in oklab, transparent, var(--bg));
15
+ }
16
+
17
+ .shell {
18
+ position: relative;
19
+ width: 100%;
20
+ height: 100%;
21
+
22
+ [part="blanket"] {
23
+ opacity: 0;
24
+
25
+ content: "";
26
+ display: block;
27
+ position: absolute;
28
+ inset: 0;
29
+
30
+ background: var(--blanket-bg);
31
+ backdrop-filter: var(--blanket-backdrop-filter);
32
+
33
+ will-change: opacity;
34
+ transition: all var(--anim-duration) ease;
35
+ }
36
+
37
+ .clipper {
38
+ pointer-events: none;
39
+ position: absolute;
40
+ inset: 0;
41
+ width: 100%;
42
+ height: 100%;
43
+ overflow: hidden;
44
+ > * { pointer-events: all; }
45
+ }
46
+
47
+ [part="tray"] {
48
+ position: absolute;
49
+ top: 0;
50
+ width: calc(100% - var(--button-size));
51
+
52
+ display: flex;
53
+ flex-direction: column;
54
+ height: auto;
55
+ max-height: 100%;
56
+
57
+ transform: translateX(-100%);
58
+ will-change: opacity, transform;
59
+ transition: all var(--anim-duration) ease;
60
+
61
+ > [part="slate"] {
62
+ opacity: var(--slate-hidden-opacity);
63
+ will-change: opacity;
64
+ transition: opacity var(--anim-duration) ease;
65
+
66
+ display: block;
67
+ height: 100%;
68
+ overflow-y: auto;
69
+ background: var(--slate-bg);
70
+ }
71
+
72
+ > button {
73
+ position: absolute;
74
+ top: 0;
75
+ left: 100%;
76
+
77
+ opacity: var(--inactive-opacity);
78
+ background: transparent;
79
+ border: none;
80
+ cursor: pointer;
81
+
82
+ &:is(:hover, :focus-visible) {
83
+ opacity: 1;
84
+ }
85
+
86
+ > slot {
87
+ display: contents;
88
+ }
89
+
90
+ svg {
91
+ width: var(--button-size);
92
+ height: var(--button-size);
93
+ }
94
+ }
95
+ }
96
+
97
+ &[data-side="right"] {
98
+ [part="tray"] {
99
+ right: 0;
100
+ transform: translateX(100%);
101
+ > button {
102
+ left: unset;
103
+ right: 100%;
104
+ }
105
+ }
106
+ }
107
+
108
+ slot[name="plate"] {
109
+ display: block;
110
+ width: 100%;
111
+ height: 100%;
112
+ }
113
+
114
+ &[data-open] {
115
+ [part="blanket"] {
116
+ opacity: 1;
117
+ }
118
+ [part="tray"] {
119
+ transform: translateX(0%);
120
+ > [part="slate"] {
121
+ opacity: 1;
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ }`
128
+
@@ -0,0 +1,76 @@
1
+
2
+ import {html} from "lit"
3
+ import {dom, shadow, useAttrs, useCss, useHost, useMount, useName, useOnce} from "@e280/sly"
4
+
5
+ import styleCss from "./style.css.js"
6
+
7
+ import {themeCss} from "../../theme/theme.css.js"
8
+ import {DrawerControl} from "./control.js"
9
+ import {States} from "../../utils/states.js"
10
+ import xSvg from "../../icons/tabler/x.svg.js"
11
+ import menu2Svg from "../../icons/tabler/menu-2.svg.js"
12
+
13
+ export const ShinyDrawer = shadow((options: {
14
+ button?: boolean
15
+ side?: "left" | "right"
16
+ control?: DrawerControl
17
+ } = {}) => {
18
+
19
+ useName("shiny-drawer")
20
+ useCss(themeCss, styleCss)
21
+
22
+ const host = useHost()
23
+ const states = useOnce(() => new States(host))
24
+ const control = useOnce(() => (options.control ?? new DrawerControl()))
25
+
26
+ const attrs = useAttrs({
27
+ side: String,
28
+ button: Boolean,
29
+ open: Boolean,
30
+ })
31
+
32
+ attrs.open = control.isOpen
33
+
34
+ const button = options.button ?? attrs.button
35
+ const side = options.side ?? (attrs.side === "right" ? "right" : "left")
36
+ states.assign(side, control.isOpen ? "opened" : "closed")
37
+
38
+ useMount(() => dom.events(window, {keydown: (event: KeyboardEvent) => {
39
+ if (event.code === "Escape")
40
+ control.close()
41
+ }}))
42
+
43
+ function renderButton() {
44
+ return html`
45
+ <button part=button @click="${control.toggle}">
46
+ ${control.isOpen
47
+ ? html`
48
+ <slot name=button-x>
49
+ ${xSvg}
50
+ </slot>
51
+ `
52
+ : html`
53
+ <slot name=button>
54
+ ${menu2Svg}
55
+ </slot>
56
+ `}
57
+ </button>
58
+ `
59
+ }
60
+
61
+ return html`
62
+ <div class=shell ?data-open="${control.isOpen}" data-side="${side}">
63
+ <slot name=plate ?inert="${control.isOpen}"></slot>
64
+
65
+ <div class=clipper>
66
+ <div part=blanket @click="${control.close}" ?inert="${!control.isOpen}"></div>
67
+
68
+ <div part=tray>
69
+ <slot part=slate ?inert="${!control.isOpen}"></slot>
70
+ ${button ?renderButton() :null}
71
+ </div>
72
+ </div>
73
+ </div>
74
+ `
75
+ })
76
+
@@ -0,0 +1,31 @@
1
+
2
+ import {signal} from "@e280/strata"
3
+
4
+ export class TabsControl {
5
+ length = 1
6
+ $index = signal(0)
7
+
8
+ constructor(start = 0) {
9
+ this.$index.value = start
10
+ }
11
+
12
+ clamp(index: number) {
13
+ index = Math.min(index, this.length - 1)
14
+ index = Math.max(index, 0)
15
+ return index
16
+ }
17
+
18
+ get index() {
19
+ return this.clamp(this.$index())
20
+ }
21
+
22
+ async setIndex(index: number) {
23
+ return this.$index(index)
24
+ }
25
+
26
+ async shimmy(delta: number) {
27
+ const index = this.clamp(this.index + delta)
28
+ return this.setIndex(index)
29
+ }
30
+ }
31
+
@@ -0,0 +1,89 @@
1
+
2
+ import {css, html} from "lit"
3
+ import {ShinyTabs} from "./view.js"
4
+ import {ShinyButton} from "../button/view.js"
5
+ import {lipsum} from "../../demo/utils/lipsum.js"
6
+ import {exhibit} from "../../demo/parts/exhibit.js"
7
+ import {Showcase} from "../../demo/views/showcase/view.js"
8
+
9
+ export const tabsShowcase = () => Showcase("ShinyTabs", [
10
+ exhibit({
11
+ name: "shiny",
12
+ explain: "a button-bar of mutually-exclusive tabs. panels are optional.",
13
+ render: () => ShinyTabs.with({
14
+ props: [{snug: true}],
15
+ children: html`
16
+ ${ShinyButton("alpha")}
17
+ ${ShinyButton("bravo")}
18
+ ${ShinyButton("charlie")}
19
+ <p slot=panel>${lipsum.takeFirst()}</p>
20
+ <p slot=panel>${lipsum.takeFirst()}</p>
21
+ <p slot=panel>${lipsum.takeFirst()}</p>
22
+ `,
23
+ }),
24
+ styleboxCss: css`
25
+ [view="shiny-button"] { font-size: 1.5em; }
26
+ p { margin-top: var(--padding); }
27
+ `,
28
+ js: `
29
+ ShinyTabs.with({
30
+ props: [{snug: true}],
31
+ children: html\`
32
+ \${ShinyButton("alpha")}
33
+ \${ShinyButton("bravo")}
34
+ \${ShinyButton("charlie")}
35
+ <p slot=panel>lorem kettlebell..</p>
36
+ <p slot=panel>lorem protein..</p>
37
+ <p slot=panel>lorem caffeine..</p>
38
+ \`,
39
+ })
40
+ `,
41
+ css: css`
42
+ [view="shiny-tabs"] {
43
+ --shiny-rounded: 0.5em;
44
+ }
45
+ `,
46
+ }),
47
+
48
+ exhibit({
49
+ name: "plain",
50
+ explain: "a button-bar of mutually-exclusive tabs. panels are optional.",
51
+ render: () => ShinyTabs.with({
52
+ props: [{snug: true}],
53
+ children: html`
54
+ <button>alpha</button>
55
+ <button>bravo</button>
56
+ <button>charlie</button>
57
+ <p slot=panel>${lipsum.takeFirst()}</p>
58
+ <p slot=panel>${lipsum.takeFirst()}</p>
59
+ <p slot=panel>${lipsum.takeFirst()}</p>
60
+ `,
61
+ }),
62
+ styleboxCss: css`
63
+ p { margin-top: var(--padding); }
64
+ button {
65
+ font-size: 1.5em;
66
+ padding: calc(var(--padding) / 2);
67
+ }
68
+ `,
69
+ js: `
70
+ ShinyTabs.with({
71
+ props: [{snug: true}],
72
+ children: html\`
73
+ <button>alpha</button>
74
+ <button>bravo</button>
75
+ <button>charlie</button>
76
+ <p slot=panel>lorem kettlebell..</p>
77
+ <p slot=panel>lorem protein..</p>
78
+ <p slot=panel>lorem caffeine..</p>
79
+ \`,
80
+ })
81
+ `,
82
+ css: css`
83
+ [view="shiny-tabs"] {
84
+ --shiny-rounded: 0.5em;
85
+ }
86
+ `,
87
+ }),
88
+ ])
89
+
@@ -0,0 +1,46 @@
1
+
2
+ import {css} from "lit"
3
+ export default css`@layer view {
4
+
5
+ :host {
6
+ display: inline-flex;
7
+ flex-direction: column;
8
+ }
9
+
10
+ slot[part="tabs"] {
11
+ display: flex;
12
+ flex-wrap: wrap;
13
+
14
+ &::slotted([data-tabbed]) {
15
+ opacity: 1;
16
+ color: currentColor;
17
+ text-decoration: underline;
18
+ }
19
+ }
20
+
21
+ slot[part="tabs"][data-snug] {
22
+ &::slotted(*) {
23
+ border-radius: var(--rounded);
24
+ }
25
+
26
+ &::slotted(:not([data-last], [data-next-is-tabbed])) {
27
+ border-right: none;
28
+ }
29
+
30
+ &::slotted([data-tabbed]:not([data-first])) {
31
+ border-left: none;
32
+ }
33
+
34
+ &::slotted(:not([data-last])) {
35
+ border-top-right-radius: 0;
36
+ border-bottom-right-radius: 0;
37
+ }
38
+
39
+ &::slotted(:not([data-first])) {
40
+ border-top-left-radius: 0;
41
+ border-bottom-left-radius: 0;
42
+ }
43
+ }
44
+
45
+ } `
46
+
@@ -0,0 +1,66 @@
1
+
2
+ import {html} from "lit"
3
+ import {dom, shadow, useHost, useName, useOnce, useSignal, useStyles} from "@e280/sly"
4
+ import styleCss from "./style.css.js"
5
+ import {TabsControl} from "./control.js"
6
+ import {themeCss} from "../../theme/theme.css.js"
7
+
8
+ export const ShinyTabs = shadow((options: {
9
+ snug?: boolean
10
+ control?: TabsControl
11
+ } = {}) => {
12
+
13
+ useName("shiny-tabs")
14
+ useStyles(themeCss, styleCss)
15
+
16
+ const host = useHost()
17
+ const attrs = useOnce(() => dom.attrs(host).spec({index: Number}))
18
+ const control = useOnce(() => options.control ?? new TabsControl(attrs.index ?? 0))
19
+
20
+ const $tabs = useSignal<HTMLElement[]>([])
21
+ const $panels = useSignal<HTMLElement[]>([])
22
+
23
+ async function tabchange(event: Event) {
24
+ const slot = event.currentTarget! as HTMLSlotElement
25
+ await $tabs.set(slot.assignedElements() as HTMLElement[])
26
+ }
27
+
28
+ async function panelchange(event: Event) {
29
+ const slot = event.currentTarget! as HTMLSlotElement
30
+ await $panels.set(slot.assignedElements() as HTMLElement[])
31
+ }
32
+
33
+ attrs.index = control.$index()
34
+ control.length = $tabs().length
35
+
36
+ function isNeighborActive(index: number, delta: number) {
37
+ const nextIndex = control.clamp(index + delta)
38
+ if (nextIndex === index) return false
39
+ return (nextIndex === control.index)
40
+ }
41
+
42
+ for (const [index, tab] of $tabs().entries()) {
43
+ const tabbed = (index === control.index)
44
+ const tabAttrs = dom.attrs(tab)
45
+ tabAttrs.booleans.disabled = tabbed
46
+ tabAttrs.booleans["data-snug"] = options.snug
47
+ tabAttrs.booleans["data-tabbed"] = tabbed
48
+ tabAttrs.booleans["data-first"] = (index === 0)
49
+ tabAttrs.booleans["data-last"] = (index === (control.length - 1))
50
+ tabAttrs.booleans["data-next-is-tabbed"] = isNeighborActive(index, 1)
51
+ tabAttrs.booleans["data-previous-is-tabbed"] = isNeighborActive(index, -1)
52
+ tab.onclick = () => control.setIndex(index)
53
+ }
54
+
55
+ for (const [index, panel] of $panels().entries()) {
56
+ const tabbed = (index === control.index)
57
+ dom.attrs(panel).booleans["data-tabbed"] = tabbed
58
+ dom.attrs(panel).booleans["hidden"] = !tabbed
59
+ }
60
+
61
+ return html`
62
+ <slot part=tabs ?data-snug="${options.snug}" @slotchange="${tabchange}"></slot>
63
+ <slot part=panels name=panel @slotchange="${panelchange}"></slot>
64
+ `
65
+ })
66
+