lithesome 0.23.5 → 0.23.7

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.
package/README.md CHANGED
@@ -9,10 +9,18 @@ An unstyled component library for Svelte 5.
9
9
 
10
10
  ## Install
11
11
 
12
+ ```bash
13
+ npm i -D lithesome
14
+ ```
15
+
12
16
  ```bash
13
17
  pnpm i -D lithesome
14
18
  ```
15
19
 
20
+ ```bash
21
+ bun add -D lithesome
22
+ ```
23
+
16
24
  ## Usage
17
25
 
18
26
  ```svelte
@@ -21,9 +29,7 @@ pnpm i -D lithesome
21
29
  </script>
22
30
 
23
31
  <Menu>
24
- <MenuTrigger>
25
- <button>Auth</button>
26
- </MenuTrigger>
32
+ <MenuTrigger>Auth</MenuTrigger>
27
33
  <MenuContent>
28
34
  <MenuItem>My Profile</MenuItem>
29
35
  <MenuItem>Account Settings</MenuItem>
@@ -7,7 +7,7 @@ declare const SelectOption: import("svelte").Component<SelectOptionProps<{
7
7
  role: string;
8
8
  tabindex: number;
9
9
  'aria-selected': boolean;
10
- 'data-value': import("../../internals/index.js").JsonValue;
10
+ 'data-value': string;
11
11
  'data-label': string;
12
12
  }> & Record<string, any>, {}, "label" | "disabled" | "ref" | "value">;
13
13
  type SelectOption = ReturnType<typeof SelectOption>;
@@ -2,19 +2,15 @@ import { SvelteMap } from 'svelte/reactivity';
2
2
  import { Floating } from '../../internals/index.js';
3
3
  import type { CalcIndexAction, GetInternalProps, JsonValue } from '../../internals/index.js';
4
4
  import type { SelectArrowProps, SelectArrowState, SelectContentProps, SelectContentState, SelectOptionProps, SelectOptionState, SelectProps, SelectState, SelectTriggerProps, SelectTriggerState, SelectValueProps, SelectValueState } from '../../types/index.js';
5
- interface InternalSelectOption {
6
- value: JsonValue;
7
- label: string;
8
- }
9
5
  type RootProps = GetInternalProps<SelectProps>;
10
6
  declare class SelectRoot extends Floating {
11
7
  $$: RootProps;
12
8
  hoveredIndex: number;
13
- options: InternalSelectOption[];
14
- selectedOptions: InternalSelectOption[];
9
+ options: HTMLElement[];
10
+ selectedOptions: HTMLElement[];
15
11
  sharedIds: SvelteMap<"content" | "trigger", string>;
16
12
  mounted: boolean;
17
- HoveredOption: InternalSelectOption | undefined;
13
+ HoveredOption: HTMLElement | undefined;
18
14
  constructor(props: RootProps);
19
15
  /**
20
16
  * Toggle the visible state of the content
@@ -34,9 +30,9 @@ declare class SelectRoot extends Floating {
34
30
  */
35
31
  doneMounting: () => void;
36
32
  /**
37
- * Get the element node of the current hovered option.
33
+ * Get all non-disabled options from the content.
38
34
  */
39
- getHoveredElement: () => HTMLElement | undefined;
35
+ getAvilableOptions: () => Promise<void>;
40
36
  /**
41
37
  * Get any element by its value inside the contents element.
42
38
  * @param value The value to search for.
@@ -45,13 +41,13 @@ declare class SelectRoot extends Floating {
45
41
  /**
46
42
  * Get either the hovered or first selected option id.
47
43
  */
48
- getHoveredOrFirstSelectedId: () => Promise<string | undefined>;
44
+ getHoveredOrFirstSelectedId: () => Promise<string | null>;
49
45
  /**
50
46
  * Append the option to the parent array, used for keeping track of options.
51
47
  * @param value The value of the option. This must be unique.
52
48
  * @param label The label of the option. If no label is found, the text content of the node is used.
53
49
  */
54
- registerOption: (value: JsonValue, label: string) => void;
50
+ registerOption: (option: HTMLElement) => void;
55
51
  /**
56
52
  * Move the focus to another element based on the action used.
57
53
  * @param action The direction in which to travel.
@@ -61,7 +57,7 @@ declare class SelectRoot extends Floating {
61
57
  * Sets the hovered option.
62
58
  * @param value The unique value of the option.
63
59
  */
64
- setHovered: (value: JsonValue) => void;
60
+ setHovered: (value: string) => void;
65
61
  /**
66
62
  * Sets the selected option.
67
63
  *
@@ -131,6 +127,7 @@ declare class SelectOption {
131
127
  _root: SelectRoot;
132
128
  Hovered: boolean;
133
129
  Selected: boolean;
130
+ Label: string;
134
131
  constructor(root: SelectRoot, props: OptionProps);
135
132
  props: {
136
133
  id: string;
@@ -140,7 +137,7 @@ declare class SelectOption {
140
137
  role: string;
141
138
  tabindex: number;
142
139
  'aria-selected': boolean;
143
- 'data-value': JsonValue;
140
+ 'data-value': string;
144
141
  'data-label': string;
145
142
  };
146
143
  state: SelectOptionState;
@@ -1,8 +1,8 @@
1
- import { tick } from 'svelte';
1
+ import { onMount, tick } from 'svelte';
2
2
  import { SvelteMap } from 'svelte/reactivity';
3
3
  import { outside } from '../../attachments/outside.js';
4
4
  import { portal } from '../../attachments/portal.js';
5
- import { addEvents, attach, buildContext, calculateIndex, createAttributes, floating, Floating, KEYS, visuallyHidden } from '../../internals/index.js';
5
+ import { addEvents, attach, buildContext, calculateIndex, createAttributes, floating, Floating, KEYS, removeDisabledElements, visuallyHidden } from '../../internals/index.js';
6
6
  const { attrs, selectors } = createAttributes('select', ['root', 'trigger', 'content', 'arrow', 'option', 'value']);
7
7
  class SelectRoot extends Floating {
8
8
  $$;
@@ -49,16 +49,12 @@ class SelectRoot extends Floating {
49
49
  this.$$.visible.val = false;
50
50
  };
51
51
  /**
52
- * Get the element node of the current hovered option.
52
+ * Get all non-disabled options from the content.
53
53
  */
54
- getHoveredElement = () => {
55
- const hovered = this.options[this.hoveredIndex];
56
- if (hovered) {
57
- const option = document.querySelector(`#${this.$$.id.val} ${selectors.option}[data-value=${hovered.value}]`);
58
- if (option)
59
- return option;
60
- }
61
- return undefined;
54
+ getAvilableOptions = async () => {
55
+ const contentElement = document.querySelector(`#${this.sharedIds.get('content')}`);
56
+ if (contentElement)
57
+ this.options = removeDisabledElements(contentElement.querySelectorAll(selectors.option));
62
58
  };
63
59
  /**
64
60
  * Get any element by its value inside the contents element.
@@ -73,22 +69,17 @@ class SelectRoot extends Floating {
73
69
  getHoveredOrFirstSelectedId = async () => {
74
70
  await tick();
75
71
  const selectedOption = this.HoveredOption || this.selectedOptions[0];
76
- if (selectedOption) {
77
- const option = this.getElementByValue(selectedOption.value);
78
- if (option)
79
- return option.id;
80
- }
81
- return undefined;
72
+ return selectedOption.id || null;
82
73
  };
83
74
  /**
84
75
  * Append the option to the parent array, used for keeping track of options.
85
76
  * @param value The value of the option. This must be unique.
86
77
  * @param label The label of the option. If no label is found, the text content of the node is used.
87
78
  */
88
- registerOption = (value, label) => {
89
- const find = this.options.find((option) => option.value === value);
79
+ registerOption = (option) => {
80
+ const find = this.options.find((option) => option.dataset.value === option.dataset.value);
90
81
  if (!find)
91
- this.options.push({ value, label });
82
+ this.options.push(option);
92
83
  };
93
84
  /**
94
85
  * Move the focus to another element based on the action used.
@@ -96,16 +87,15 @@ class SelectRoot extends Floating {
96
87
  */
97
88
  navigate = (action) => {
98
89
  this.hoveredIndex = calculateIndex(action, this.options, this.hoveredIndex);
99
- const element = this.getHoveredElement();
100
- if (element)
101
- element.scrollIntoView({ block: 'nearest' });
90
+ if (this.HoveredOption)
91
+ this.HoveredOption.scrollIntoView({ block: 'nearest' });
102
92
  };
103
93
  /**
104
94
  * Sets the hovered option.
105
95
  * @param value The unique value of the option.
106
96
  */
107
97
  setHovered = (value) => {
108
- this.hoveredIndex = this.options.findIndex((el) => el.value === value);
98
+ this.hoveredIndex = this.options.findIndex((el) => el.dataset.value === value);
109
99
  };
110
100
  /**
111
101
  * Sets the selected option.
@@ -116,8 +106,8 @@ class SelectRoot extends Floating {
116
106
  if (!this.HoveredOption)
117
107
  return;
118
108
  if (this.$$.multiple.val) {
119
- if (this.selectedOptions.find((el) => el.value === this.HoveredOption?.value)) {
120
- this.selectedOptions = this.selectedOptions.filter((el) => el.value !== this.HoveredOption?.value);
109
+ if (this.selectedOptions.find((el) => el.dataset.value === this.HoveredOption?.dataset.value)) {
110
+ this.selectedOptions = this.selectedOptions.filter((el) => el.dataset.value !== this.HoveredOption?.dataset.value);
121
111
  }
122
112
  else {
123
113
  this.selectedOptions.push(this.HoveredOption);
@@ -129,8 +119,8 @@ class SelectRoot extends Floating {
129
119
  if (!this.$$.multiple.val)
130
120
  this.$$.visible.val = false;
131
121
  this.$$.value.val = this.$$.multiple.val
132
- ? this.selectedOptions.map((el) => el.value)
133
- : this.selectedOptions[0].value;
122
+ ? this.selectedOptions.map((el) => el.dataset.value)
123
+ : this.selectedOptions[0].dataset.value;
134
124
  };
135
125
  /**
136
126
  * Sets the trigger label to the selected value, only if it's found in the options array.
@@ -139,9 +129,9 @@ class SelectRoot extends Floating {
139
129
  await tick();
140
130
  const value = this.$$.value.val;
141
131
  this.selectedOptions = this.options.filter((el) => {
142
- if (!Array.isArray(value) && el.value === value)
132
+ if (!Array.isArray(value) && el.dataset.value === value)
143
133
  return el;
144
- else if (Array.isArray(value) && value.includes(el.value))
134
+ else if (Array.isArray(value) && value.includes(el.dataset.value))
145
135
  return el;
146
136
  });
147
137
  this.doneMounting();
@@ -203,7 +193,7 @@ class SelectTrigger {
203
193
  if (key === KEYS.enter) {
204
194
  e.preventDefault();
205
195
  if (this._root.HoveredOption && this._root.$$.visible.val) {
206
- this._root.getElementByValue(this._root.HoveredOption.value)?.click();
196
+ this._root.HoveredOption?.click();
207
197
  if (!this._root.$$.multiple.val)
208
198
  this._root.close();
209
199
  }
@@ -228,6 +218,17 @@ class SelectContent {
228
218
  this._root = root;
229
219
  this.$$ = props;
230
220
  this._root.sharedIds.set('content', this.$$.id.val);
221
+ $effect(() => {
222
+ // Get element references
223
+ if (this._root.$$.visible.val)
224
+ this._root.getAvilableOptions();
225
+ });
226
+ $effect(() => {
227
+ // Set first selected value as highlighted option
228
+ if (this._root.$$.visible.val && this._root.selectedOptions.length && !this._root.HoveredOption) {
229
+ this._root.hoveredIndex = this._root.options.findIndex((el) => el.dataset.selected === 'true');
230
+ }
231
+ });
231
232
  }
232
233
  props = $derived.by(() => ({
233
234
  id: this.$$.id.val,
@@ -273,17 +274,12 @@ class SelectArrow {
273
274
  class SelectOption {
274
275
  $$;
275
276
  _root;
276
- Hovered = $derived.by(() => this._root.HoveredOption?.value === this.$$.value.val);
277
- Selected = $derived.by(() => !!this._root.selectedOptions.find((el) => el.value === this.$$.value.val));
277
+ Hovered = $derived.by(() => this._root.HoveredOption?.dataset.value === this.$$.value.val);
278
+ Selected = $derived.by(() => !!this._root.selectedOptions.find((el) => el.dataset.value === this.$$.value.val));
279
+ Label = $derived.by(() => this.$$.label.val);
278
280
  constructor(root, props) {
279
281
  this._root = root;
280
282
  this.$$ = props;
281
- $effect(() => {
282
- if (this.$$.disabled.val)
283
- return;
284
- const label = this.$$.label.val || this.$$.ref.val.textContent.trim();
285
- this._root.registerOption(this.$$.value.val, label);
286
- });
287
283
  }
288
284
  props = $derived.by(() => ({
289
285
  id: this.$$.id.val,
@@ -294,11 +290,14 @@ class SelectOption {
294
290
  tabindex: 0,
295
291
  'aria-selected': this.Selected,
296
292
  'data-value': this.$$.value.val,
297
- 'data-label': this.$$.label.val,
293
+ 'data-label': this.Label,
298
294
  ...attach((node) => {
295
+ if (!this.$$.label.val && this.$$.ref.val) {
296
+ this.Label = this.$$.ref.val.textContent.trim();
297
+ }
299
298
  // Set the hovered index to the active item, if that item is "selected".
300
299
  if (this._root.$$.value.val === this.$$.value.val) {
301
- this._root.hoveredIndex = this._root.options.findIndex((el) => el.value === this.$$.value.val);
300
+ this._root.hoveredIndex = this._root.options.findIndex((el) => el.dataset.value === this.$$.value.val);
302
301
  }
303
302
  return addEvents(node, {
304
303
  click: () => {
@@ -327,7 +326,9 @@ class SelectValue {
327
326
  this._root = root;
328
327
  this.$$ = props;
329
328
  }
330
- label = $derived.by(() => this.PlaceholderVisible ? this.$$.placeholder.val : this._root.selectedOptions.map((el) => el.label).join(','));
329
+ label = $derived.by(() => this.PlaceholderVisible
330
+ ? this.$$.placeholder.val
331
+ : this._root.selectedOptions.map((el) => el.dataset.label).join(','));
331
332
  props = $derived.by(() => ({
332
333
  id: this.$$.id.val,
333
334
  [attrs.value]: '',
@@ -1,6 +1,5 @@
1
1
  import { on } from 'svelte/events';
2
2
  import { isBrowser, isObject } from './is.js';
3
- import { log } from './log.js';
4
3
  /**
5
4
  * Calculates the index position of the action given.
6
5
  * @param action Which direction to navigate the index.
@@ -1,5 +1,5 @@
1
- import type { FloatingContent, JsonValue, Props, PropsNoChildren, PropsNoRender } from '../../internals/index.js';
2
- export interface SelectProps<V extends JsonValue = any> extends PropsNoRender<SelectState>, FloatingContent {
1
+ import type { FloatingContent, Props, PropsNoChildren, PropsNoRender } from '../../internals/index.js';
2
+ export interface SelectProps<V extends string | string[] = any> extends PropsNoRender<SelectState>, FloatingContent {
3
3
  /**
4
4
  * The currently selected option(s).
5
5
  *
@@ -50,7 +50,7 @@ export interface SelectOptionProps<P = any> extends Props<HTMLButtonElement, P,
50
50
  /**
51
51
  * The value of the option.
52
52
  */
53
- value: JsonValue;
53
+ value: string;
54
54
  /**
55
55
  * Disables the option, skipping mouse/keyboard navigation and stopping events from firing.
56
56
  *
package/package.json CHANGED
@@ -1,75 +1,75 @@
1
- {
2
- "name": "lithesome",
3
- "version": "0.23.5",
4
- "scripts": {
5
- "dev": "vite dev",
6
- "build": "bun --bun vite build && npm run prepack",
7
- "preview": "bun --bun vite preview",
8
- "prepare": "svelte-kit sync || echo ''",
9
- "prepack": "svelte-kit sync && svelte-package && publint",
10
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
11
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
12
- "format": "prettier --write .",
13
- "lint": "prettier --check ."
14
- },
15
- "files": [
16
- "dist",
17
- "!dist/**/*.test.*",
18
- "!dist/**/*.spec.*"
19
- ],
20
- "sideEffects": [
21
- "**/*.css"
22
- ],
23
- "svelte": "./dist/index.js",
24
- "types": "./dist/index.d.ts",
25
- "type": "module",
26
- "exports": {
27
- ".": {
28
- "types": "./dist/index.d.ts",
29
- "svelte": "./dist/index.js"
30
- },
31
- "./types": {
32
- "types": "./dist/types/index.d.ts"
33
- }
34
- },
35
- "peerDependencies": {
36
- "svelte": "^5.0.0"
37
- },
38
- "devDependencies": {
39
- "@lucide/svelte": "^0.541.0",
40
- "@prgm/sveltekit-progress-bar": "^3.0.2",
41
- "@sveltejs/adapter-vercel": "^5.6.3",
42
- "@sveltejs/kit": "^2.22.0",
43
- "@sveltejs/package": "^2.0.0",
44
- "@sveltejs/vite-plugin-svelte": "^6.0.0",
45
- "@tailwindcss/typography": "^0.5.16",
46
- "@tailwindcss/vite": "^4.0.0",
47
- "@types/node": "^24.3.0",
48
- "clsx": "^2.1.1",
49
- "gray-matter": "^4.0.3",
50
- "mdsvex": "^0.12.6",
51
- "mode-watcher": "^1.1.0",
52
- "prettier": "^3.4.2",
53
- "prettier-plugin-imports": "^4.3.3",
54
- "prettier-plugin-svelte": "^3.3.3",
55
- "prettier-plugin-tailwindcss": "^0.6.11",
56
- "publint": "^0.3.2",
57
- "rehype-external-links": "^3.0.0",
58
- "rehype-slug": "^6.0.0",
59
- "shiki": "^3.11.0",
60
- "svelte": "^5.0.0",
61
- "svelte-check": "^4.0.0",
62
- "tailwind-merge": "^3.3.1",
63
- "tailwindcss": "^4.0.0",
64
- "typescript": "^5.0.0",
65
- "unist-util-visit": "^5.0.0",
66
- "vite": "^7.0.4"
67
- },
68
- "keywords": [
69
- "svelte"
70
- ],
71
- "dependencies": {
72
- "@floating-ui/dom": "^1.7.4",
73
- "focus-trap": "^7.6.5"
74
- }
75
- }
1
+ {
2
+ "name": "lithesome",
3
+ "version": "0.23.7",
4
+ "scripts": {
5
+ "dev": "vite dev",
6
+ "build": "bun --bun vite build && npm run prepack",
7
+ "preview": "bun --bun vite preview",
8
+ "prepare": "svelte-kit sync || echo ''",
9
+ "prepack": "svelte-kit sync && svelte-package && publint",
10
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
11
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
12
+ "format": "prettier --write .",
13
+ "lint": "prettier --check ."
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "!dist/**/*.test.*",
18
+ "!dist/**/*.spec.*"
19
+ ],
20
+ "sideEffects": [
21
+ "**/*.css"
22
+ ],
23
+ "svelte": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "svelte": "./dist/index.js"
30
+ },
31
+ "./types": {
32
+ "types": "./dist/types/index.d.ts"
33
+ }
34
+ },
35
+ "peerDependencies": {
36
+ "svelte": "^5.53.12"
37
+ },
38
+ "devDependencies": {
39
+ "@lucide/svelte": "^0.577.0",
40
+ "@prgm/sveltekit-progress-bar": "^3.0.2",
41
+ "@sveltejs/adapter-vercel": "^6.3.3",
42
+ "@sveltejs/kit": "^2.55.0",
43
+ "@sveltejs/package": "^2.5.7",
44
+ "@sveltejs/vite-plugin-svelte": "^7.0.0",
45
+ "@tailwindcss/typography": "^0.5.19",
46
+ "@tailwindcss/vite": "^4.2.1",
47
+ "@types/node": "^25.5.0",
48
+ "clsx": "^2.1.1",
49
+ "gray-matter": "^4.0.3",
50
+ "mdsvex": "^0.12.7",
51
+ "mode-watcher": "^1.1.0",
52
+ "prettier": "^3.8.1",
53
+ "prettier-plugin-imports": "^4.3.3",
54
+ "prettier-plugin-svelte": "^3.5.1",
55
+ "prettier-plugin-tailwindcss": "^0.7.2",
56
+ "publint": "^0.3.18",
57
+ "rehype-external-links": "^3.0.0",
58
+ "rehype-slug": "^6.0.0",
59
+ "shiki": "^4.0.2",
60
+ "svelte": "^5.53.12",
61
+ "svelte-check": "^4.4.5",
62
+ "tailwind-merge": "^3.5.0",
63
+ "tailwindcss": "^4.2.1",
64
+ "typescript": "^5.9.3",
65
+ "unist-util-visit": "^5.1.0",
66
+ "vite": "^8.0.0"
67
+ },
68
+ "keywords": [
69
+ "svelte"
70
+ ],
71
+ "dependencies": {
72
+ "@floating-ui/dom": "^1.7.6",
73
+ "focus-trap": "^8.0.0"
74
+ }
75
+ }