noph-ui 0.2.9 → 0.3.2

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.
@@ -264,6 +264,7 @@
264
264
  .button-icon {
265
265
  display: inline-flex;
266
266
  align-items: center;
267
+ pointer-events: none;
267
268
  }
268
269
 
269
270
  :global(.np-button .button-icon) {
@@ -59,6 +59,7 @@
59
59
  {#snippet content()}
60
60
  {#if !disabled}
61
61
  <Ripple forElement={touchEl} />
62
+ <span class="np-touch" bind:this={touchEl}></span>
62
63
  {/if}
63
64
  {#if selectedIcon && selectedState}
64
65
  {@render selectedIcon()}
@@ -84,7 +85,6 @@
84
85
  : ''} {attributes.class}"
85
86
  >
86
87
  {@render content()}
87
- <span class="np-touch" bind:this={touchEl}></span>
88
88
  </button>
89
89
  {:else if isLink(attributes)}
90
90
  <a
@@ -1,43 +1,32 @@
1
1
  <script lang="ts">
2
2
  import Ripple from '../ripple/Ripple.svelte'
3
- import type { HTMLInputAttributes } from 'svelte/elements'
3
+ import type { CheckboxProps } from './types.ts'
4
4
 
5
5
  let {
6
6
  indeterminate = $bindable(),
7
7
  checked = $bindable(),
8
8
  ...attributes
9
- }: HTMLInputAttributes = $props()
10
- let selected = $derived(checked || indeterminate)
9
+ }: CheckboxProps = $props()
11
10
  </script>
12
11
 
13
12
  <div class="np-host">
14
- <div
15
- class:selected
16
- class="np-container"
17
- class:checked
18
- class:unselected={!selected}
19
- class:prev-checked={!checked}
20
- class:prev-unselected={selected}
21
- class:indeterminate
22
- >
13
+ <div class="np-container">
23
14
  <label class="np-input-wrapper">
24
15
  <input
25
- {...attributes}
26
16
  class="np-input"
27
17
  type="checkbox"
28
- {indeterminate}
18
+ bind:indeterminate
29
19
  bind:checked
30
20
  aria-checked={indeterminate ? 'mixed' : undefined}
31
- onclick={() => {
32
- indeterminate = false
33
- }}
21
+ {...attributes}
34
22
  />
35
- <Ripple />
23
+ {#if !attributes.disabled}
24
+ <Ripple />
25
+ {/if}
36
26
  </label>
37
27
 
38
28
  <div class="np-outline"></div>
39
29
  <div class="np-background"></div>
40
- {#if !attributes.disabled}{/if}
41
30
  <svg class="np-icon" viewBox="0 0 18 18" aria-hidden="true">
42
31
  <rect class="mark short" />
43
32
  <rect class="mark long" />
@@ -60,6 +49,9 @@
60
49
  cursor: pointer;
61
50
  margin: max(0px, (48px - 18px)/2);
62
51
  }
52
+ .np-host:has(input:disabled) {
53
+ cursor: default;
54
+ }
63
55
  .np-container {
64
56
  border-radius: inherit;
65
57
  display: flex;
@@ -114,6 +106,12 @@
114
106
  .np-background {
115
107
  border-radius: inherit;
116
108
  }
109
+ .np-outline {
110
+ border-color: var(--np-checkbox-outline-color, var(--np-color-on-surface-variant));
111
+ border-style: solid;
112
+ border-width: 2px;
113
+ box-sizing: border-box;
114
+ }
117
115
  :where(:hover) .np-outline {
118
116
  border-color: var(--np-color-on-surface);
119
117
  border-width: 2px;
@@ -122,11 +120,14 @@
122
120
  border-color: var(--np-color-on-surface);
123
121
  border-width: 2px;
124
122
  }
125
- .np-outline {
126
- border-color: var(--np-checkbox-outline-color, var(--np-color-on-surface-variant));
127
- border-style: solid;
123
+ .np-container:has(input:disabled) .np-outline {
124
+ border-color: var(--np-color-on-surface);
128
125
  border-width: 2px;
129
- box-sizing: border-box;
126
+ opacity: 0.38;
127
+ }
128
+ .np-container:has(input:disabled:checked) .np-outline,
129
+ .np-container:has(input:disabled:indeterminate) .np-outline {
130
+ visibility: hidden;
130
131
  }
131
132
  .np-background {
132
133
  background-color: var(--np-color-primary);
@@ -139,29 +140,42 @@
139
140
  transition-timing-function: cubic-bezier(0.3, 0, 0.8, 0.15), linear;
140
141
  transform: scale(0.6);
141
142
  }
142
- :where(.selected) :is(.np-background, .np-icon) {
143
+ .np-container:has(input:indeterminate) .np-background,
144
+ .np-container:has(input:checked) .np-background,
145
+ .np-container:has(input:indeterminate) .np-icon,
146
+ .np-container:has(input:checked) .np-icon {
143
147
  opacity: 1;
144
148
  transition-duration: 350ms, 50ms;
145
149
  transition-timing-function: cubic-bezier(0.05, 0.7, 0.1, 1), linear;
146
150
  transform: scale(1);
147
151
  }
152
+ .np-container:has(input:disabled:checked) .np-background,
153
+ .np-container:has(input:disabled:indeterminate) .np-background {
154
+ background: var(--np-color-on-surface);
155
+ opacity: 0.38;
156
+ }
148
157
  .np-icon {
149
158
  fill: var(--np-checkbox-selected-icon-color, var(--np-color-on-primary));
150
159
  height: 18px;
151
160
  width: 18px;
152
161
  }
153
- .checked .mark.short,
154
- .prev-checked.unselected .mark.short {
162
+ .np-container:has(input:disabled) .np-icon {
163
+ fill: var(--np-color-surface);
164
+ }
165
+ .np-container:has(input:checked) .mark.short,
166
+ .np-container:has(input:not(:checked):not(:indeterminate)) .mark.short {
155
167
  height: 5.6568542495px;
156
168
  }
157
- .prev-unselected .mark {
169
+ .np-container:has(input:indeterminate) .mark,
170
+ .np-container:has(input:checked) .mark {
158
171
  transition-property: none;
159
172
  }
160
- .checked .mark,
161
- .prev-checked.unselected .mark {
173
+ .np-container:has(input:checked) .mark,
174
+ .np-container:has(input:not(:checked):not(:indeterminate)) .mark {
162
175
  transform: scaleY(-1) translate(7px, -14px) rotate(45deg);
163
176
  }
164
- .selected .mark {
177
+ .np-container:has(input:indeterminate) .mark,
178
+ .np-container:has(input:checked) .mark {
165
179
  animation-duration: 350ms;
166
180
  animation-timing-function: cubic-bezier(0.05, 0.7, 0.1, 1);
167
181
  transition-duration: 350ms;
@@ -178,11 +192,11 @@
178
192
  transition-duration: 150ms;
179
193
  transition-timing-function: cubic-bezier(0.3, 0, 0.8, 0.15);
180
194
  }
181
- .prev-unselected.checked .mark.long {
195
+ .np-container:has(input:checked) .mark.long {
182
196
  animation-name: prev-unselected-to-checked;
183
197
  }
184
- .checked .mark.long,
185
- .prev-checked.unselected .mark.long {
198
+ .np-container:has(input:checked) .mark.long,
199
+ .np-container:has(input:not(:checked):not(:indeterminate)) .mark.long {
186
200
  width: 11.313708499px;
187
201
  }
188
202
  .mark.long {
@@ -190,7 +204,7 @@
190
204
  transition-property: transform, width;
191
205
  width: 10px;
192
206
  }
193
- .indeterminate .mark {
207
+ .np-container:has(input:indeterminate) .mark {
194
208
  transform: scaleY(-1) translate(4px, -10px) rotate(0deg);
195
209
  }
196
210
  @keyframes prev-unselected-to-checked {
@@ -1,4 +1,4 @@
1
- import type { HTMLInputAttributes } from 'svelte/elements';
2
- declare const Checkbox: import("svelte").Component<HTMLInputAttributes, {}, "indeterminate" | "checked">;
1
+ import type { CheckboxProps } from './types.ts';
2
+ declare const Checkbox: import("svelte").Component<CheckboxProps, {}, "checked" | "indeterminate">;
3
3
  type Checkbox = ReturnType<typeof Checkbox>;
4
4
  export default Checkbox;
@@ -1 +1,2 @@
1
1
  export { default as Checkbox } from './Checkbox.svelte';
2
+ export type { CheckboxProps } from './types.ts';
@@ -0,0 +1,2 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ export type CheckboxProps = Omit<HTMLInputAttributes, 'class' | 'type'>;
@@ -0,0 +1 @@
1
+ export {};
@@ -21,11 +21,10 @@
21
21
  -webkit-font-smoothing: antialiased;
22
22
  }
23
23
  .np-icon {
24
- font-variation-settings:
25
- 'FILL' 0,
26
- 'wght' 400,
27
- 'GRAD' 0,
28
- 'opsz' 24;
24
+ transition-property: font-variation-settings;
25
+ transition-timing-function: ease-in;
26
+ transition: font-variation-settings 0.3s;
27
+ font-variation-settings: var(--np-icon-settings, 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24);
29
28
  }
30
29
  :where(.button-icon) .np-icon {
31
30
  display: inline-flex;
@@ -5,7 +5,7 @@
5
5
 
6
6
  interface MenuProps extends HTMLAttributes<HTMLDivElement> {
7
7
  children: Snippet
8
- anchor: HTMLElement | undefined
8
+ anchor?: HTMLElement | undefined
9
9
  }
10
10
 
11
11
  let { anchor, children, ...attributes }: MenuProps = $props()
@@ -17,8 +17,7 @@
17
17
  let scrollY = $state(0)
18
18
  let scrollX = $state(0)
19
19
  let popoverElement: HTMLDivElement | undefined = $state()
20
- let anchorId = `--${generateUUIDv4()}`
21
-
20
+ let anchorId = anchor?.style.getPropertyValue('anchor-name')
22
21
  const refreshValues = () => {
23
22
  if (popoverElement && anchor && !('anchorName' in document.documentElement.style)) {
24
23
  const anchorRect = anchor.getBoundingClientRect()
@@ -52,9 +51,10 @@
52
51
  },
53
52
  { passive: true },
54
53
  )
55
- } else {
56
- popoverElement.style.setProperty('position-anchor', anchorId)
57
- anchor.style.setProperty('anchor-name', anchorId)
54
+ } else if (!anchorId) {
55
+ const generatedId = `--${generateUUIDv4()}`
56
+ popoverElement.style.setProperty('position-anchor', generatedId)
57
+ anchor.style.setProperty('anchor-name', generatedId)
58
58
  }
59
59
  }
60
60
  })
@@ -2,7 +2,7 @@ import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
3
  declare const Menu: import("svelte").Component<HTMLAttributes<HTMLDivElement> & {
4
4
  children: Snippet;
5
- anchor: HTMLElement | undefined;
5
+ anchor?: HTMLElement | undefined;
6
6
  }, {}, "">;
7
7
  type Menu = ReturnType<typeof Menu>;
8
8
  export default Menu;
@@ -1,30 +1,118 @@
1
1
  <script lang="ts">
2
2
  import Ripple from '../ripple/Ripple.svelte'
3
- import type { HTMLInputAttributes } from 'svelte/elements'
4
- let { ...attributes }: HTMLInputAttributes = $props()
3
+ import type { RadioProps } from './types.ts'
5
4
 
6
- const maskId = '1'
5
+ let { ...attributes }: RadioProps = $props()
6
+
7
+ let touchEl: HTMLSpanElement | undefined = $state()
7
8
  </script>
8
9
 
9
- <div class="host">
10
+ <label class="np-host">
11
+ <input {...attributes} type="radio" class="np-input" />
10
12
  <div class="np-container" aria-hidden="true">
11
13
  {#if !attributes.disabled}
12
- <Ripple />
14
+ <Ripple forElement={touchEl} class="np-radio-ripple" />
13
15
  {/if}
14
- <svg class="icon" viewBox="0 0 20 20">
15
- <mask id={maskId}>
16
+ <svg class="np-radio-icon" viewBox="0 0 20 20">
17
+ <mask id="1">
16
18
  <rect width="100%" height="100%" fill="white" />
17
19
  <circle cx="10" cy="10" r="8" fill="black" />
18
20
  </mask>
19
- <circle class="outer circle" cx="10" cy="10" r="10" mask="url(#{maskId})" />
21
+ <circle class="outer circle" cx="10" cy="10" r="10" mask="url(#1)" />
20
22
  <circle class="inner circle" cx="10" cy="10" r="5" />
21
23
  </svg>
22
- <div class="touch-target"></div>
24
+ <span class="np-touch" bind:this={touchEl}></span>
23
25
  </div>
24
- </div>
26
+ </label>
25
27
 
26
28
  <style>
27
- .np-container {
29
+ :global(.np-radio-ripple) {
30
+ border-radius: 50% !important;
31
+ height: 40px;
32
+ inset: unset !important;
33
+ width: 40px;
34
+ }
35
+ .np-input {
36
+ opacity: 0;
37
+ inset: 0;
38
+ position: absolute;
39
+ cursor: inherit;
40
+ }
41
+ .np-host {
42
+ margin: max(0px, (48px - var(--np-radio-icon-size, 20px))/2);
28
43
  position: relative;
44
+ display: inline-flex;
45
+ vertical-align: top;
46
+ width: 20px;
47
+ height: 20px;
48
+ cursor: pointer;
49
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
50
+ outline: none;
51
+ }
52
+
53
+ .np-host:has(input:focus-visible) .np-container {
54
+ outline-style: solid;
55
+ outline-color: var(--np-color-primary);
56
+ outline-width: 3px;
57
+ outline-offset: 12px;
58
+ border-radius: 50%;
59
+ animation: focusAnimation 0.3s ease forwards;
60
+ }
61
+ @keyframes focusAnimation {
62
+ 0% {
63
+ outline-width: 3px;
64
+ }
65
+ 50% {
66
+ outline-width: 6px;
67
+ }
68
+ 100% {
69
+ outline-width: 3px;
70
+ }
71
+ }
72
+
73
+ .np-host:has(input:disabled) {
74
+ cursor: default;
75
+ }
76
+
77
+ .np-container {
78
+ display: flex;
79
+ height: 100%;
80
+ place-content: center;
81
+ place-items: center;
82
+ width: 100%;
83
+ }
84
+ .np-touch {
85
+ height: 48px;
86
+ position: absolute;
87
+ width: 48px;
88
+ }
89
+ .np-radio-icon {
90
+ fill: var(--np-radio-icon-color, var(--np-color-on-surface-variant));
91
+ inset: 0px;
92
+ position: absolute;
93
+ }
94
+ .np-host:has(input:checked) .np-radio-icon {
95
+ fill: var(--np-radio-selected-icon-color, var(--np-color-primary));
96
+ }
97
+ .np-host:has(input:disabled) .np-radio-icon {
98
+ fill: var(--np-color-on-surface);
99
+ opacity: 0.38;
100
+ }
101
+ .inner.circle {
102
+ opacity: 0;
103
+ transform-origin: center center;
104
+ transition: opacity 50ms linear;
105
+ }
106
+ .np-host:has(input:checked) .inner.circle {
107
+ animation: 300ms cubic-bezier(0.05, 0.7, 0.1, 1) 0s 1 normal none running inner-circle-grow;
108
+ opacity: 1;
109
+ }
110
+ @keyframes inner-circle-grow {
111
+ from {
112
+ transform: scale(0);
113
+ }
114
+ to {
115
+ transform: scale(1);
116
+ }
29
117
  }
30
118
  </style>
@@ -1,4 +1,4 @@
1
- import type { HTMLInputAttributes } from 'svelte/elements';
2
- declare const Radio: import("svelte").Component<HTMLInputAttributes, {}, "">;
1
+ import type { RadioProps } from './types.ts';
2
+ declare const Radio: import("svelte").Component<RadioProps, {}, "">;
3
3
  type Radio = ReturnType<typeof Radio>;
4
4
  export default Radio;
@@ -1 +1,2 @@
1
1
  export { default as Radio } from './Radio.svelte';
2
+ export type { RadioProps } from './types.ts';
@@ -0,0 +1,2 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ export type RadioProps = Omit<HTMLInputAttributes, 'class' | 'type'>;
@@ -0,0 +1 @@
1
+ export {};
@@ -345,6 +345,7 @@
345
345
  }
346
346
  }
347
347
 
348
+ .np-ripple-surface:hover:before,
348
349
  .np-ripple-hovered::before {
349
350
  background-color: var(--np-ripple-hover-color, var(--np-color-on-surface));
350
351
  opacity: var(--np-ripple-hover-opacity, 0.08);
@@ -12,6 +12,7 @@
12
12
  start,
13
13
  end,
14
14
  label,
15
+ style,
15
16
  noAsterisk = false,
16
17
  variant = 'filled',
17
18
  placeholder = ' ',
@@ -57,11 +58,11 @@
57
58
  <!-- svelte-ignore a11y_click_events_have_key_events -->
58
59
  <!-- svelte-ignore a11y_no_static_element_interactions -->
59
60
  <span
60
- style={variant === 'outlined'
61
+ style={(variant === 'outlined'
61
62
  ? '--top-space:1rem;--bottom-space:1rem;--floating-label-top:-0.5rem;--floating-label-left:-2.25rem;--_focus-outline-width:3px'
62
63
  : !label?.length
63
64
  ? '--top-space:1rem;--bottom-space:1rem'
64
- : ''}
65
+ : '') + style}
65
66
  class="text-field"
66
67
  onclick={() => {
67
68
  if (attributes.disabled) {
@@ -1 +1,2 @@
1
1
  export { default as TextField } from './TextField.svelte';
2
+ export type { TextAreaFieldProps, TextFieldProps } from './types.ts';
@@ -1,6 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLInputAttributes, HTMLTextareaAttributes } from 'svelte/elements';
3
- export interface TextFieldProps extends HTMLInputAttributes {
3
+ export interface TextFieldProps extends Omit<HTMLInputAttributes, 'class'> {
4
4
  label?: string;
5
5
  type?: 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url';
6
6
  supportingText?: string;
@@ -13,7 +13,7 @@ export interface TextFieldProps extends HTMLInputAttributes {
13
13
  end?: Snippet;
14
14
  noAsterisk?: boolean;
15
15
  }
16
- export interface TextAreaFieldProps extends HTMLTextareaAttributes {
16
+ export interface TextAreaFieldProps extends Omit<HTMLTextareaAttributes, 'class'> {
17
17
  label?: string;
18
18
  type: 'textarea';
19
19
  supportingText?: string;
package/package.json CHANGED
@@ -1,90 +1,89 @@
1
1
  {
2
- "name": "noph-ui",
3
- "version": "0.2.9",
4
- "license": "MIT",
5
- "homepage": "https://noph.dev",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/cnolte/noph-ui"
9
- },
10
- "author": {
11
- "name": "cnolte"
12
- },
13
- "keywords": [
14
- "svelte",
15
- "svelte 5",
16
- "material",
17
- "material 3",
18
- "material you",
19
- "m3",
20
- "ui",
21
- "frontend",
22
- "design-system",
23
- "ui-library",
24
- "theming"
25
- ],
26
- "scripts": {
27
- "dev": "vite dev",
28
- "build": "vite build && npm run package",
29
- "preview": "vite preview",
30
- "package": "svelte-kit sync && svelte-package && publint",
31
- "prepublishOnly": "npm run package",
32
- "test": "npm run test:integration && npm run test:unit",
33
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
34
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
35
- "lint": "prettier --check . && eslint .",
36
- "format": "prettier --write .",
37
- "test:integration": "playwright test",
38
- "test:unit": "vitest"
39
- },
40
- "exports": {
41
- ".": {
42
- "types": "./dist/index.d.ts",
43
- "svelte": "./dist/index.js"
44
- },
45
- "./icons": {
46
- "types": "./dist/icons/index.d.ts",
47
- "svelte": "./dist/icons/index.js"
48
- },
49
- "./defaultTheme": {
50
- "import": "./dist/themes/defaultTheme.css",
51
- "require": "./dist/themes/defaultTheme.css"
52
- }
53
- },
54
- "sideEffects": [
55
- "**/*.css"
56
- ],
57
- "files": [
58
- "dist",
59
- "!dist/**/*.test.*",
60
- "!dist/**/*.spec.*"
61
- ],
62
- "peerDependencies": {
63
- "svelte": "^5.0.0"
64
- },
65
- "devDependencies": {
66
- "@material/material-color-utilities": "^0.3.0",
67
- "@playwright/test": "^1.49.1",
68
- "@sveltejs/adapter-vercel": "^5.5.2",
69
- "@sveltejs/kit": "^2.11.1",
70
- "@sveltejs/package": "^2.3.7",
71
- "@sveltejs/vite-plugin-svelte": "^5.0.2",
72
- "@types/eslint": "^9.6.1",
73
- "eslint": "^9.17.0",
74
- "eslint-config-prettier": "^9.1.0",
75
- "eslint-plugin-svelte": "^2.46.1",
76
- "globals": "^15.13.0",
77
- "prettier": "^3.4.2",
78
- "prettier-plugin-svelte": "^3.3.2",
79
- "publint": "^0.2.12",
80
- "svelte": "^5.14.0",
81
- "svelte-check": "^4.1.1",
82
- "typescript": "^5.7.2",
83
- "typescript-eslint": "^8.18.0",
84
- "vite": "^6.0.3",
85
- "vitest": "^2.1.8"
86
- },
87
- "svelte": "./dist/index.js",
88
- "types": "./dist/index.d.ts",
89
- "type": "module"
90
- }
2
+ "name": "noph-ui",
3
+ "version": "0.3.2",
4
+ "license": "MIT",
5
+ "homepage": "https://noph.dev",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/cnolte/noph-ui"
9
+ },
10
+ "author": {
11
+ "name": "cnolte"
12
+ },
13
+ "keywords": [
14
+ "svelte",
15
+ "svelte 5",
16
+ "material",
17
+ "material 3",
18
+ "material you",
19
+ "m3",
20
+ "ui",
21
+ "frontend",
22
+ "design-system",
23
+ "ui-library",
24
+ "theming"
25
+ ],
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "svelte": "./dist/index.js"
30
+ },
31
+ "./icons": {
32
+ "types": "./dist/icons/index.d.ts",
33
+ "svelte": "./dist/icons/index.js"
34
+ },
35
+ "./defaultTheme": {
36
+ "import": "./dist/themes/defaultTheme.css",
37
+ "require": "./dist/themes/defaultTheme.css"
38
+ }
39
+ },
40
+ "sideEffects": [
41
+ "**/*.css"
42
+ ],
43
+ "files": [
44
+ "dist",
45
+ "!dist/**/*.test.*",
46
+ "!dist/**/*.spec.*"
47
+ ],
48
+ "peerDependencies": {
49
+ "svelte": "^5.0.0"
50
+ },
51
+ "devDependencies": {
52
+ "@material/material-color-utilities": "^0.3.0",
53
+ "@playwright/test": "^1.49.1",
54
+ "@sveltejs/adapter-vercel": "^5.5.2",
55
+ "@sveltejs/kit": "^2.12.1",
56
+ "@sveltejs/package": "^2.3.7",
57
+ "@sveltejs/vite-plugin-svelte": "^5.0.2",
58
+ "@types/eslint": "^9.6.1",
59
+ "eslint": "^9.17.0",
60
+ "eslint-config-prettier": "^9.1.0",
61
+ "eslint-plugin-svelte": "^2.46.1",
62
+ "globals": "^15.13.0",
63
+ "prettier": "^3.4.2",
64
+ "prettier-plugin-svelte": "^3.3.2",
65
+ "publint": "^0.2.12",
66
+ "svelte": "^5.14.1",
67
+ "svelte-check": "^4.1.1",
68
+ "typescript": "^5.7.2",
69
+ "typescript-eslint": "^8.18.1",
70
+ "vite": "^6.0.3",
71
+ "vitest": "^2.1.8"
72
+ },
73
+ "svelte": "./dist/index.js",
74
+ "types": "./dist/index.d.ts",
75
+ "type": "module",
76
+ "scripts": {
77
+ "dev": "vite dev",
78
+ "build": "vite build && npm run package",
79
+ "preview": "vite preview",
80
+ "package": "svelte-kit sync && svelte-package && publint",
81
+ "test": "npm run test:integration && npm run test:unit",
82
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
83
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
84
+ "lint": "prettier --check . && eslint .",
85
+ "format": "prettier --write .",
86
+ "test:integration": "playwright test",
87
+ "test:unit": "vitest"
88
+ }
89
+ }