@mhmo91/schmancy 0.9.4 → 0.9.5

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 (217) hide show
  1. package/.claude-plugin/plugin.json +13 -0
  2. package/README.md +14 -4
  3. package/dist/.claude-plugin/plugin.json +13 -0
  4. package/dist/skills/SKILL.md +120 -0
  5. package/dist/skills/schmancy/SKILL.md +120 -0
  6. package/package.json +3 -3
  7. package/skills/schmancy/INDEX.md +72 -0
  8. package/skills/schmancy/SKILL.md +120 -0
  9. package/skills/schmancy/animation.md +64 -0
  10. package/skills/schmancy/area.md +141 -0
  11. package/skills/schmancy/audio.md +69 -0
  12. package/skills/schmancy/autocomplete.md +53 -0
  13. package/skills/schmancy/avatar.md +47 -0
  14. package/skills/schmancy/badge.md +41 -0
  15. package/skills/schmancy/boat.md +47 -0
  16. package/skills/schmancy/busy.md +36 -0
  17. package/skills/schmancy/button.md +59 -0
  18. package/skills/schmancy/card.md +53 -0
  19. package/skills/schmancy/charts.md +93 -0
  20. package/skills/schmancy/checkbox.md +36 -0
  21. package/skills/schmancy/chips.md +87 -0
  22. package/skills/schmancy/code-highlight.md +47 -0
  23. package/skills/schmancy/connectivity.md +36 -0
  24. package/skills/schmancy/content-drawer.md +65 -0
  25. package/skills/schmancy/date-range-inline.md +44 -0
  26. package/skills/schmancy/date-range.md +50 -0
  27. package/skills/schmancy/delay.md +50 -0
  28. package/skills/schmancy/details.md +66 -0
  29. package/skills/schmancy/dialog.md +69 -0
  30. package/skills/schmancy/directives.md +298 -0
  31. package/skills/schmancy/discovery.md +67 -0
  32. package/skills/schmancy/divider.md +31 -0
  33. package/skills/schmancy/dropdown.md +47 -0
  34. package/skills/schmancy/expand.md +63 -0
  35. package/skills/schmancy/extra.md +59 -0
  36. package/skills/schmancy/float.md +14 -0
  37. package/skills/schmancy/form.md +49 -0
  38. package/skills/schmancy/icons.md +44 -0
  39. package/skills/schmancy/iframe.md +44 -0
  40. package/skills/schmancy/input.md +56 -0
  41. package/skills/schmancy/json.md +33 -0
  42. package/skills/schmancy/layout.md +63 -0
  43. package/skills/schmancy/lightbox.md +36 -0
  44. package/skills/schmancy/list.md +67 -0
  45. package/skills/schmancy/mailbox.md +102 -0
  46. package/skills/schmancy/map.md +55 -0
  47. package/skills/schmancy/menu.md +39 -0
  48. package/skills/schmancy/mixins.md +99 -0
  49. package/skills/schmancy/nav-drawer.md +52 -0
  50. package/skills/schmancy/navigation-bar.md +48 -0
  51. package/skills/schmancy/navigation-rail.md +62 -0
  52. package/skills/schmancy/notification.md +60 -0
  53. package/skills/schmancy/option.md +43 -0
  54. package/skills/schmancy/page.md +42 -0
  55. package/skills/schmancy/progress.md +30 -0
  56. package/skills/schmancy/qr-scanner.md +51 -0
  57. package/skills/schmancy/radio-group.md +50 -0
  58. package/skills/schmancy/range.md +47 -0
  59. package/skills/schmancy/rxjs-utils.md +60 -0
  60. package/skills/schmancy/select.md +49 -0
  61. package/skills/schmancy/sheet.md +76 -0
  62. package/skills/schmancy/slider.md +43 -0
  63. package/skills/schmancy/steps.md +53 -0
  64. package/skills/schmancy/store.md +126 -0
  65. package/skills/schmancy/surface.md +86 -0
  66. package/skills/schmancy/table.md +60 -0
  67. package/skills/schmancy/tabs.md +49 -0
  68. package/skills/schmancy/teleport.md +55 -0
  69. package/skills/schmancy/textarea.md +48 -0
  70. package/skills/schmancy/theme-button.md +26 -0
  71. package/skills/schmancy/theme.md +58 -0
  72. package/skills/schmancy/tooltip.md +38 -0
  73. package/skills/schmancy/tree.md +53 -0
  74. package/skills/schmancy/typewriter.md +46 -0
  75. package/skills/schmancy/typography.md +53 -0
  76. package/skills/schmancy/utils.md +95 -0
  77. package/skills/schmancy/window.md +67 -0
  78. /package/{ai → dist/skills}/INDEX.md +0 -0
  79. /package/{ai → dist/skills}/animation.md +0 -0
  80. /package/{ai → dist/skills}/area.md +0 -0
  81. /package/{ai → dist/skills}/audio.md +0 -0
  82. /package/{ai → dist/skills}/autocomplete.md +0 -0
  83. /package/{ai → dist/skills}/avatar.md +0 -0
  84. /package/{ai → dist/skills}/badge.md +0 -0
  85. /package/{ai → dist/skills}/boat.md +0 -0
  86. /package/{ai → dist/skills}/busy.md +0 -0
  87. /package/{ai → dist/skills}/button.md +0 -0
  88. /package/{ai → dist/skills}/card.md +0 -0
  89. /package/{ai → dist/skills}/charts.md +0 -0
  90. /package/{ai → dist/skills}/checkbox.md +0 -0
  91. /package/{ai → dist/skills}/chips.md +0 -0
  92. /package/{ai → dist/skills}/code-highlight.md +0 -0
  93. /package/{ai → dist/skills}/connectivity.md +0 -0
  94. /package/{ai → dist/skills}/content-drawer.md +0 -0
  95. /package/{ai → dist/skills}/date-range-inline.md +0 -0
  96. /package/{ai → dist/skills}/date-range.md +0 -0
  97. /package/{ai → dist/skills}/delay.md +0 -0
  98. /package/{ai → dist/skills}/details.md +0 -0
  99. /package/{ai → dist/skills}/dialog.md +0 -0
  100. /package/{ai → dist/skills}/directives.md +0 -0
  101. /package/{ai → dist/skills}/discovery.md +0 -0
  102. /package/{ai → dist/skills}/divider.md +0 -0
  103. /package/{ai → dist/skills}/dropdown.md +0 -0
  104. /package/{ai → dist/skills}/expand.md +0 -0
  105. /package/{ai → dist/skills}/extra.md +0 -0
  106. /package/{ai → dist/skills}/float.md +0 -0
  107. /package/{ai → dist/skills}/form.md +0 -0
  108. /package/{ai → dist/skills}/icons.md +0 -0
  109. /package/{ai → dist/skills}/iframe.md +0 -0
  110. /package/{ai → dist/skills}/input.md +0 -0
  111. /package/{ai → dist/skills}/json.md +0 -0
  112. /package/{ai → dist/skills}/layout.md +0 -0
  113. /package/{ai → dist/skills}/lightbox.md +0 -0
  114. /package/{ai → dist/skills}/list.md +0 -0
  115. /package/{ai → dist/skills}/mailbox.md +0 -0
  116. /package/{ai → dist/skills}/map.md +0 -0
  117. /package/{ai → dist/skills}/menu.md +0 -0
  118. /package/{ai → dist/skills}/mixins.md +0 -0
  119. /package/{ai → dist/skills}/nav-drawer.md +0 -0
  120. /package/{ai → dist/skills}/navigation-bar.md +0 -0
  121. /package/{ai → dist/skills}/navigation-rail.md +0 -0
  122. /package/{ai → dist/skills}/notification.md +0 -0
  123. /package/{ai → dist/skills}/option.md +0 -0
  124. /package/{ai → dist/skills}/page.md +0 -0
  125. /package/{ai → dist/skills}/progress.md +0 -0
  126. /package/{ai → dist/skills}/qr-scanner.md +0 -0
  127. /package/{ai → dist/skills}/radio-group.md +0 -0
  128. /package/{ai → dist/skills}/range.md +0 -0
  129. /package/{ai → dist/skills}/rxjs-utils.md +0 -0
  130. /package/dist/{ai → skills/schmancy}/INDEX.md +0 -0
  131. /package/dist/{ai → skills/schmancy}/animation.md +0 -0
  132. /package/dist/{ai → skills/schmancy}/area.md +0 -0
  133. /package/dist/{ai → skills/schmancy}/audio.md +0 -0
  134. /package/dist/{ai → skills/schmancy}/autocomplete.md +0 -0
  135. /package/dist/{ai → skills/schmancy}/avatar.md +0 -0
  136. /package/dist/{ai → skills/schmancy}/badge.md +0 -0
  137. /package/dist/{ai → skills/schmancy}/boat.md +0 -0
  138. /package/dist/{ai → skills/schmancy}/busy.md +0 -0
  139. /package/dist/{ai → skills/schmancy}/button.md +0 -0
  140. /package/dist/{ai → skills/schmancy}/card.md +0 -0
  141. /package/dist/{ai → skills/schmancy}/charts.md +0 -0
  142. /package/dist/{ai → skills/schmancy}/checkbox.md +0 -0
  143. /package/dist/{ai → skills/schmancy}/chips.md +0 -0
  144. /package/dist/{ai → skills/schmancy}/code-highlight.md +0 -0
  145. /package/dist/{ai → skills/schmancy}/connectivity.md +0 -0
  146. /package/dist/{ai → skills/schmancy}/content-drawer.md +0 -0
  147. /package/dist/{ai → skills/schmancy}/date-range-inline.md +0 -0
  148. /package/dist/{ai → skills/schmancy}/date-range.md +0 -0
  149. /package/dist/{ai → skills/schmancy}/delay.md +0 -0
  150. /package/dist/{ai → skills/schmancy}/details.md +0 -0
  151. /package/dist/{ai → skills/schmancy}/dialog.md +0 -0
  152. /package/dist/{ai → skills/schmancy}/directives.md +0 -0
  153. /package/dist/{ai → skills/schmancy}/discovery.md +0 -0
  154. /package/dist/{ai → skills/schmancy}/divider.md +0 -0
  155. /package/dist/{ai → skills/schmancy}/dropdown.md +0 -0
  156. /package/dist/{ai → skills/schmancy}/expand.md +0 -0
  157. /package/dist/{ai → skills/schmancy}/extra.md +0 -0
  158. /package/dist/{ai → skills/schmancy}/float.md +0 -0
  159. /package/dist/{ai → skills/schmancy}/form.md +0 -0
  160. /package/dist/{ai → skills/schmancy}/icons.md +0 -0
  161. /package/dist/{ai → skills/schmancy}/iframe.md +0 -0
  162. /package/dist/{ai → skills/schmancy}/input.md +0 -0
  163. /package/dist/{ai → skills/schmancy}/json.md +0 -0
  164. /package/dist/{ai → skills/schmancy}/layout.md +0 -0
  165. /package/dist/{ai → skills/schmancy}/lightbox.md +0 -0
  166. /package/dist/{ai → skills/schmancy}/list.md +0 -0
  167. /package/dist/{ai → skills/schmancy}/mailbox.md +0 -0
  168. /package/dist/{ai → skills/schmancy}/map.md +0 -0
  169. /package/dist/{ai → skills/schmancy}/menu.md +0 -0
  170. /package/dist/{ai → skills/schmancy}/mixins.md +0 -0
  171. /package/dist/{ai → skills/schmancy}/nav-drawer.md +0 -0
  172. /package/dist/{ai → skills/schmancy}/navigation-bar.md +0 -0
  173. /package/dist/{ai → skills/schmancy}/navigation-rail.md +0 -0
  174. /package/dist/{ai → skills/schmancy}/notification.md +0 -0
  175. /package/dist/{ai → skills/schmancy}/option.md +0 -0
  176. /package/dist/{ai → skills/schmancy}/page.md +0 -0
  177. /package/dist/{ai → skills/schmancy}/progress.md +0 -0
  178. /package/dist/{ai → skills/schmancy}/qr-scanner.md +0 -0
  179. /package/dist/{ai → skills/schmancy}/radio-group.md +0 -0
  180. /package/dist/{ai → skills/schmancy}/range.md +0 -0
  181. /package/dist/{ai → skills/schmancy}/rxjs-utils.md +0 -0
  182. /package/{ai → dist/skills/schmancy}/select.md +0 -0
  183. /package/{ai → dist/skills/schmancy}/sheet.md +0 -0
  184. /package/{ai → dist/skills/schmancy}/slider.md +0 -0
  185. /package/{ai → dist/skills/schmancy}/steps.md +0 -0
  186. /package/{ai → dist/skills/schmancy}/store.md +0 -0
  187. /package/{ai → dist/skills/schmancy}/surface.md +0 -0
  188. /package/{ai → dist/skills/schmancy}/table.md +0 -0
  189. /package/{ai → dist/skills/schmancy}/tabs.md +0 -0
  190. /package/{ai → dist/skills/schmancy}/teleport.md +0 -0
  191. /package/{ai → dist/skills/schmancy}/textarea.md +0 -0
  192. /package/{ai → dist/skills/schmancy}/theme-button.md +0 -0
  193. /package/{ai → dist/skills/schmancy}/theme.md +0 -0
  194. /package/{ai → dist/skills/schmancy}/tooltip.md +0 -0
  195. /package/{ai → dist/skills/schmancy}/tree.md +0 -0
  196. /package/{ai → dist/skills/schmancy}/typewriter.md +0 -0
  197. /package/{ai → dist/skills/schmancy}/typography.md +0 -0
  198. /package/{ai → dist/skills/schmancy}/utils.md +0 -0
  199. /package/{ai → dist/skills/schmancy}/window.md +0 -0
  200. /package/dist/{ai → skills}/select.md +0 -0
  201. /package/dist/{ai → skills}/sheet.md +0 -0
  202. /package/dist/{ai → skills}/slider.md +0 -0
  203. /package/dist/{ai → skills}/steps.md +0 -0
  204. /package/dist/{ai → skills}/store.md +0 -0
  205. /package/dist/{ai → skills}/surface.md +0 -0
  206. /package/dist/{ai → skills}/table.md +0 -0
  207. /package/dist/{ai → skills}/tabs.md +0 -0
  208. /package/dist/{ai → skills}/teleport.md +0 -0
  209. /package/dist/{ai → skills}/textarea.md +0 -0
  210. /package/dist/{ai → skills}/theme-button.md +0 -0
  211. /package/dist/{ai → skills}/theme.md +0 -0
  212. /package/dist/{ai → skills}/tooltip.md +0 -0
  213. /package/dist/{ai → skills}/tree.md +0 -0
  214. /package/dist/{ai → skills}/typewriter.md +0 -0
  215. /package/dist/{ai → skills}/typography.md +0 -0
  216. /package/dist/{ai → skills}/utils.md +0 -0
  217. /package/dist/{ai → skills}/window.md +0 -0
@@ -0,0 +1,44 @@
1
+ # schmancy-iframe
2
+
3
+ > Sandboxed iframe that auto-sizes to its content. Ships with sensible document resets (font, spacing, tables, pre, blockquote).
4
+
5
+ ## Usage
6
+ ```html
7
+ <schmancy-iframe .html=${emailBodyHtml}></schmancy-iframe>
8
+ ```
9
+
10
+ ## Properties
11
+ | Property | Type | Default | Description |
12
+ |----------|------|---------|-------------|
13
+ | `html` | string | `''` | Body HTML fragment to render |
14
+ | `css` | string | `''` | Extra CSS injected after `baseCss` |
15
+ | `baseCss` | string | default reset | Document CSS — override for fully custom styling |
16
+ | `sandbox` | string | `'allow-same-origin allow-popups'` | Iframe sandbox attribute |
17
+ | `minHeight` | number | `60` | Minimum iframe height in px |
18
+
19
+ ## Events
20
+ | Event | When |
21
+ |-------|------|
22
+ | `load` | Native iframe load; height auto-syncs after |
23
+
24
+ ## Default Resets
25
+ The built-in `baseCss` applies: system font, 14px/1.6 line-height, reset margins for headings/lists/paragraphs, bordered tables, blockquote styling, code/pre backgrounds, responsive images.
26
+
27
+ ## Examples
28
+ ```html
29
+ <!-- Email preview with extra styles -->
30
+ <schmancy-iframe
31
+ .html=${this.email.bodyHtml}
32
+ .css=${`h1 { color: #6200ee; }`}
33
+ ></schmancy-iframe>
34
+
35
+ <!-- Fully custom base styling -->
36
+ <schmancy-iframe
37
+ .html=${fragment}
38
+ .baseCss=${'html,body{margin:0;background:#000;color:#fff}'}
39
+ ></schmancy-iframe>
40
+ ```
41
+
42
+ ## When to Use
43
+ - Rendering untrusted / styled third-party HTML (emails, rich snippets, MD-rendered content) in isolation.
44
+ - Preview panes for user-generated HTML.
@@ -0,0 +1,56 @@
1
+ # schmancy-input
2
+
3
+ > Text input with form association, validation strategies, and size variants.
4
+
5
+ ## Usage
6
+ ```html
7
+ <schmancy-input label="Email" type="email" required></schmancy-input>
8
+ ```
9
+
10
+ ## Properties
11
+ | Property | Type | Default | Description |
12
+ |----------|------|---------|-------------|
13
+ | value | string | `''` | Current input value |
14
+ | type | string | `'text'` | HTML input type (text, password, email, number, date, etc.) |
15
+ | label | string | `''` | Label text above the input |
16
+ | placeholder | string | `''` | Placeholder text |
17
+ | name | string | auto | Form submission name |
18
+ | required | boolean | `false` | Whether the field is required |
19
+ | disabled | boolean | `false` | Whether the field is disabled |
20
+ | readonly | boolean | `false` | Whether the field is read-only |
21
+ | error | boolean | `false` | Whether the field shows an error state |
22
+ | validationMessage | string | `''` | Custom validation message |
23
+ | hint | string | `undefined` | Hint text below the field |
24
+ | size | `'xxs'\|'xs'\|'sm'\|'md'\|'lg'` | `'md'` | Input height (24-56px) |
25
+ | validateOn | `'always'\|'touched'\|'dirty'\|'submitted'` | `'touched'` | When to show validation |
26
+ | align | `'left'\|'center'\|'right'` | `'left'` | Text alignment |
27
+ | pattern | string | `undefined` | Regex validation pattern |
28
+ | inputmode | string | `undefined` | Virtual keyboard hint |
29
+ | min/max | string | `undefined` | Range constraints |
30
+ | minlength/maxlength | number | `undefined` | Length constraints |
31
+ | clickable | boolean | `false` | Show pointer cursor when readonly |
32
+ | autofocus | boolean | `false` | Auto-focus on mount |
33
+
34
+ ## Events
35
+ | Event | Detail | Description |
36
+ |-------|--------|-------------|
37
+ | input | `{ value: string }` | Every keystroke |
38
+ | change | `{ value: string }` | On blur/native change |
39
+ | enter | `{ value: string }` | When Enter key is pressed |
40
+
41
+ ## Examples
42
+ ```html
43
+ <!-- Password with validation -->
44
+ <schmancy-input label="Password" type="password" required minlength="8"
45
+ hint="At least 8 characters"></schmancy-input>
46
+
47
+ <!-- Compact number input -->
48
+ <schmancy-input type="number" size="xs" min="0" max="100"
49
+ placeholder="Qty"></schmancy-input>
50
+
51
+ <!-- With enter handler -->
52
+ <schmancy-input label="Search" placeholder="Type and press Enter"
53
+ @enter=${(e) => search(e.detail.value)}></schmancy-input>
54
+ ```
55
+
56
+ **Tag aliases:** `<sch-input>` (backward compatible)
@@ -0,0 +1,33 @@
1
+ # schmancy-json
2
+
3
+ > Pretty-printed JSON viewer with key highlighting and click-to-copy.
4
+
5
+ ## Usage
6
+ ```html
7
+ <schmancy-json .data=${{ user: 'alice', score: 42 }}></schmancy-json>
8
+ ```
9
+
10
+ ## Properties
11
+ | Property | Type | Default | Description |
12
+ |----------|------|---------|-------------|
13
+ | `data` | object | `{}` | JSON-serialisable data to display |
14
+ | `highlightKeys` | string[] | `[]` | Keys whose values are highlighted (warning color, bold) |
15
+ | `compact` | boolean | `false` | Disable pretty-printing (single line) |
16
+
17
+ ## Behavior
18
+ - Click anywhere on the viewer to copy the full JSON to clipboard (fires `$notify.success`).
19
+ - Container uses `surface-container` glass background with hover state.
20
+ - Monospace font, 10px size — optimized for dense debug output.
21
+ - Values for `highlightKeys` render inside `<span class="text-warning-default font-bold">`.
22
+
23
+ ## Examples
24
+ ```html
25
+ <!-- Debug view with key highlights -->
26
+ <schmancy-json
27
+ .data=${this.state}
28
+ .highlightKeys=${['error', 'pending']}
29
+ ></schmancy-json>
30
+
31
+ <!-- Compact inline -->
32
+ <schmancy-json compact .data=${{ id: 42, status: 'ok' }}></schmancy-json>
33
+ ```
@@ -0,0 +1,63 @@
1
+ # Schmancy Layout
2
+
3
+ > Layout primitives: `schmancy-grid`, `schmancy-flex`, `schmancy-scroll`, plus `sch-grid` / `sch-flex` v2 variants.
4
+
5
+ > **Prefer Tailwind classes directly** for layout in new code. These components exist for quick composition and design-token consistency (`gap="sm|md|lg"` maps to theme spacing).
6
+
7
+ ## schmancy-grid
8
+ ```html
9
+ <schmancy-grid cols="1fr 2fr 1fr" gap="md" align="center">
10
+ <div>Left</div><div>Center</div><div>Right</div>
11
+ </schmancy-grid>
12
+ ```
13
+
14
+ | Property | Type | Default | Description |
15
+ |----------|------|---------|-------------|
16
+ | `flow` | `'row' \| 'col' \| 'dense' \| 'row-dense' \| 'col-dense'` | `'row'` | Grid auto-flow |
17
+ | `align` | `'start' \| 'center' \| 'end' \| 'stretch' \| 'baseline'` | `'stretch'` | Item alignment |
18
+ | `justify` | `'start' \| 'center' \| 'end' \| 'stretch'` | `'stretch'` | Item justification |
19
+ | `content` | `'start' \| 'center' \| 'end' \| 'stretch' \| 'around' \| 'evenly' \| 'between'` | — | Align-content |
20
+ | `gap` | `'none' \| 'xs' \| 'sm' \| 'md' \| 'lg'` | `'none'` | Grid gap |
21
+ | `cols` | string | — | grid-template-columns (e.g. `"1fr 2fr"`) |
22
+ | `rows` | string | — | grid-template-rows |
23
+ | `rcols` | object | — | Responsive cols: `{ sm: '1fr', md: '1fr 1fr', lg: '1fr 2fr 1fr' }` |
24
+ | `wrap` | boolean | `false` | Grid auto-wrap |
25
+
26
+ ## schmancy-flex
27
+ ```html
28
+ <schmancy-flex flow="row" justify="between" align="center" gap="md">
29
+ <div>Logo</div><div>Nav</div>
30
+ </schmancy-flex>
31
+ ```
32
+
33
+ | Property | Type | Default |
34
+ |----------|------|---------|
35
+ | `flow` | `'row' \| 'row-reverse' \| 'col' \| 'col-reverse'` | `'col'` |
36
+ | `wrap` | `'wrap' \| 'nowrap' \| 'wrap-reverse'` | `'wrap'` |
37
+ | `align` | `'start' \| 'center' \| 'end' \| 'stretch' \| 'baseline'` | `'start'` |
38
+ | `justify` | `'start' \| 'center' \| 'end' \| 'stretch' \| 'between'` | `'start'` |
39
+ | `gap` | `'none' \| 'sm' \| 'md' \| 'lg'` | `'none'` |
40
+
41
+ ## schmancy-scroll
42
+ ```html
43
+ <schmancy-scroll hide direction="vertical" name="main">
44
+ <!-- long content -->
45
+ </schmancy-scroll>
46
+ ```
47
+
48
+ | Property | Type | Default | Description |
49
+ |----------|------|---------|-------------|
50
+ | `hide` | boolean | `false` | Hide scrollbar in supported browsers |
51
+ | `direction` | `'vertical' \| 'horizontal' \| 'both'` | `'both'` | Scroll axes |
52
+ | `name` | string | — | Identifier for global scroll events |
53
+ | `debounce` | number | — | Debounce time in ms for scroll events |
54
+
55
+ Smooth scroll-behavior, overscroll containment, and support for flex-shrink sizing.
56
+
57
+ ## sch-grid / sch-flex (v2)
58
+
59
+ Reflected-attribute variants optimized for styling via CSS selectors. Same prop model as the classic components with a `sch-` prefix. Use when you need to target the layout from parent CSS without Tailwind.
60
+
61
+ ## Notes
62
+ - All layout components extend the base `Layout` class which exposes pass-through CSS properties (padding, margin, width, position, border, etc.).
63
+ - Prefer Tailwind (`class="flex items-center gap-2"`) for new code — these components remain for consistent theme-driven gaps and rapid prototyping.
@@ -0,0 +1,36 @@
1
+ # lightbox (directive)
2
+
3
+ > Lit directive that opens a fullscreen image viewer on click with gallery support and FLIP animation.
4
+
5
+ ## Usage
6
+ ```typescript
7
+ import { lightbox } from '@mhmo91/schmancy'
8
+
9
+ html`<img src="photo.jpg" ${lightbox()} />`
10
+ ```
11
+
12
+ ## Parameters
13
+ | Parameter | Type | Default | Description |
14
+ |-----------|------|---------|-------------|
15
+ | options.images | string[] | `undefined` | Array of image URLs for gallery mode |
16
+ | options.index | number | `0` | Starting image index in gallery |
17
+ | options.overlay | TemplateResult | `undefined` | Custom overlay template on image |
18
+
19
+ ## Examples
20
+ ```html
21
+ <!-- Single image -->
22
+ <img src="photo.jpg" ${lightbox()} />
23
+
24
+ <!-- Gallery with multiple images -->
25
+ <img src="thumb1.jpg" ${lightbox({
26
+ images: ['full1.jpg', 'full2.jpg', 'full3.jpg'],
27
+ index: 0
28
+ })} />
29
+
30
+ <!-- With overlay content -->
31
+ <img src="photo.jpg" ${lightbox({
32
+ overlay: html`<div class="absolute bottom-4 left-4 text-white">Caption</div>`
33
+ })} />
34
+ ```
35
+
36
+ Features: FLIP animation from click position, keyboard navigation (Escape, Arrow keys), gallery counter, backdrop blur. Click image or press Escape to close.
@@ -0,0 +1,67 @@
1
+ # schmancy-list / schmancy-list-item
2
+
3
+ > List container with list items that have glass hover glow, spring press, and selected state with secondary glow.
4
+
5
+ ## Usage
6
+ ```html
7
+ <schmancy-list>
8
+ <schmancy-list-item>Item 1</schmancy-list-item>
9
+ <schmancy-list-item selected>Item 2</schmancy-list-item>
10
+ </schmancy-list>
11
+ ```
12
+
13
+ ## Properties (schmancy-list)
14
+ | Property | Type | Default | Description |
15
+ |----------|------|---------|-------------|
16
+ | `surface` | `TSurfaceColor` | `undefined` | Surface color type, provided to children via context |
17
+ | `fill` | `SchmancySurfaceFill` | `'auto'` | Surface fill style |
18
+ | `elevation` | `0-5` | `0` | Surface elevation level |
19
+
20
+ ## Properties (schmancy-list-item)
21
+ | Property | Type | Default | Description |
22
+ |----------|------|---------|-------------|
23
+ | `selected` | `boolean` | `false` | Selected state with secondary glow |
24
+ | `readonly` | `boolean` | `false` | Disables interaction (no hover, no cursor) |
25
+ | `rounded` | `boolean` | `false` | Rounded corners |
26
+
27
+ ## Slots (schmancy-list-item)
28
+ | Slot | Description |
29
+ |------|-------------|
30
+ | (default) | Item content |
31
+ | `leading` | Leading content (images auto-sized to 16-20px) |
32
+ | `trailing` | Trailing content (images auto-sized to 16-20px) |
33
+
34
+ ## Physics
35
+ - Hover: glass background (`surface-on` at 8%) + subtle primary glow shadow
36
+ - Active: spring compress `scale(0.98)` with 100ms transition
37
+ - Selected: secondary container background at 30% + secondary glow shadow
38
+ - No resting background on unselected items (transparent)
39
+
40
+ ## Examples
41
+ ```html
42
+ <!-- Basic list with selection -->
43
+ <schmancy-list>
44
+ ${repeat(items, item => item.id, item => html`
45
+ <schmancy-list-item
46
+ ?selected=${item.id === selectedId}
47
+ @click=${() => this.select(item.id)}
48
+ >
49
+ ${item.name}
50
+ </schmancy-list-item>
51
+ `)}
52
+ </schmancy-list>
53
+
54
+ <!-- List with leading/trailing slots -->
55
+ <schmancy-list surface="surface">
56
+ <schmancy-list-item>
57
+ <schmancy-icon slot="leading">person</schmancy-icon>
58
+ User Name
59
+ <schmancy-icon slot="trailing">chevron_right</schmancy-icon>
60
+ </schmancy-list-item>
61
+ </schmancy-list>
62
+
63
+ <!-- Readonly list -->
64
+ <schmancy-list>
65
+ <schmancy-list-item readonly>Display only</schmancy-list-item>
66
+ </schmancy-list>
67
+ ```
@@ -0,0 +1,102 @@
1
+ # Schmancy Mailbox
2
+
3
+ > Full email composition + campaign management system. Orchestrates recipients, templates, editor, attachments, and preview.
4
+
5
+ ## Components
6
+
7
+ | Tag | Purpose |
8
+ |-----|---------|
9
+ | `schmancy-mailbox` | Top-level orchestrator |
10
+ | `schmancy-email-editor` | Subject + body composition |
11
+ | `schmancy-email-viewer` | HTML/plaintext preview |
12
+ | `schmancy-email-recipients` | Recipient list with import/CSV |
13
+ | `schmancy-email-layout-selector` | Layout template picker |
14
+ | `schmancy-email-template-picker` | Email template browser |
15
+
16
+ ## Usage
17
+ ```html
18
+ <schmancy-mailbox
19
+ .config=${{
20
+ sendEndpoint: '/api/emails/send',
21
+ uploadEndpoint: '/api/uploads',
22
+ authenticateRequest: (req) => ({ ...req, headers: { Authorization: `Bearer ${token}` } })
23
+ }}
24
+ .templates=${myTemplates}
25
+ .importSources=${[
26
+ { id: 'ticketholders', label: 'Ticket holders', icon: 'confirmation_number', handler: loadTicketHolders },
27
+ { id: 'waitlist', label: 'Waitlist', icon: 'hourglass_empty', handler: loadWaitlist },
28
+ ]}
29
+ ></schmancy-mailbox>
30
+ ```
31
+
32
+ ## schmancy-mailbox Properties
33
+ | Property | Type | Default | Description |
34
+ |----------|------|---------|-------------|
35
+ | `config` | `EmailComposeConfig` | `{}` | Endpoints, auth, upload handlers |
36
+ | `templates` | `EmailTemplate[]` | `[]` | Available email templates |
37
+ | `importSources` | `ImportSource[]` | `[]` | Import buttons shown in recipients panel |
38
+ | `disabled` | boolean | `false` | Disable all interactions |
39
+ | `recipientsTitle` | string | `'Recipients'` | Recipients panel heading |
40
+ | `recipientsEmptyTitle` | string | `'No recipients yet'` | Empty state title |
41
+ | `recipientsEmptyMessage` | string | `'Import from sources or upload a CSV'` | Empty state body |
42
+ | `enableCsvImport` | boolean | `true` | Allow CSV file import |
43
+ | `enableDragDrop` | boolean | `true` | Accept dropped CSV files |
44
+
45
+ ## Key Types
46
+ ```typescript
47
+ interface EmailTemplate { id, name, subject, body, category?, thumbnail? }
48
+
49
+ interface EmailComposeConfig {
50
+ sendEndpoint?: string
51
+ templatesEndpoint?: string
52
+ uploadEndpoint?: string
53
+ authenticateRequest?: (req: RequestInit) => RequestInit
54
+ uploadHandler?: (file: File) => Promise<string>
55
+ imageUploadHandler?: (file: File) => Promise<string>
56
+ }
57
+
58
+ interface SendEmailRequest {
59
+ recipients: string[]
60
+ subject: string
61
+ body: string
62
+ attachments: EmailAttachment[]
63
+ templateId?: string | null
64
+ }
65
+
66
+ interface ImportSource {
67
+ id: string
68
+ label: string
69
+ icon: string // Material icon name
70
+ handler: () => void // Populates recipients via events
71
+ }
72
+ ```
73
+
74
+ ## Events
75
+ | Event | Detail | Where |
76
+ |-------|--------|-------|
77
+ | `emails-imported` | `{ emails, source }` | Recipients |
78
+ | `recipient-removed` | `{ email }` | Recipients |
79
+ | `recipients-cleared` | `{}` | Recipients |
80
+ | `selection-changed` | `{ selectedEmails }` | Recipients |
81
+ | `compose-changed` | `{ subject, body, templateId, attachments }` | Editor |
82
+ | `send-email` | `{ request: SendEmailRequest }` | Mailbox — consumer POSTs to backend |
83
+ | `send-error` | `{ error }` | Mailbox |
84
+
85
+ ## Recipients Flow
86
+ 1. User clicks an `ImportSource` button (or drops a CSV).
87
+ 2. CSV is parsed → `validEmails`, `invalidEmails`, `duplicates` reported.
88
+ 3. `emails-imported` event fires; mailbox merges into selected recipients.
89
+ 4. User edits subject/body via email-editor; changes bubble as `compose-changed`.
90
+ 5. On send, `send-email` fires with the full `SendEmailRequest` — parent handles network.
91
+
92
+ ## Minimal Integration
93
+ ```typescript
94
+ <schmancy-mailbox
95
+ .config=${config}
96
+ .templates=${templates}
97
+ @send-email=${async (e: CustomEvent<{ request: SendEmailRequest }>) => {
98
+ const result = await api.sendCampaign(e.detail.request)
99
+ if (!result.success) this.dispatchError(result.message)
100
+ }}
101
+ ></schmancy-mailbox>
102
+ ```
@@ -0,0 +1,55 @@
1
+ # schmancy-map
2
+
3
+ > Google Maps component. Accepts address string (auto-geocoded) or exact lat/lng. Singleton-loaded Maps SDK.
4
+
5
+ ## Usage
6
+ ```html
7
+ <!-- By address -->
8
+ <schmancy-map
9
+ address="Times Square, New York"
10
+ api-key="YOUR_GOOGLE_MAPS_KEY"
11
+ ></schmancy-map>
12
+
13
+ <!-- By coordinates -->
14
+ <schmancy-map
15
+ .latitude=${40.758}
16
+ .longitude=${-73.985}
17
+ .zoom=${15}
18
+ type="satellite"
19
+ api-key="YOUR_GOOGLE_MAPS_KEY"
20
+ ></schmancy-map>
21
+ ```
22
+
23
+ ## Properties
24
+ | Property | Type | Default | Description |
25
+ |----------|------|---------|-------------|
26
+ | `address` | string | — | Location for geocoding (e.g. `"Eiffel Tower, Paris"`) |
27
+ | `latitude` | number | — | Precise latitude (takes precedence over address) |
28
+ | `longitude` | number | — | Precise longitude |
29
+ | `zoom` | number | default Maps default | Map zoom level |
30
+ | `type` | `'roadmap' \| 'satellite' \| 'hybrid' \| 'terrain'` | `'roadmap'` | Map style |
31
+ | `height` | string | — | Custom height (e.g. `'400px'`, `'60vh'`) |
32
+ | `apiKey` | string | **required** | Google Maps API key |
33
+
34
+ ## Behavior
35
+ - The first `schmancy-map` instance triggers a singleton script load of `maps.googleapis.com/maps/api/js` with the `places` library.
36
+ - Subsequent instances reuse the same loader — no duplicate network requests.
37
+ - If `address` is provided without coordinates, the built-in `Geocoder` resolves it before rendering.
38
+ - Errors (invalid key, unauthorized domain, geocode failure) render a fallback message inside the host.
39
+
40
+ ## Setup
41
+ 1. Acquire a Google Maps JavaScript API key from Google Cloud Console.
42
+ 2. Enable the **Maps JavaScript API** and **Geocoding API**.
43
+ 3. Add your domain to the authorized list.
44
+
45
+ ## Example
46
+ ```html
47
+ <schmancy-surface type="subtle" rounded="all">
48
+ <schmancy-map
49
+ address="1600 Amphitheatre Parkway, Mountain View, CA"
50
+ height="320px"
51
+ type="hybrid"
52
+ .apiKey=${import.meta.env.VITE_GOOGLE_MAPS_KEY}
53
+ ></schmancy-map>
54
+ </schmancy-surface>
55
+ ```
@@ -0,0 +1,39 @@
1
+ # schmancy-menu
2
+
3
+ > Context menu that opens a dialog with menu items positioned at the click location.
4
+
5
+ ## Usage
6
+ ```html
7
+ <schmancy-menu>
8
+ <schmancy-button slot="trigger">Actions</schmancy-button>
9
+ <schmancy-menu-item @click=${() => edit()}>Edit</schmancy-menu-item>
10
+ <schmancy-menu-item @click=${() => remove()}>Delete</schmancy-menu-item>
11
+ </schmancy-menu>
12
+ ```
13
+
14
+ ## Slots
15
+ | Slot | Description |
16
+ |------|-------------|
17
+ | trigger | Button that opens the menu (clicks open dialog) |
18
+ | button | Legacy alias for trigger |
19
+ | default | Menu items or custom content |
20
+
21
+ ## schmancy-menu-item
22
+ Auto-dismisses the dialog when clicked. Renders as a `schmancy-list-item` internally.
23
+
24
+ ## Examples
25
+ ```html
26
+ <!-- With icon button trigger -->
27
+ <schmancy-menu>
28
+ <schmancy-icon-button slot="trigger">more_vert</schmancy-icon-button>
29
+ <schmancy-menu-item @click=${() => duplicate()}>Duplicate</schmancy-menu-item>
30
+ <schmancy-menu-item @click=${() => archive()}>Archive</schmancy-menu-item>
31
+ </schmancy-menu>
32
+
33
+ <!-- Default trigger (three-dot icon button) -->
34
+ <schmancy-menu>
35
+ <schmancy-menu-item @click=${() => share()}>Share</schmancy-menu-item>
36
+ </schmancy-menu>
37
+ ```
38
+
39
+ Uses `$dialog.component()` internally. Menu items auto-dismiss the dialog. Custom components in the default slot must call `$dialog.dismiss()` manually.
@@ -0,0 +1,99 @@
1
+ # `$LitElement`
2
+
3
+ Base class for Schmancy components. Extends `LitElement` with RxJS cleanup, Tailwind support, and cross-shadow discovery.
4
+
5
+ ## Usage
6
+
7
+ ```typescript
8
+ import { $LitElement } from '@mhmo91/schmancy/mixins'
9
+ import { customElement } from 'lit/decorators.js'
10
+ import { css, html } from 'lit'
11
+
12
+ @customElement('my-component')
13
+ class MyComponent extends $LitElement(css`
14
+ :host { display: block; }
15
+ `) {
16
+ render() {
17
+ return html`<slot></slot>`
18
+ }
19
+ }
20
+ ```
21
+
22
+ `$LitElement(style?)` accepts either a Lit `css` tagged literal or a string (e.g. SCSS imported with `?inline`).
23
+
24
+ ## Provided members
25
+
26
+ ### `disconnecting: Subject<void>`
27
+ Emits once when the element disconnects. Pair with `takeUntil` for cleanup:
28
+
29
+ ```typescript
30
+ connectedCallback() {
31
+ super.connectedCallback()
32
+ someObservable$.pipe(
33
+ takeUntil(this.disconnecting),
34
+ ).subscribe()
35
+ }
36
+ ```
37
+
38
+ ### `classMap(obj)`
39
+ Wraps Lit's `classMap` so keys can be space-separated:
40
+
41
+ ```typescript
42
+ html`<div class=${this.classMap({
43
+ 'flex items-center gap-2': true,
44
+ 'opacity-50': this.disabled,
45
+ })}></div>`
46
+ ```
47
+
48
+ Must be the only expression in `class=` — don't mix with string interpolation.
49
+
50
+ ### `styleMap(obj)`
51
+ Passthrough to Lit's `styleMap`.
52
+
53
+ ```typescript
54
+ html`<div style=${this.styleMap({ width: `${w}px` })}></div>`
55
+ ```
56
+
57
+ ### `discover<T>(tag): Observable<T | null>`
58
+ Find another Schmancy component by tag via event-based handshake. Works across shadow DOM.
59
+
60
+ ```typescript
61
+ this.discover<SchmancyDialog>('schmancy-dialog').pipe(
62
+ takeUntil(this.disconnecting),
63
+ ).subscribe(dialog => dialog?.setDefaultAction('confirm'))
64
+ ```
65
+
66
+ ### Auto-response to discovery
67
+ Every `$LitElement` responds to `{its-tag}-where-are-you` events with `{its-tag}-here-i-am` carrying `{ detail: { component: this } }`.
68
+
69
+ ## Component skeleton
70
+
71
+ ```typescript
72
+ @customElement('my-component')
73
+ export class MyComponent extends $LitElement(css`:host { display: block }`) {
74
+ @property({ type: String, reflect: true }) variant: 'a' | 'b' = 'a'
75
+ @state() private _open = false
76
+
77
+ connectedCallback() {
78
+ super.connectedCallback()
79
+ // subscriptions here, with takeUntil(this.disconnecting)
80
+ }
81
+
82
+ render() {
83
+ return html`<slot></slot>`
84
+ }
85
+ }
86
+
87
+ declare global {
88
+ interface HTMLElementTagNameMap {
89
+ 'my-component': MyComponent
90
+ }
91
+ }
92
+ ```
93
+
94
+ ## Rules
95
+
96
+ - Call `super.connectedCallback()` / `super.disconnectedCallback()` when overriding.
97
+ - All RxJS subscriptions end with `.pipe(takeUntil(this.disconnecting))`.
98
+ - Register the tag in `HTMLElementTagNameMap` for TypeScript.
99
+ - Don't mix `classMap` with string interpolation in the same attribute.
@@ -0,0 +1,52 @@
1
+ # schmancy-nav-drawer
2
+
3
+ > Responsive navigation drawer that switches between push (desktop) and overlay (mobile) modes.
4
+
5
+ ## Usage
6
+ ```html
7
+ <schmancy-nav-drawer breakpoint="md">
8
+ <schmancy-nav-drawer-navbar>
9
+ <!-- Navigation rail or sidebar content -->
10
+ </schmancy-nav-drawer-navbar>
11
+ <schmancy-nav-drawer-content>
12
+ <!-- Main content area -->
13
+ </schmancy-nav-drawer-content>
14
+ </schmancy-nav-drawer>
15
+ ```
16
+
17
+ ## Properties (schmancy-nav-drawer)
18
+ | Property | Type | Default | Description |
19
+ |----------|------|---------|-------------|
20
+ | breakpoint | `'sm'\|'md'\|'lg'\|'xl'` | `'md'` | Responsive breakpoint for push/overlay switch |
21
+ | open | `'open'\|'close'` | auto | Drawer state |
22
+ | fullscreen | boolean | `false` | Fullscreen mode (hides sidebar) |
23
+
24
+ ## Child Components
25
+ | Component | Description |
26
+ |-----------|-------------|
27
+ | `schmancy-nav-drawer-navbar` | Sidebar container (auto-hides in overlay mode) |
28
+ | `schmancy-nav-drawer-content` | Main content area (fills remaining space) |
29
+
30
+ ## Behavior
31
+ - Above breakpoint: push mode (sidebar pushes content)
32
+ - Below breakpoint: overlay mode (sidebar overlays content)
33
+ - Toggle via `SchmancyEvents.NavDrawer_toggle` event
34
+ - Grid layout: `auto 1fr` columns
35
+
36
+ ## Examples
37
+ ```html
38
+ <schmancy-nav-drawer breakpoint="lg">
39
+ <schmancy-nav-drawer-navbar>
40
+ <schmancy-navigation-rail activeIndex="0">
41
+ <schmancy-navigation-rail-item icon="home" label="Home">
42
+ </schmancy-navigation-rail-item>
43
+ </schmancy-navigation-rail>
44
+ </schmancy-nav-drawer-navbar>
45
+ <schmancy-nav-drawer-content>
46
+ <schmancy-area name="main" default="home-page">
47
+ <schmancy-route when="home-page" .component=${lazy(() => import('./home'))}>
48
+ </schmancy-route>
49
+ </schmancy-area>
50
+ </schmancy-nav-drawer-content>
51
+ </schmancy-nav-drawer>
52
+ ```