lutra 0.1.68 → 0.1.69

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 (72) hide show
  1. package/dist/components/AspectRatio.svelte +19 -9
  2. package/dist/components/AspectRatio.svelte.d.ts +2 -1
  3. package/dist/components/Avatar.svelte +5 -8
  4. package/dist/components/Close.svelte +24 -27
  5. package/dist/components/Close.svelte.d.ts +2 -0
  6. package/dist/components/ContextTip.svelte +3 -2
  7. package/dist/components/Dialog.svelte +38 -0
  8. package/dist/components/Icon.svelte +2 -2
  9. package/dist/components/IconButton.svelte +10 -22
  10. package/dist/components/Image.svelte +2 -2
  11. package/dist/components/Indicator.svelte +2 -1
  12. package/dist/components/Inset.svelte +13 -0
  13. package/dist/components/Layout.svelte +7 -3
  14. package/dist/components/Layout.svelte.d.ts +3 -2
  15. package/dist/components/MenuDropdown.svelte +12 -2
  16. package/dist/components/MenuItem.svelte +30 -14
  17. package/dist/components/MenuItem.svelte.d.ts +6 -0
  18. package/dist/components/Modal.svelte +36 -20
  19. package/dist/components/Popover.svelte +39 -12
  20. package/dist/components/TabbedContent.svelte +1 -1
  21. package/dist/components/TabbedContentItem.svelte +14 -0
  22. package/dist/components/TabbedContentItem.svelte.d.ts +4 -0
  23. package/dist/components/Table.svelte +69 -0
  24. package/dist/components/Table.svelte.d.ts +7 -0
  25. package/dist/components/Tabs.svelte +44 -36
  26. package/dist/components/Tag.svelte +53 -13
  27. package/dist/components/Tag.svelte.d.ts +4 -0
  28. package/dist/components/Theme.svelte +121 -94
  29. package/dist/components/Theme.svelte.d.ts +7 -6
  30. package/dist/components/Toast.svelte +11 -8
  31. package/dist/components/Tooltip.svelte +17 -10
  32. package/dist/css/1-props.css +64 -51
  33. package/dist/css/2-init.css +503 -0
  34. package/dist/css/{2-base.css → 3-base.css} +42 -131
  35. package/dist/css/{3-typo.css → 4-typo.css} +3 -1
  36. package/dist/css/lutra.css +7 -6
  37. package/dist/css/themes/DefaultTheme.css +16 -4
  38. package/dist/form/Button.svelte +20 -0
  39. package/dist/form/Button.svelte.d.ts +9 -0
  40. package/dist/form/Datepicker.svelte +13 -0
  41. package/dist/form/Datepicker.svelte.d.ts +3 -0
  42. package/dist/form/FieldContent.svelte +18 -9
  43. package/dist/form/FieldError.svelte +1 -1
  44. package/dist/form/Fieldset.svelte +19 -11
  45. package/dist/form/Form.svelte +137 -63
  46. package/dist/form/Form.svelte.d.ts +21 -0
  47. package/dist/form/FormActions.svelte +21 -3
  48. package/dist/form/FormActions.svelte.d.ts +3 -0
  49. package/dist/form/FormSection.svelte +22 -20
  50. package/dist/form/ImageUpload.svelte +50 -30
  51. package/dist/form/ImageUpload.svelte.d.ts +14 -0
  52. package/dist/form/Input.svelte +62 -30
  53. package/dist/form/Input.svelte.d.ts +0 -1
  54. package/dist/form/InputLength.svelte +5 -5
  55. package/dist/form/Label.svelte +6 -6
  56. package/dist/form/LogoUpload.svelte +24 -10
  57. package/dist/form/Select.svelte +23 -10
  58. package/dist/form/Select.svelte.d.ts +6 -6
  59. package/dist/form/Textarea.svelte +11 -1
  60. package/dist/form/client.svelte.js +0 -2
  61. package/dist/state/Persisted.svelte.d.ts +6 -0
  62. package/dist/state/Persisted.svelte.js +29 -0
  63. package/dist/state/theme.svelte.d.ts +7 -0
  64. package/dist/state/theme.svelte.js +14 -0
  65. package/dist/types.d.ts +6 -23
  66. package/dist/types.js +0 -17
  67. package/dist/util/color.js +2 -2
  68. package/package.json +5 -4
  69. package/dist/config.d.ts +0 -30
  70. package/dist/config.js +0 -18
  71. /package/dist/css/{4-layout.css → 5-layout.css} +0 -0
  72. /package/dist/css/{5-media.css → 6-media.css} +0 -0
@@ -6,6 +6,19 @@
6
6
  import { ZodType } from "zod";
7
7
  import FieldContent from "./FieldContent.svelte";
8
8
 
9
+ /**
10
+ * @description
11
+ * A styled `<select>` element with a custom dropdown arrow, integrated with the
12
+ * Lutra form system for validation and error display. Accepts options via props
13
+ * or as `<option>` children via a snippet.
14
+ *
15
+ * @example
16
+ * <Select name="country" label="Country" options={['US', 'UK', 'DE']} />
17
+ * <Select name="role" label="Role" options={[
18
+ * { value: 'admin', label: 'Administrator' },
19
+ * { value: 'user', label: 'User' },
20
+ * ]} />
21
+ */
9
22
  let {
10
23
  autofocus,
11
24
  children,
@@ -32,19 +45,19 @@
32
45
  }: {
33
46
  /** Whether the input should be autofocused. */
34
47
  autofocus?: boolean;
35
- /** Options for the select element. */
48
+ /** Options for the select element rendered as a snippet. */
36
49
  children?: Snippet;
37
- /** Whether the input should be disabled. */
50
+ /** Whether the select should be disabled. */
38
51
  disabled?: boolean;
39
- /** Help text to display below the input. */
52
+ /** Help text to display below the select. */
40
53
  help?: string | Snippet;
41
54
  /** A random id is generated if not provided. */
42
55
  id?: string;
43
- /** The label for the input */
56
+ /** The label for the select. */
44
57
  label?: string | Snippet
45
- /** Context tooltip for a label. Renders with a questionmark using ContextTip. */
58
+ /** Context tooltip for the label. Renders with a questionmark using ContextTip. */
46
59
  labelTip?: string | Snippet;
47
- /** The name of the input element. */
60
+ /** The name of the select element. */
48
61
  name: string;
49
62
  /** The onblur event handler */
50
63
  onblur?: (e: FocusEvent) => void;
@@ -160,10 +173,10 @@
160
173
  width: 100%;
161
174
  }
162
175
  select {
163
- padding-inline-end: calc(1.5em + 10px);
176
+ padding-inline-end: 2.5em;
164
177
  width: 100%;
165
178
  appearance: none;
166
- border: none !important;
179
+ border: none;
167
180
  border-radius: 0;
168
181
  grid-area: select;
169
182
  background: none;
@@ -174,11 +187,11 @@
174
187
  display: flex;
175
188
  align-items: center;
176
189
  justify-content: end;
177
- padding-inline-end: 0.75em;
190
+ padding-inline-end: var(--field-padding-inline);
178
191
  }
179
192
  svg {
180
193
  display: block;
181
- color: var(--field-text-color, light-dark(black, white));
194
+ color: var(--field-color);
182
195
  }
183
196
  select:focus-visible {
184
197
  outline: none;
@@ -2,19 +2,19 @@ import { type Snippet } from "svelte";
2
2
  type $$ComponentProps = {
3
3
  /** Whether the input should be autofocused. */
4
4
  autofocus?: boolean;
5
- /** Options for the select element. */
5
+ /** Options for the select element rendered as a snippet. */
6
6
  children?: Snippet;
7
- /** Whether the input should be disabled. */
7
+ /** Whether the select should be disabled. */
8
8
  disabled?: boolean;
9
- /** Help text to display below the input. */
9
+ /** Help text to display below the select. */
10
10
  help?: string | Snippet;
11
11
  /** A random id is generated if not provided. */
12
12
  id?: string;
13
- /** The label for the input */
13
+ /** The label for the select. */
14
14
  label?: string | Snippet;
15
- /** Context tooltip for a label. Renders with a questionmark using ContextTip. */
15
+ /** Context tooltip for the label. Renders with a questionmark using ContextTip. */
16
16
  labelTip?: string | Snippet;
17
- /** The name of the input element. */
17
+ /** The name of the select element. */
18
18
  name: string;
19
19
  /** The onblur event handler */
20
20
  onblur?: (e: FocusEvent) => void;
@@ -9,6 +9,16 @@
9
9
  import { getFromObjWithStringPath } from "./form.js";
10
10
  import { ZodType } from "zod";
11
11
 
12
+ /**
13
+ * @description
14
+ * A styled textarea component with optional auto-resize, copy-to-clipboard, and
15
+ * prefix/suffix content. Integrates with the Lutra form system for validation
16
+ * and error display.
17
+ *
18
+ * @example
19
+ * <Textarea name="bio" label="Biography" placeholder="Tell us about yourself..." />
20
+ * <Textarea name="notes" label="Notes" autoresize maxlength={500} />
21
+ */
12
22
  let {
13
23
  autofocus,
14
24
  autocapitalize,
@@ -43,7 +53,7 @@
43
53
  prefix,
44
54
  readonly,
45
55
  required,
46
- resize = true,
56
+ resize,
47
57
  shape = 'rounded',
48
58
  step,
49
59
  style,
@@ -85,8 +85,6 @@ function getTypedValue(el) {
85
85
  export function fieldChange(form, name, el, validator, onchange) {
86
86
  return async function (e) {
87
87
  if (form && form.fields[name]) {
88
- console.log('fieldChange', name, el()?.value, form.data[name]);
89
- // Update the form data with the new value.
90
88
  form.data[name] = getTypedValue(el());
91
89
  // if the value is the same as the original value, then the field is not tainted
92
90
  form.fields[name].tainted = form.data[name] !== form.originalData[name];
@@ -0,0 +1,6 @@
1
+ export declare class Persisted<T extends string = string> {
2
+ #private;
3
+ constructor(key: string, fallback: T, storage?: Storage | undefined);
4
+ get current(): T;
5
+ set current(v: T);
6
+ }
@@ -0,0 +1,29 @@
1
+ import { on } from 'svelte/events';
2
+ import { createSubscriber } from 'svelte/reactivity';
3
+ export class Persisted {
4
+ #key;
5
+ #storage;
6
+ #fallback;
7
+ #version = $state(0);
8
+ #subscribe = createSubscriber((update) => {
9
+ return on(window, 'storage', (e) => {
10
+ if (e.key === this.#key) {
11
+ update();
12
+ }
13
+ });
14
+ });
15
+ constructor(key, fallback, storage = typeof localStorage === 'undefined' ? undefined : localStorage) {
16
+ this.#key = key;
17
+ this.#fallback = fallback;
18
+ this.#storage = storage;
19
+ }
20
+ get current() {
21
+ this.#subscribe(); // handle cross-tab updates
22
+ this.#version; // handle same-tab updates
23
+ return this.#storage?.getItem(this.#key) ?? this.#fallback;
24
+ }
25
+ set current(v) {
26
+ this.#storage?.setItem(this.#key, v);
27
+ this.#version += 1;
28
+ }
29
+ }
@@ -0,0 +1,7 @@
1
+ declare class Theme {
2
+ #private;
3
+ get current(): "light" | "dark";
4
+ set current(value: 'light' | 'dark');
5
+ }
6
+ export declare const theme: Theme;
7
+ export {};
@@ -0,0 +1,14 @@
1
+ import { MediaQuery } from 'svelte/reactivity';
2
+ import { Persisted } from './Persisted.svelte';
3
+ class Theme {
4
+ #preference = new Persisted('lutra.theme', 'system');
5
+ #query = new MediaQuery('prefers-color-scheme: dark');
6
+ #system = $derived(this.#query.current ? 'dark' : 'light');
7
+ get current() {
8
+ return this.#preference.current === 'system' ? this.#system : this.#preference.current;
9
+ }
10
+ set current(value) {
11
+ this.#preference.current = value === this.#system ? 'system' : value;
12
+ }
13
+ }
14
+ export const theme = new Theme();
package/dist/types.d.ts CHANGED
@@ -6,42 +6,25 @@ import { type Snippet } from "svelte";
6
6
  * @template T - Tuple of argument types passed to the render function
7
7
  */
8
8
  export type RenderFn<T extends any[] = []> = (...args: T) => ReturnType<Snippet<T>>;
9
- export interface LutraConfig {
9
+ export type LutraTheme = 'light' | 'dark' | 'system' | 'invert' | undefined;
10
+ export interface LutraThemeConfig {
10
11
  /**
11
12
  * The default theme to use.
12
13
  */
13
- theme?: 'light' | 'dark' | 'auto' | 'inherit';
14
+ theme?: LutraTheme;
14
15
  /**
15
16
  * Theme color for the theme-color meta tag. Only used in the root theme.
16
17
  * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color
17
18
  */
18
- themeColor: {
19
+ themeColor?: {
19
20
  light?: string;
20
21
  dark?: string;
21
22
  };
22
- /**
23
- * Whether to show a background color or image on the components.
24
- */
25
- background?: boolean;
26
- /**
27
- * Whether to use a contained layout (with borders and rounded corners).
28
- */
29
- contained?: boolean;
30
23
  }
31
- /**
32
- * Default Lutra config.
33
- */
34
- export declare const defaultConfig: LutraConfig;
35
- export declare const setConfig: (config: Partial<LutraConfig>) => void;
36
24
  export declare enum LutraContext {
37
- Theme = "lutra.theme",
38
- Config = "lutra.config",
39
- SecChPrefersColorScheme = "sec-ch-prefers-color-scheme"
25
+ Theme = "lutra.theme"
40
26
  }
41
- export type LutraTheme = 'light' | 'dark' | 'invert' | undefined;
42
27
  export type LutraContextTypeMap = {
43
- [LutraContext.Theme]: LutraTheme;
44
- [LutraContext.Config]: LutraConfig;
45
- [LutraContext.SecChPrefersColorScheme]: 'light' | 'dark' | undefined;
28
+ [LutraContext.Theme]: () => LutraTheme;
46
29
  };
47
30
  export declare function getContextItem<K extends keyof LutraContextTypeMap>(key: K): LutraContextTypeMap[K] | undefined;
package/dist/types.js CHANGED
@@ -1,24 +1,7 @@
1
1
  import { getContext } from "svelte";
2
- /**
3
- * Default Lutra config.
4
- */
5
- export const defaultConfig = {
6
- theme: undefined,
7
- themeColor: {
8
- light: '#fff',
9
- dark: '#000',
10
- },
11
- background: true,
12
- contained: true,
13
- };
14
- export const setConfig = (config) => {
15
- Object.assign(config, config);
16
- };
17
2
  export var LutraContext;
18
3
  (function (LutraContext) {
19
4
  LutraContext["Theme"] = "lutra.theme";
20
- LutraContext["Config"] = "lutra.config";
21
- LutraContext["SecChPrefersColorScheme"] = "sec-ch-prefers-color-scheme";
22
5
  })(LutraContext || (LutraContext = {}));
23
6
  export function getContextItem(key) {
24
7
  return getContext(key);
@@ -91,7 +91,7 @@ export const isStatusColor = (value) => {
91
91
  */
92
92
  export function getStatusColorVar(color, fallback) {
93
93
  if (isStatusColor(color)) {
94
- return `var(--status-${color})`;
94
+ return `var(--status-${color}-color)`;
95
95
  }
96
- return color || fallback || "var(--status-default)";
96
+ return color || fallback || "var(--status-default-color)";
97
97
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lutra",
3
- "version": "0.1.68",
3
+ "version": "0.1.69",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "bump-and-publish:patch": "pnpm version:patch && pnpm build && npm publish",
@@ -36,16 +36,17 @@
36
36
  }
37
37
  },
38
38
  "peerDependencies": {
39
- "svelte": "^5.0.0"
39
+ "svelte": "^5.0.0",
40
+ "@sveltejs/kit": "^2.50.2"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@sveltejs/adapter-auto": "^7.0.0",
43
44
  "@sveltejs/kit": "^2.50.2",
44
45
  "@sveltejs/package": "^2.5.7",
45
46
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
46
- "@types/node": "^25.2.0",
47
+ "@types/node": "^25.2.3",
47
48
  "publint": "^0.3.17",
48
- "svelte": "^5.49.1",
49
+ "svelte": "^5.50.1",
49
50
  "svelte-check": "^4.3.6",
50
51
  "typescript": "^5.9.3",
51
52
  "vite": "^7.3.1"
package/dist/config.d.ts DELETED
@@ -1,30 +0,0 @@
1
- export interface LutraConfig {
2
- /**
3
- * Whether to show a background color or image on the components.
4
- */
5
- background?: boolean;
6
- /**
7
- * Whether to use a contained layout (with borders and rounded corners).
8
- */
9
- contained?: boolean;
10
- }
11
- export interface LutraRootConfig extends LutraConfig {
12
- /**
13
- * The default theme to use.
14
- */
15
- theme?: 'light' | 'dark' | 'auto' | 'inherit';
16
- /**
17
- * Theme color for the theme-color meta tag. Only used in the root theme.
18
- * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color
19
- */
20
- themeColor?: {
21
- light?: string;
22
- dark?: string;
23
- };
24
- }
25
- /**
26
- * Default Lutra config.
27
- */
28
- export declare const defaultConfig: LutraConfig;
29
- export declare const defaultRootConfig: LutraRootConfig;
30
- export declare const setConfig: (config: Partial<LutraConfig>) => void;
package/dist/config.js DELETED
@@ -1,18 +0,0 @@
1
- /**
2
- * Default Lutra config.
3
- */
4
- export const defaultConfig = {
5
- background: true,
6
- contained: true,
7
- };
8
- export const defaultRootConfig = {
9
- theme: 'auto',
10
- themeColor: {
11
- light: '#fff',
12
- dark: '#000',
13
- },
14
- ...defaultConfig,
15
- };
16
- export const setConfig = (config) => {
17
- Object.assign(config, config);
18
- };
File without changes
File without changes