@joewinke/jatui 0.1.26 → 0.1.28

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.
@@ -17,8 +17,24 @@ type $$ComponentProps = {
17
17
  displayValue?: string;
18
18
  colorFn?: (value: string) => string | undefined;
19
19
  footer?: Snippet;
20
+ /** 'chip' renders a compact TopBar-style pill instead of the full-width trigger */
21
+ variant?: 'default' | 'chip';
22
+ /** 'sm' renders a smaller trigger matching text-[11px] compact UIs */
23
+ size?: 'sm' | 'md';
24
+ /** Open the panel above the trigger instead of below */
25
+ dropup?: boolean;
26
+ /** Render panel with position:fixed using viewport coordinates — escapes table/overflow stacking contexts */
27
+ appendToBody?: boolean;
28
+ /** Show an × button on the trigger when a value is selected */
29
+ clearable?: boolean;
30
+ /** Extra attributes forwarded to the trigger button (e.g. data-rail-id) */
31
+ triggerAttrs?: Record<string, string>;
32
+ /** Render inline without a trigger button — panel content is always visible, no dropdown behavior */
33
+ embedded?: boolean;
20
34
  onChange: (value: string) => void;
21
35
  };
22
- declare const SearchDropdown: import("svelte").Component<$$ComponentProps, {}, "">;
36
+ declare const SearchDropdown: import("svelte").Component<$$ComponentProps, {
37
+ openDropdown: () => void;
38
+ }, "">;
23
39
  type SearchDropdown = ReturnType<typeof SearchDropdown>;
24
40
  export default SearchDropdown;
@@ -4,6 +4,8 @@
4
4
  *
5
5
  * Exports `clear()` and `isEmpty()` methods. Outputs signature as base64 PNG data URL
6
6
  * via the bindable `signatureData` prop or `onSignatureChange` callback.
7
+ *
8
+ * Pass `disabled` to render a non-interactive, dimmed pad (e.g. form-builder previews).
7
9
  */
8
10
  import { onMount } from 'svelte';
9
11
 
@@ -17,6 +19,7 @@
17
19
  label = 'Signature',
18
20
  required = false,
19
21
  placeholder = 'Sign here',
22
+ disabled = false,
20
23
  }: {
21
24
  signatureData?: string;
22
25
  onSignatureChange?: (data: string) => void;
@@ -27,6 +30,7 @@
27
30
  label?: string;
28
31
  required?: boolean;
29
32
  placeholder?: string;
33
+ disabled?: boolean;
30
34
  } = $props();
31
35
 
32
36
  let canvas: HTMLCanvasElement;
@@ -68,6 +72,7 @@
68
72
  }
69
73
 
70
74
  function startDrawing(e: MouseEvent | TouchEvent) {
75
+ if (disabled) return;
71
76
  e.preventDefault();
72
77
  isDrawing = true;
73
78
  const coords = getCoordinates(e);
@@ -76,7 +81,7 @@
76
81
  }
77
82
 
78
83
  function draw(e: MouseEvent | TouchEvent) {
79
- if (!isDrawing || !ctx) return;
84
+ if (disabled || !isDrawing || !ctx) return;
80
85
  e.preventDefault();
81
86
 
82
87
  const coords = getCoordinates(e);
@@ -145,19 +150,21 @@
145
150
  <span class="text-error">*</span>
146
151
  {/if}
147
152
  </span>
148
- <button type="button" onclick={() => clear()} class="btn btn-ghost btn-xs">
149
- Clear
150
- </button>
153
+ {#if !disabled}
154
+ <button type="button" onclick={() => clear()} class="btn btn-ghost btn-xs">
155
+ Clear
156
+ </button>
157
+ {/if}
151
158
  </div>
152
159
  {/if}
153
160
 
154
161
  <div class="rounded-lg bg-base-100 overflow-hidden relative transition-all duration-300
155
- {hasDrawn ? 'border-2 border-success/60' : 'border-2 border-base-300'}">
162
+ {disabled ? 'opacity-50 border-2 border-base-300' : hasDrawn ? 'border-2 border-success/60' : 'border-2 border-base-300'}">
156
163
  <canvas
157
164
  bind:this={canvas}
158
165
  {width}
159
166
  {height}
160
- class="w-full touch-none cursor-crosshair"
167
+ class="w-full touch-none {disabled ? 'cursor-not-allowed' : 'cursor-crosshair'}"
161
168
  ></canvas>
162
169
 
163
170
  {#if !hasDrawn}
@@ -8,6 +8,7 @@ type $$ComponentProps = {
8
8
  label?: string;
9
9
  required?: boolean;
10
10
  placeholder?: string;
11
+ disabled?: boolean;
11
12
  };
12
13
  declare const SignaturePad: import("svelte").Component<$$ComponentProps, {
13
14
  clear: () => void;
@@ -53,12 +53,18 @@
53
53
  defaultTheme = 'light',
54
54
  storageKey = 'theme',
55
55
  onThemeChange,
56
+ compact = false,
57
+ inline = false,
56
58
  }: {
57
59
  dropUp?: boolean;
58
60
  themes?: ThemeOption[];
59
61
  defaultTheme?: string;
60
62
  storageKey?: string;
61
63
  onThemeChange?: (theme: string) => void;
64
+ /** Icon-only trigger (label hidden, shown as tooltip). Dropdown mode only. */
65
+ compact?: boolean;
66
+ /** Render a collapsible `<details>` theme list instead of a dropdown — for embedding inside another menu. */
67
+ inline?: boolean;
62
68
  } = $props();
63
69
 
64
70
  // svelte-ignore state_referenced_locally
@@ -104,6 +110,51 @@
104
110
  }
105
111
  </script>
106
112
 
113
+ {#if inline}
114
+ <!-- Inline mode: collapsible theme list with color previews, for embedding inside another menu -->
115
+ <details class="w-full theme-details">
116
+ <summary class="flex items-center gap-2 cursor-pointer hover:bg-base-300 transition-colors px-2 py-1.5 rounded list-none">
117
+ <div
118
+ data-theme={currentTheme}
119
+ class="bg-base-100 grid shrink-0 grid-cols-2 gap-0.5 rounded p-0.5 w-4 h-4 transition-transform duration-300 ease-out"
120
+ class:theme-spin={isAnimating}
121
+ >
122
+ <div class="bg-base-content size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 0ms"></div>
123
+ <div class="bg-primary size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 50ms"></div>
124
+ <div class="bg-secondary size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 100ms"></div>
125
+ <div class="bg-accent size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 150ms"></div>
126
+ </div>
127
+ <span class="text-[0.75rem] flex-1 text-left text-base-content/70">{currentThemeLabel}</span>
128
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-3 h-3 text-base-content/45 transition-transform duration-200 details-chevron">
129
+ <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
130
+ </svg>
131
+ </summary>
132
+ <div class="max-h-48 overflow-y-auto mt-1 ml-2 border-l border-base-content/10 pl-2">
133
+ {#each themes as theme}
134
+ <button
135
+ class="flex items-center gap-2 w-full px-2 py-1 rounded transition-colors hover:bg-base-300 {currentTheme === theme.name ? 'bg-primary/20' : ''}"
136
+ onclick={() => handleThemeChange(theme.name)}
137
+ >
138
+ <div
139
+ data-theme={theme.name}
140
+ class="bg-base-100 grid shrink-0 grid-cols-2 gap-0.5 rounded p-0.5 w-4 h-4"
141
+ >
142
+ <div class="bg-base-content size-1 rounded-full"></div>
143
+ <div class="bg-primary size-1 rounded-full"></div>
144
+ <div class="bg-secondary size-1 rounded-full"></div>
145
+ <div class="bg-accent size-1 rounded-full"></div>
146
+ </div>
147
+ <span class="text-[0.75rem] text-base-content/70 flex-1 text-left">{theme.label}</span>
148
+ {#if currentTheme === theme.name}
149
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-3 h-3 text-primary">
150
+ <path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"></path>
151
+ </svg>
152
+ {/if}
153
+ </button>
154
+ {/each}
155
+ </div>
156
+ </details>
157
+ {:else}
107
158
  <div class="dropdown dropdown-end" class:dropdown-top={dropUp}>
108
159
  <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
109
160
  <div
@@ -111,7 +162,7 @@
111
162
  role="button"
112
163
  class="flex items-center gap-2 cursor-pointer hover:bg-base-300 transition-colors px-2 py-1 rounded"
113
164
  aria-label="Change Theme"
114
- title="Change Theme"
165
+ title={compact ? currentThemeLabel : 'Change Theme'}
115
166
  >
116
167
  <div
117
168
  data-theme={currentTheme}
@@ -123,7 +174,9 @@
123
174
  <div class="bg-secondary size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 100ms"></div>
124
175
  <div class="bg-accent size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 150ms"></div>
125
176
  </div>
126
- <span class="text-sm font-medium transition-opacity duration-200" class:opacity-50={isAnimating}>{currentThemeLabel}</span>
177
+ {#if !compact}
178
+ <span class="text-sm font-medium transition-opacity duration-200" class:opacity-50={isAnimating}>{currentThemeLabel}</span>
179
+ {/if}
127
180
  </div>
128
181
 
129
182
  <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
@@ -171,8 +224,14 @@
171
224
  </ul>
172
225
  </div>
173
226
  </div>
227
+ {/if}
174
228
 
175
229
  <style>
230
+ /* Rotate chevron when the inline details list is open */
231
+ .theme-details[open] .details-chevron {
232
+ transform: rotate(180deg);
233
+ }
234
+
176
235
  /* NOTE: DaisyUI v5.0.0 has a stuck-animation bug on `.dropdown-content`
177
236
  (`@keyframes dropdown { 0% { opacity: 0 } }` pins opacity to 0). If the
178
237
  theme dropdown opens but stays invisible, the consuming project needs
@@ -8,6 +8,10 @@ type $$ComponentProps = {
8
8
  defaultTheme?: string;
9
9
  storageKey?: string;
10
10
  onThemeChange?: (theme: string) => void;
11
+ /** Icon-only trigger (label hidden, shown as tooltip). Dropdown mode only. */
12
+ compact?: boolean;
13
+ /** Render a collapsible `<details>` theme list instead of a dropdown — for embedding inside another menu. */
14
+ inline?: boolean;
11
15
  };
12
16
  declare const ThemeSelector: import("svelte").Component<$$ComponentProps, {}, "">;
13
17
  type ThemeSelector = ReturnType<typeof ThemeSelector>;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { railNav, createRailNav, cycle } from './actions/railNav';
2
2
  export type { RailNavOptions, RailNavController } from './actions/railNav';
3
+ export { listNav, createListNav } from './actions/listNav';
4
+ export type { ListNavOptions, ListNavController } from './actions/listNav';
3
5
  export { collapse } from './actions/collapse';
4
6
  export type { CollapseOptions } from './actions/collapse';
5
7
  export { default as Collapse } from './components/Collapse.svelte';
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // Actions
2
2
  export { railNav, createRailNav, cycle } from './actions/railNav';
3
+ export { listNav, createListNav } from './actions/listNav';
3
4
  export { collapse } from './actions/collapse';
4
5
  export { default as Collapse } from './components/Collapse.svelte';
5
6
  // Components — Session primitives (from JAT IDE, jat-jj79f.16 G1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joewinke/jatui",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "private": false,
5
5
  "description": "Shared Svelte 5 component library for JAT projects",
6
6
  "type": "module",
@@ -20,10 +20,13 @@
20
20
  "README.md"
21
21
  ],
22
22
  "scripts": {
23
- "dev": "vite dev --port 5180",
23
+ "dev": "npm run package && vite dev --port 5180",
24
24
  "build": "vite build",
25
+ "build:site": "npm run package && vite build",
25
26
  "preview": "vite preview",
26
27
  "package": "svelte-package --input src/lib --output dist",
28
+ "check:demos": "node scripts/check-demo-coverage.mjs",
29
+ "guard": "node scripts/check-demo-coverage.mjs",
27
30
  "prepublishOnly": "npm run package"
28
31
  },
29
32
  "peerDependencies": {
@@ -45,6 +48,7 @@
45
48
  },
46
49
  "devDependencies": {
47
50
  "@sveltejs/adapter-auto": "^6.0.0",
51
+ "@sveltejs/adapter-static": "^3.0.10",
48
52
  "@sveltejs/kit": "^2.21.0",
49
53
  "@sveltejs/package": "^2.5.8",
50
54
  "@sveltejs/vite-plugin-svelte": "^5.0.0",