thekselect 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Thekster
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,338 @@
1
+ # ThekSelect
2
+
3
+ A lightweight, framework-agnostic, and accessible select library with native drag-and-drop tag reordering.
4
+
5
+ ## Features
6
+
7
+ - Headless core with renderer/state separation
8
+ - Search with debounce support
9
+ - Single and multi-select modes
10
+ - User-created tags (`canCreate`)
11
+ - Remote async options (`loadOptions`)
12
+ - Native HTML5 drag-and-drop for selected tags
13
+ - Keyboard-friendly and ARIA-aware behavior
14
+ - Height control via `height` option (`number` or CSS string)
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install thekselect
20
+ ```
21
+
22
+ ## Release
23
+
24
+ Before publishing:
25
+
26
+ 1. Ensure the package name and version in `package.json` are correct.
27
+ 2. Add an `NPM_TOKEN` repository secret in GitHub.
28
+ 3. Push the commit you want to publish.
29
+
30
+ Verify the package locally:
31
+
32
+ ```bash
33
+ npm run release:check
34
+ ```
35
+
36
+ Publish through GitHub Actions:
37
+
38
+ 1. Bump `package.json` to the version you want to publish.
39
+ 2. Push the version commit to GitHub.
40
+ 3. Trigger one of these:
41
+ - Create and publish a GitHub Release.
42
+ - Open `Actions` -> `Publish to npm` -> `Run workflow`.
43
+
44
+ Manual fallback from a local terminal:
45
+
46
+ ```bash
47
+ npm run release:check
48
+ npm publish --access public
49
+ ```
50
+
51
+ `prepublishOnly` runs the same release checks before `npm publish`.
52
+
53
+ ## CSS Themes
54
+
55
+ Import one of the distributed theme files:
56
+
57
+ ```js
58
+ import 'thekselect/css/base.css';
59
+ // or
60
+ import 'thekselect/css/dark.css';
61
+ // or
62
+ import 'thekselect/css/forest.css';
63
+ // or
64
+ import 'thekselect/css/red.css';
65
+ // or
66
+ import 'thekselect/css/blue.css';
67
+ // or
68
+ import 'thekselect/css/gray.css';
69
+ // or
70
+ import 'thekselect/css/bootstrap.css';
71
+ // or
72
+ import 'thekselect/css/tailwind.css';
73
+ // or
74
+ import 'thekselect/css/material.css';
75
+ ```
76
+
77
+ `base.css` is the system theme: light defaults with automatic dark-mode tokens via `prefers-color-scheme`.
78
+
79
+ Customize tokens with plain CSS:
80
+
81
+ ```css
82
+ :root {
83
+ --thek-primary: #1d4ed8;
84
+ --thek-border-radius: 12px;
85
+ --thek-height-md: 44px;
86
+ }
87
+ ```
88
+
89
+ ## Quick Start
90
+
91
+ ```html
92
+ <select id="my-select">
93
+ <option value="">Choose one...</option>
94
+ <option value="react">React</option>
95
+ <option value="vue">Vue</option>
96
+ </select>
97
+ ```
98
+
99
+ ```js
100
+ import { ThekSelect } from 'thekselect';
101
+
102
+ const select = ThekSelect.init('#my-select', {
103
+ placeholder: 'Select an option...'
104
+ });
105
+ ```
106
+
107
+ ### Browser `<script>` Tag (No Bundler)
108
+
109
+ ```html
110
+ <link rel="stylesheet" href="./dist/css/base.css" />
111
+ <script src="./dist/thekselect.umd.min.cjs"></script>
112
+
113
+ <select id="my-select">
114
+ <option value="react">React</option>
115
+ <option value="vue">Vue</option>
116
+ </select>
117
+
118
+ <script>
119
+ const { ThekSelect } = window.ThekSelect;
120
+ ThekSelect.init('#my-select');
121
+ </script>
122
+ ```
123
+
124
+ If your static server does not serve `.cjs` files as JavaScript, rename the UMD output to `.js` for browser delivery.
125
+
126
+ ### Initialize From Existing `<select>`
127
+
128
+ If options already exist in your native `<select>`, initialize directly from it:
129
+
130
+ ```html
131
+ <select id="country-select" multiple>
132
+ <option value="us" selected>United States</option>
133
+ <option value="de">Germany</option>
134
+ <option value="jp" disabled>Japan (disabled)</option>
135
+ </select>
136
+ ```
137
+
138
+ ```js
139
+ import { ThekSelect } from 'thekselect';
140
+
141
+ ThekSelect.init('#country-select');
142
+ ```
143
+
144
+ `ThekSelect` reads existing `<option>` values, labels, `selected`, `disabled`, and `multiple` automatically.
145
+
146
+ ### Large Datasets (Virtualization)
147
+
148
+ ```html
149
+ <div id="big-list"></div>
150
+ ```
151
+
152
+ ```js
153
+ ThekSelect.init('#big-list', {
154
+ options: hugeOptions,
155
+ searchable: true,
156
+ virtualize: true,
157
+ virtualThreshold: 80,
158
+ virtualItemHeight: 40,
159
+ virtualOverscan: 4
160
+ });
161
+ ```
162
+
163
+ ### Global Defaults (App-wide)
164
+
165
+ ```html
166
+ <div id="first"></div>
167
+ <div id="second"></div>
168
+ ```
169
+
170
+ ```js
171
+ import { ThekSelect } from 'thekselect';
172
+
173
+ ThekSelect.setDefaults({
174
+ height: 40,
175
+ virtualize: true
176
+ });
177
+
178
+ // Uses global defaults
179
+ ThekSelect.init('#first');
180
+
181
+ // Instance options still override globals
182
+ ThekSelect.init('#second', { height: 52 });
183
+
184
+ // Optional cleanup (tests, app teardown, etc.)
185
+ ThekSelect.resetDefaults();
186
+ ```
187
+
188
+ ## Development (Repo)
189
+
190
+ ```bash
191
+ npm install
192
+ npm test
193
+ ```
194
+
195
+ ### Scripts
196
+
197
+ | Script | Purpose |
198
+ | ------------------ | ----------------------------------------------------------------- |
199
+ | `npm run dev` | Start Vite dev server for local development/showcase. |
200
+ | `npm run build` | Build library outputs into `dist/` and emit types. |
201
+ | `npm run preview` | Preview built output with Vite. Optional for library development. |
202
+ | `npm test` | Run test suite in watch mode. |
203
+ | `npm run coverage` | Run tests with coverage report. |
204
+
205
+ Notes:
206
+
207
+ - `preview` is optional for day-to-day library work.
208
+ - For this repo, tests are the primary correctness check when bundle preview is unavailable.
209
+
210
+ ## Showcase
211
+
212
+ The demo page is `showcase/index.html` and is intended to run through Vite during development.
213
+
214
+ ```bash
215
+ npm run dev
216
+ ```
217
+
218
+ Then open the local URL printed by Vite.
219
+
220
+ ## Configuration
221
+
222
+ | Option | Type | Default | Description |
223
+ | ------------------- | ---------------------------------------- | ------------------------ | ------------------------------------------------------ |
224
+ | `options` | `ThekSelectOption[]` | `[]` | Initial options list. |
225
+ | `multiple` | `boolean` | `false` | Enable multi-select mode. |
226
+ | `searchable` | `boolean` | `true` | Enable search input and filtering. |
227
+ | `disabled` | `boolean` | `false` | Disable interaction. |
228
+ | `placeholder` | `string` | `'Select...'` | Placeholder text. |
229
+ | `canCreate` | `boolean` | `false` | Allow creating new options from input. |
230
+ | `createText` | `string` | `"Create '{%t}'..."` | Create row label template (`{%t}` = typed text). |
231
+ | `height` | `number \| string` | `40` | Control control/dropdown input height (`number` = px). |
232
+ | `debounce` | `number` | `300` | Debounce delay for `loadOptions`. |
233
+ | `maxSelectedLabels` | `number` | `3` | Max visible tags before summary text. |
234
+ | `displayField` | `string` | `'label'` | Field used for rendering text. |
235
+ | `valueField` | `string` | `'value'` | Field used as internal value key. |
236
+ | `maxOptions` | `number \| null` | `null` | Limit rendered dropdown items. |
237
+ | `virtualize` | `boolean` | `false` | Enable virtualized option rendering for large lists. |
238
+ | `virtualItemHeight` | `number` | `40` | Row height (px) used by virtualization calculations. |
239
+ | `virtualOverscan` | `number` | `4` | Extra rows rendered above and below viewport. |
240
+ | `virtualThreshold` | `number` | `80` | Minimum option count before virtualization activates. |
241
+ | `loadOptions` | `(query) => Promise<ThekSelectOption[]>` | `undefined` | Async loader for remote search. |
242
+ | `renderOption` | `(option) => string \| HTMLElement` | `(o) => o[displayField]` | Custom dropdown renderer. |
243
+ | `renderSelection` | `(option) => string \| HTMLElement` | `(o) => o[displayField]` | Custom selected-item renderer. |
244
+
245
+ ## Option Shape
246
+
247
+ ```ts
248
+ interface ThekSelectOption {
249
+ value: string;
250
+ label: string;
251
+ disabled?: boolean;
252
+ selected?: boolean;
253
+ data?: any;
254
+ [key: string]: any;
255
+ }
256
+ ```
257
+
258
+ | Field | Type | Description |
259
+ | --------------- | --------- | ------------------------------------------------------------------ |
260
+ | `value` | `string` | Stable internal value used for selection state and emitted values. |
261
+ | `label` | `string` | Default display text shown in dropdown and selected state. |
262
+ | `disabled` | `boolean` | Prevents selecting the option when `true`. |
263
+ | `selected` | `boolean` | Marks initial selection when options are provided at init time. |
264
+ | `data` | `any` | Optional extra metadata for custom rendering and app logic. |
265
+ | `[key: string]` | `any` | Allows custom fields for `displayField`/`valueField` mapping. |
266
+
267
+ When using custom fields (`valueField`, `displayField`), keep `value` and `label` available if you share the same option objects across default and custom-field instances.
268
+
269
+ ## API Methods
270
+
271
+ - `getValue()`: Return selected value (`string`), values (`string[]`), or `undefined` when single-select has no selection.
272
+ - `getSelectedOptions()`: Return selected option object(s), or `undefined` when single-select has no selection.
273
+ - `setValue(value, silent = false)`: Set current selection programmatically.
274
+ - `setHeight(height)`: Update height at runtime (`number` = px or CSS string like `'2.75rem'`).
275
+ - `setMaxOptions(limit)`: Update dropdown item limit.
276
+ - `setRenderOption(callback)`: Update option rendering function.
277
+ - `ThekSelect.setDefaults(defaults)`: Set global defaults for future instances.
278
+ - `ThekSelect.resetDefaults()`: Clear global defaults.
279
+ - `on(event, callback)`: Subscribe to component events. Returns an unsubscribe function.
280
+ - `destroy()`: Tear down and restore original element.
281
+
282
+ ## API Reference
283
+
284
+ | Method | Parameters | Returns | Description |
285
+ | --------------------------- | ------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------- |
286
+ | `getValue()` | none | `string \| string[] \| undefined` | Gets current selected value(s) from component state. |
287
+ | `getSelectedOptions()` | none | `ThekSelectOption \| ThekSelectOption[] \| undefined` | Gets current selected option object(s). |
288
+ | `setValue(value, silent?)` | `value: string \| string[]`, `silent?: boolean` | `void` | Sets selected value(s). In single mode keeps first value; in multi mode de-duplicates values. |
289
+ | `setHeight(height)` | `height: number \| string` | `void` | Sets control height for the instance. Numeric input is treated as px. |
290
+ | `setMaxOptions(limit)` | `limit: number \| null` | `void` | Limits filtered options rendered in dropdown (`null` disables limit). |
291
+ | `setRenderOption(callback)` | `callback: (option) => string \| HTMLElement` | `void` | Overrides dropdown option rendering for this instance. |
292
+ | `on(event, callback)` | `event: ThekSelectEvent`, `callback: (payload) => void` | `() => void` | Subscribes to an event and returns an unsubscribe function. |
293
+ | `destroy()` | none | `void` | Removes generated DOM/listeners and restores original element visibility. |
294
+
295
+ | Static Method | Parameters | Returns | Description |
296
+ | ----------------------------------- | ------------------------------------------------------------- | ------------ | ------------------------------------------------ |
297
+ | `ThekSelect.init(element, config?)` | `element: string \| HTMLElement`, `config?: ThekSelectConfig` | `ThekSelect` | Creates and initializes a new instance. |
298
+ | `ThekSelect.setDefaults(defaults)` | `defaults: Partial<ThekSelectConfig>` | `void` | Sets process-wide defaults for future instances. |
299
+ | `ThekSelect.resetDefaults()` | none | `void` | Clears global defaults. |
300
+
301
+ ## Events
302
+
303
+ - `change`: Selection changed (value or values).
304
+ - `open`: Dropdown opened.
305
+ - `close`: Dropdown closed.
306
+ - `search`: Search input changed.
307
+ - `tagAdded`: Tag added in multi mode.
308
+ - `tagRemoved`: Tag removed in multi mode.
309
+ - `reordered`: Selected tags reordered by drag-and-drop.
310
+
311
+ ## Troubleshooting
312
+
313
+ ### `Error: spawn EPERM` on `npm run dev`, `npm run build`, or `npm run preview` (Windows)
314
+
315
+ This is typically an environment policy/permissions issue around spawning build tooling (e.g. esbuild), not a ThekSelect API issue.
316
+
317
+ Recommended checks:
318
+
319
+ 1. Reinstall dependencies:
320
+
321
+ ```bash
322
+ rmdir /s /q node_modules
323
+ npm install
324
+ ```
325
+
326
+ 2. Verify esbuild binary can execute:
327
+
328
+ ```bash
329
+ node_modules\\@esbuild\\win32-x64\\esbuild.exe --version
330
+ ```
331
+
332
+ 3. If blocked, review antivirus/EDR/AppLocker/Windows Security rules for child-process execution from your workspace.
333
+
334
+ 4. Use `npm test` to continue validating behavior while fixing build environment constraints.
335
+
336
+ ## License
337
+
338
+ MIT
@@ -0,0 +1,5 @@
1
+ import { ThekSelectConfig, ThekSelectOption, ThekSelectState } from './types.js';
2
+ export declare const NOOP_LOAD_OPTIONS: (_query: string) => Promise<ThekSelectOption[]>;
3
+ export declare function parseSelectOptions(select: HTMLSelectElement): ThekSelectOption[];
4
+ export declare function buildConfig<T = unknown>(element: HTMLElement, config: ThekSelectConfig<T>, globalDefaults?: Partial<ThekSelectConfig<T>>): Required<ThekSelectConfig<T>>;
5
+ export declare function buildInitialState<T = unknown>(config: Required<ThekSelectConfig<T>>): ThekSelectState<T>;
@@ -0,0 +1,38 @@
1
+ import { ThekSelectConfig, ThekSelectState, ThekSelectOption } from './types.js';
2
+ export interface RendererCallbacks {
3
+ onSelect: (option: ThekSelectOption) => void;
4
+ onCreate: (label: string) => void;
5
+ onRemove: (option: ThekSelectOption) => void;
6
+ onReorder: (from: number, to: number) => void;
7
+ }
8
+ export declare class DomRenderer {
9
+ private config;
10
+ private id;
11
+ private callbacks;
12
+ wrapper: HTMLElement;
13
+ control: HTMLElement;
14
+ selectionContainer: HTMLElement;
15
+ indicatorsContainer: HTMLElement;
16
+ placeholderElement: HTMLElement;
17
+ input: HTMLInputElement;
18
+ dropdown: HTMLElement;
19
+ optionsList: HTMLElement;
20
+ private lastState;
21
+ private lastFilteredOptions;
22
+ constructor(config: Required<ThekSelectConfig>, id: string, callbacks: RendererCallbacks);
23
+ private normalizeHeight;
24
+ private applyHeight;
25
+ createDom(): void;
26
+ render(state: ThekSelectState, filteredOptions: ThekSelectOption[]): void;
27
+ private renderSelectionContent;
28
+ private renderOptionsContent;
29
+ private createSpacer;
30
+ private createOptionItem;
31
+ private handleOptionsScroll;
32
+ private handleOptionsWheel;
33
+ positionDropdown(): void;
34
+ private setupTagDnd;
35
+ setHeight(height: number | string): void;
36
+ updateConfig(newConfig: Partial<Required<ThekSelectConfig>>): void;
37
+ destroy(): void;
38
+ }
@@ -0,0 +1,7 @@
1
+ import { ThekSelectEvent } from './types.js';
2
+ export declare class ThekSelectEventEmitter {
3
+ private listeners;
4
+ on(event: ThekSelectEvent, callback: Function): () => void;
5
+ off(event: ThekSelectEvent, callback: Function): void;
6
+ emit(event: ThekSelectEvent, data: unknown): void;
7
+ }
@@ -0,0 +1,4 @@
1
+ import { ThekSelectConfig, ThekSelectOption, ThekSelectState } from './types.js';
2
+ export declare function isRemoteMode<T = unknown>(config: Required<ThekSelectConfig<T>>): boolean;
3
+ export declare function getFilteredOptions<T = unknown>(config: Required<ThekSelectConfig<T>>, state: ThekSelectState<T>): ThekSelectOption<T>[];
4
+ export declare function mergeSelectedOptionsByValue<T = unknown>(valueField: string, selectedValues: string[], previous: Record<string, ThekSelectOption<T>>, latestOptions: ThekSelectOption<T>[]): Record<string, ThekSelectOption<T>>;
@@ -0,0 +1,20 @@
1
+ import { ThekSelectConfig, ThekSelectOption, ThekSelectState } from './types.js';
2
+ type TagEvent = 'tagAdded' | 'tagRemoved';
3
+ export interface SelectionUpdate<T = unknown> {
4
+ selectedValues: string[];
5
+ selectedOptionsByValue: Record<string, ThekSelectOption<T>>;
6
+ inputValue?: string;
7
+ tagEvent?: TagEvent;
8
+ tagOption?: ThekSelectOption<T>;
9
+ }
10
+ export declare function applySelection<T = unknown>(config: Required<ThekSelectConfig<T>>, state: ThekSelectState<T>, option: ThekSelectOption<T>): SelectionUpdate<T>;
11
+ export declare function createOptionFromLabel<T = unknown>(config: Required<ThekSelectConfig<T>>, label: string): ThekSelectOption<T>;
12
+ export declare function removeLastSelection<T = unknown>(config: Required<ThekSelectConfig<T>>, state: ThekSelectState<T>): {
13
+ selectedValues: string[];
14
+ selectedOptionsByValue: Record<string, ThekSelectOption<T>>;
15
+ removedOption?: ThekSelectOption<T>;
16
+ };
17
+ export declare function reorderSelectedValues(state: ThekSelectState, from: number, to: number): string[];
18
+ export declare function resolveSelectedOptions<T = unknown>(config: Required<ThekSelectConfig<T>>, state: ThekSelectState<T>): ThekSelectOption<T>[];
19
+ export declare function buildSelectedOptionsMapFromValues<T = unknown>(config: Required<ThekSelectConfig<T>>, state: ThekSelectState<T>, values: string[]): Record<string, ThekSelectOption<T>>;
20
+ export {};
@@ -0,0 +1,10 @@
1
+ export type StateListener<T> = (state: T) => void;
2
+ export declare class StateManager<T extends object> {
3
+ private state;
4
+ private listeners;
5
+ constructor(initialState: T);
6
+ getState(): T;
7
+ setState(newState: Partial<T>): void;
8
+ subscribe(listener: StateListener<T>): () => void;
9
+ private notify;
10
+ }
@@ -0,0 +1,42 @@
1
+ import { ThekSelectConfig, ThekSelectOption, ThekSelectEvent } from './types.js';
2
+ export declare class ThekSelect<T = unknown> {
3
+ private static globalDefaults;
4
+ private config;
5
+ private stateManager;
6
+ private renderer;
7
+ private id;
8
+ private events;
9
+ private originalElement;
10
+ private unsubscribeEvents;
11
+ private unsubscribeState?;
12
+ private remoteRequestId;
13
+ private isDestroyed;
14
+ private focusTimeoutId;
15
+ private constructor();
16
+ static init<T = unknown>(element: string | HTMLElement, config?: ThekSelectConfig<T>): ThekSelect<T>;
17
+ static setDefaults(defaults: Partial<ThekSelectConfig>): void;
18
+ static resetDefaults(): void;
19
+ private initialize;
20
+ private setupListeners;
21
+ private handleSearch;
22
+ private setupHandleSearch;
23
+ private handleKeyDown;
24
+ private toggleDropdown;
25
+ private openDropdown;
26
+ private closeDropdown;
27
+ private handleSelect;
28
+ private handleCreate;
29
+ private handleRemoveLastSelection;
30
+ private handleReorder;
31
+ private syncOriginalElement;
32
+ private render;
33
+ on(event: ThekSelectEvent, callback: Function): () => void;
34
+ private emit;
35
+ getValue(): string | string[] | undefined;
36
+ getSelectedOptions(): ThekSelectOption<T> | ThekSelectOption<T>[] | undefined;
37
+ setValue(value: string | string[], silent?: boolean): void;
38
+ setHeight(height: number | string): void;
39
+ setRenderOption(callback: (option: ThekSelectOption<T>) => string | HTMLElement): void;
40
+ setMaxOptions(max: number | null): void;
41
+ destroy(): void;
42
+ }
@@ -0,0 +1,40 @@
1
+ export interface ThekSelectOption<T = unknown> {
2
+ value: string;
3
+ label: string;
4
+ disabled?: boolean;
5
+ selected?: boolean;
6
+ data?: T;
7
+ [key: string]: unknown;
8
+ }
9
+ export interface ThekSelectConfig<T = unknown> {
10
+ options?: ThekSelectOption<T>[];
11
+ multiple?: boolean;
12
+ searchable?: boolean;
13
+ disabled?: boolean;
14
+ placeholder?: string;
15
+ canCreate?: boolean;
16
+ createText?: string;
17
+ height?: number | string;
18
+ debounce?: number;
19
+ maxSelectedLabels?: number;
20
+ displayField?: string;
21
+ valueField?: string;
22
+ maxOptions?: number | null;
23
+ virtualize?: boolean;
24
+ virtualItemHeight?: number;
25
+ virtualOverscan?: number;
26
+ virtualThreshold?: number;
27
+ loadOptions?: (query: string) => Promise<ThekSelectOption<T>[]>;
28
+ renderOption?: (option: ThekSelectOption<T>) => string | HTMLElement;
29
+ renderSelection?: (option: ThekSelectOption<T>) => string | HTMLElement;
30
+ }
31
+ export interface ThekSelectState<T = unknown> {
32
+ options: ThekSelectOption<T>[];
33
+ selectedValues: string[];
34
+ selectedOptionsByValue: Record<string, ThekSelectOption<T>>;
35
+ isOpen: boolean;
36
+ focusedIndex: number;
37
+ inputValue: string;
38
+ isLoading: boolean;
39
+ }
40
+ export type ThekSelectEvent = 'change' | 'open' | 'close' | 'search' | 'tagAdded' | 'tagRemoved' | 'reordered';
@@ -0,0 +1,37 @@
1
+ :root {
2
+ --thek-primary: #0f172a;
3
+ --thek-primary-light: #f1f5f9;
4
+ --thek-bg-surface: #ffffff;
5
+ --thek-bg-panel: #f8fafc;
6
+ --thek-bg-subtle: #f1f5f9;
7
+ --thek-border: #e2e8f0;
8
+ --thek-border-strong: #cbd5e1;
9
+ --thek-text-main: #0f172a;
10
+ --thek-text-muted: #64748b;
11
+ --thek-text-inverse: #ffffff;
12
+ --thek-danger: #ef4444;
13
+ --thek-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
14
+ --thek-font-family: inherit;
15
+ --thek-border-radius: 8px;
16
+ --thek-height-sm: 32px;
17
+ --thek-height-md: 40px;
18
+ --thek-height-lg: 48px;
19
+ --thek-item-padding: 8px 10px;
20
+ }
21
+
22
+ @media (prefers-color-scheme: dark) {
23
+ :root {
24
+ --thek-primary: #38bdf8;
25
+ --thek-primary-light: rgba(56, 189, 248, 0.15);
26
+ --thek-bg-surface: #0f172a;
27
+ --thek-bg-panel: #334155;
28
+ --thek-bg-subtle: #475569;
29
+ --thek-border: #334155;
30
+ --thek-border-strong: #475569;
31
+ --thek-text-main: #f8fafc;
32
+ --thek-text-muted: #94a3b8;
33
+ --thek-text-inverse: #0f172a;
34
+ --thek-danger: #f43f5e;
35
+ --thek-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -4px rgba(0, 0, 0, 0.4);
36
+ }
37
+ }
@@ -0,0 +1,14 @@
1
+ html[data-thek-theme='blue'] {
2
+ --thek-primary: #1d4ed8;
3
+ --thek-primary-light: #dbeafe;
4
+ --thek-bg-surface: #f8fbff;
5
+ --thek-bg-panel: #eff6ff;
6
+ --thek-bg-subtle: #dbeafe;
7
+ --thek-border: #bfdbfe;
8
+ --thek-border-strong: #60a5fa;
9
+ --thek-text-main: #1e3a8a;
10
+ --thek-text-muted: #1d4ed8;
11
+ --thek-text-inverse: #ffffff;
12
+ --thek-danger: #dc2626;
13
+ --thek-shadow: 0 8px 18px -8px rgba(29, 78, 216, 0.3);
14
+ }
@@ -0,0 +1,15 @@
1
+ html[data-thek-theme='bootstrap'] {
2
+ --thek-primary: #0d6efd;
3
+ --thek-primary-light: #cfe2ff;
4
+ --thek-bg-surface: #ffffff;
5
+ --thek-bg-panel: #f8f9fa;
6
+ --thek-bg-subtle: #e9ecef;
7
+ --thek-border: #dee2e6;
8
+ --thek-border-strong: #adb5bd;
9
+ --thek-text-main: #212529;
10
+ --thek-text-muted: #6c757d;
11
+ --thek-text-inverse: #ffffff;
12
+ --thek-danger: #dc3545;
13
+ --thek-shadow: 0 10px 20px -10px rgba(33, 37, 41, 0.25);
14
+ --thek-border-radius: 6px;
15
+ }
@@ -0,0 +1,14 @@
1
+ html[data-thek-theme='dark'] {
2
+ --thek-primary: #38bdf8;
3
+ --thek-primary-light: rgba(56, 189, 248, 0.15);
4
+ --thek-bg-surface: #0f172a;
5
+ --thek-bg-panel: #334155;
6
+ --thek-bg-subtle: #475569;
7
+ --thek-border: #334155;
8
+ --thek-border-strong: #475569;
9
+ --thek-text-main: #f8fafc;
10
+ --thek-text-muted: #94a3b8;
11
+ --thek-text-inverse: #0f172a;
12
+ --thek-danger: #f43f5e;
13
+ --thek-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -4px rgba(0, 0, 0, 0.4);
14
+ }
@@ -0,0 +1,14 @@
1
+ html[data-thek-theme='forest'] {
2
+ --thek-primary: #14532d;
3
+ --thek-primary-light: #dcfce7;
4
+ --thek-bg-surface: #f7fee7;
5
+ --thek-bg-panel: #ecfccb;
6
+ --thek-bg-subtle: #d9f99d;
7
+ --thek-border: #a3e635;
8
+ --thek-border-strong: #65a30d;
9
+ --thek-text-main: #1f2937;
10
+ --thek-text-muted: #4b5563;
11
+ --thek-text-inverse: #ffffff;
12
+ --thek-danger: #dc2626;
13
+ --thek-shadow: 0 8px 18px -6px rgba(20, 83, 45, 0.2);
14
+ }
@@ -0,0 +1,14 @@
1
+ html[data-thek-theme='gray'] {
2
+ --thek-primary: #374151;
3
+ --thek-primary-light: #e5e7eb;
4
+ --thek-bg-surface: #f9fafb;
5
+ --thek-bg-panel: #f3f4f6;
6
+ --thek-bg-subtle: #e5e7eb;
7
+ --thek-border: #d1d5db;
8
+ --thek-border-strong: #9ca3af;
9
+ --thek-text-main: #111827;
10
+ --thek-text-muted: #4b5563;
11
+ --thek-text-inverse: #ffffff;
12
+ --thek-danger: #ef4444;
13
+ --thek-shadow: 0 8px 18px -8px rgba(55, 65, 81, 0.22);
14
+ }
@@ -0,0 +1,15 @@
1
+ html[data-thek-theme='material'] {
2
+ --thek-primary: #6750a4;
3
+ --thek-primary-light: #eaddff;
4
+ --thek-bg-surface: #fffbfe;
5
+ --thek-bg-panel: #f7f2fa;
6
+ --thek-bg-subtle: #ede7f6;
7
+ --thek-border: #d0c4dc;
8
+ --thek-border-strong: #b39ddb;
9
+ --thek-text-main: #1d1b20;
10
+ --thek-text-muted: #625b71;
11
+ --thek-text-inverse: #ffffff;
12
+ --thek-danger: #b3261e;
13
+ --thek-shadow: 0 10px 20px -10px rgba(103, 80, 164, 0.35);
14
+ --thek-border-radius: 10px;
15
+ }