noph-ui 0.16.3 → 0.16.5

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.
@@ -5,7 +5,7 @@
5
5
  </script>
6
6
 
7
7
  {#if children}
8
- <div class={['np-chip-set', attributes.class]} role="toolbar">
8
+ <div class={['np-chip-set', attributes.class]} style={attributes.style} role="toolbar">
9
9
  {@render children()}
10
10
  </div>
11
11
  {/if}
@@ -14,9 +14,7 @@
14
14
  .np-chip-set {
15
15
  display: flex;
16
16
  flex-wrap: wrap;
17
- padding-top: 2px;
18
- padding-bottom: 2px;
19
- padding-right: 2px;
20
17
  gap: 0.5rem;
18
+ align-items: center;
21
19
  }
22
20
  </style>
@@ -148,6 +148,10 @@
148
148
  color: var(--np-color-primary);
149
149
  display: flex;
150
150
  }
151
+
152
+ .np-filter-chip-disabled .np-chip-icon {
153
+ color: var(--np-color-on-surface);
154
+ }
151
155
  .np-filter-chip:has(input:checked) .np-filter-chip-label,
152
156
  .np-filter-chip-icon .np-filter-chip-label {
153
157
  padding-left: 0.5rem;
@@ -172,7 +176,6 @@
172
176
  inset: 0;
173
177
  border-radius: inherit;
174
178
  pointer-events: none;
175
- background-color: var(--np-color-surface-container-low);
176
179
  }
177
180
  .np-filter-chip-default::before {
178
181
  border-width: 1px;
@@ -184,9 +187,11 @@
184
187
  background-color: var(--np-color-secondary-container);
185
188
  }
186
189
  .np-filter-chip-elevated {
187
- border-width: 0;
188
190
  box-shadow: var(--np-elevation-1);
189
191
  }
192
+ .np-filter-chip-elevated::before {
193
+ background-color: var(--np-color-surface-container-low);
194
+ }
190
195
  .np-filter-chip:has(input:checked) {
191
196
  --np-icon-button-icon-color: var(--np-color-on-secondary-container);
192
197
  }
@@ -217,7 +222,7 @@
217
222
 
218
223
  .np-filter-chip-disabled .np-filter-chip-label {
219
224
  cursor: default;
220
- color: var(--np-color-on-surface);
225
+ color: var(--np-color-on-surface) !important;
221
226
  opacity: 0.38;
222
227
  }
223
228
  .np-filter-chip-disabled.np-filter-chip-elevated {
@@ -238,7 +243,7 @@
238
243
  opacity: 0.12;
239
244
  }
240
245
  .np-filter-chip-disabled::before {
241
- background-color: var(--np-color-on-surface);
246
+ background-color: var(--np-color-on-surface) !important;
242
247
  opacity: 0.12;
243
248
  border-width: 0;
244
249
  }
@@ -2,6 +2,7 @@
2
2
  import IconButton from '../button/IconButton.svelte'
3
3
  import CloseIcon from '../icons/CloseIcon.svelte'
4
4
  import Ripple from '../ripple/Ripple.svelte'
5
+ import { onMount } from 'svelte'
5
6
  import type { InputChipProps } from './types.ts'
6
7
 
7
8
  let {
@@ -14,106 +15,97 @@
14
15
  onremove,
15
16
  name,
16
17
  value,
17
- group = $bindable(),
18
- defaultSelected,
19
18
  ...attributes
20
19
  }: InputChipProps = $props()
21
20
 
22
- let chipLabel: HTMLLabelElement | undefined = $state()
21
+ let chipLabel: HTMLDivElement | undefined = $state()
22
+ let visible = $state(false)
23
23
 
24
- $effect(() => {
25
- if (group && value) {
26
- selected = group.includes(value)
27
- }
28
- })
29
-
30
- $effect(() => {
31
- if (value && group) {
32
- const index = group.indexOf(value)
33
- if (selected) {
34
- if (index < 0) {
35
- group?.push(value)
36
- group = group
24
+ onMount(() => {
25
+ const observer = new IntersectionObserver((entries) => {
26
+ entries.forEach((entry) => {
27
+ if (entry.isIntersecting) {
28
+ visible = true
29
+ observer.disconnect()
37
30
  }
38
- } else {
39
- if (index >= 0) {
40
- group.splice(index, 1)
41
- group = group
42
- }
43
- }
31
+ })
32
+ })
33
+
34
+ if (element) {
35
+ observer.observe(element)
44
36
  }
37
+
38
+ return () => observer.disconnect()
45
39
  })
46
40
  </script>
47
41
 
48
42
  <div
49
43
  {...attributes}
50
44
  bind:this={element}
45
+ tabindex={disabled ? -1 : 0}
46
+ aria-disabled={disabled}
47
+ role="button"
51
48
  class={[
52
- 'np-filter-chip',
53
- icon ? 'np-filter-chip-icon' : '',
54
- disabled ? 'np-filter-chip-disabled' : '',
49
+ 'np-input-chip',
50
+ icon ? 'np-input-chip-icon' : '',
51
+ disabled ? 'np-input-chip-disabled' : '',
52
+ selected ? 'np-input-chip-selected' : '',
55
53
  attributes.class,
56
54
  ]}
57
55
  >
58
- <label bind:this={chipLabel} class="np-filter-chip-label">
59
- {#if icon}
60
- <div class="np-chip-icon">
61
- {@render icon()}
62
- </div>
56
+ {#if visible}
57
+ <div bind:this={chipLabel} class={['np-input-chip-label']}>
58
+ {#if icon}
59
+ <div class="np-chip-icon">
60
+ {@render icon()}
61
+ </div>
62
+ {/if}
63
+ <div class="np-chip-label">{label || value}</div>
64
+ <input type="hidden" {value} {name} {disabled} />
65
+ </div>
66
+ {#if !disabled}
67
+ <Ripple forElement={chipLabel} />
63
68
  {/if}
64
- <div class="np-chip-label">{label}</div>
65
- <input
66
- type="checkbox"
67
- bind:checked={selected}
68
- onclick={(e) => e.preventDefault()}
69
- {value}
70
- {name}
69
+ <IconButton
71
70
  {disabled}
72
- defaultChecked={defaultSelected}
73
- />
74
- </label>
75
- {#if !disabled}
76
- <Ripple forElement={chipLabel} />
71
+ type="button"
72
+ --np-icon-button-container-height="1.75rem"
73
+ --np-icon-button-container-width="1.75rem"
74
+ --np-icon-button-icon-size="1.125rem"
75
+ aria-label={ariaLabelRemove}
76
+ onclick={(
77
+ event: MouseEvent & {
78
+ currentTarget: EventTarget & HTMLButtonElement
79
+ },
80
+ ) => {
81
+ if (element === undefined) {
82
+ return
83
+ }
84
+ onremove?.(event)
85
+ }}
86
+ >
87
+ <CloseIcon />
88
+ </IconButton>
77
89
  {/if}
78
- <IconButton
79
- {disabled}
80
- type="button"
81
- --np-icon-button-container-height="1.75rem"
82
- --np-icon-button-container-width="1.75rem"
83
- --np-icon-button-icon-size="1.125rem"
84
- aria-label={ariaLabelRemove}
85
- onclick={(
86
- event: MouseEvent & {
87
- currentTarget: EventTarget & HTMLButtonElement
88
- },
89
- ) => {
90
- if (element === undefined) {
91
- return
92
- }
93
- onremove?.(event)
94
- }}
95
- >
96
- <CloseIcon />
97
- </IconButton>
98
90
  </div>
99
91
 
100
92
  <style>
101
- .np-filter-chip {
93
+ .np-input-chip {
102
94
  position: relative;
103
95
  display: inline-flex;
104
96
  align-items: center;
105
97
  user-select: none;
106
- border-radius: var(--np-filter-chip-container-shape, var(--np-shape-corner-small));
98
+ border-radius: var(--np-input-chip-container-shape, var(--np-shape-corner-small));
107
99
  --np-icon-button-icon-color: var(--np-color-on-surface-variant);
108
100
  --np-icon-size: 1.125rem;
109
101
  padding-right: 2px;
110
102
  }
111
- .np-filter-chip-label input {
103
+ .np-input-chip-label input {
112
104
  opacity: 0;
113
105
  position: absolute;
114
106
  pointer-events: none;
115
107
  }
116
- .np-filter-chip-label {
108
+ .np-input-chip-label {
117
109
  cursor: pointer;
118
110
  display: inline-flex;
119
111
  align-items: center;
@@ -131,7 +123,7 @@
131
123
  color: var(--np-color-primary);
132
124
  display: flex;
133
125
  }
134
- .np-filter-chip-icon .np-filter-chip-label {
126
+ .np-input-chip-icon .np-input-chip-label {
135
127
  padding-left: 0.5rem;
136
128
  }
137
129
  .np-chip-label {
@@ -143,31 +135,27 @@
143
135
  overflow: hidden;
144
136
  text-overflow: ellipsis;
145
137
  }
146
- .np-filter-chip::before {
138
+ .np-input-chip::before {
147
139
  content: '';
148
140
  position: absolute;
149
141
  inset: 0;
150
142
  border-radius: inherit;
151
143
  pointer-events: none;
152
- background-color: var(--np-color-surface-container-low);
153
144
  border-width: 1px;
154
145
  border-style: solid;
155
- border-color: var(--np-filter-chip-outline-color, var(--np-color-outline-variant));
146
+ border-color: var(--np-input-chip-outline-color, var(--np-color-outline-variant));
156
147
  }
157
- .np-filter-chip:has(input:checked)::before {
148
+ .np-input-chip-selected::before {
158
149
  border-width: 0;
159
150
  background-color: var(--np-color-secondary-container);
160
151
  }
161
- .np-filter-chip:has(input:checked) {
152
+ .np-input-chip-selected {
162
153
  --np-icon-button-icon-color: var(--np-color-on-secondary-container);
163
154
  }
164
- .np-filter-chip:has(input:checked) .np-filter-chip-label {
155
+ .np-input-chip-selected .np-input-chip-label {
165
156
  color: var(--np-color-on-secondary-container);
166
157
  }
167
- .np-filter-chip-label:focus-visible {
168
- outline-width: 0;
169
- }
170
- .np-filter-chip:has(input:focus-visible) {
158
+ .np-input-chip:focus-visible {
171
159
  outline-style: solid;
172
160
  outline-color: var(--np-color-primary);
173
161
  outline-width: 3px;
@@ -186,15 +174,15 @@
186
174
  }
187
175
  }
188
176
 
189
- .np-filter-chip-disabled .np-filter-chip-label {
177
+ .np-input-chip-disabled .np-input-chip-label {
190
178
  cursor: default;
191
179
  color: var(--np-color-on-surface);
192
180
  opacity: 0.38;
193
181
  }
194
- .np-filter-chip-disabled:has(input:checked)::before {
182
+ .np-input-chip-disabled.np-input-chip-selected::before {
195
183
  opacity: 0.12;
196
184
  }
197
- .np-filter-chip-disabled:has(input:not(:checked)).np-filter-chip::after {
185
+ .np-input-chip-disabled:not(.np-input-chip-selected).np-input-chip::after {
198
186
  content: '';
199
187
  position: absolute;
200
188
  inset: 0;
@@ -205,9 +193,10 @@
205
193
  border-color: var(--np-color-on-surface);
206
194
  opacity: 0.12;
207
195
  }
208
- .np-filter-chip-disabled::before {
209
- background-color: var(--np-color-on-surface);
196
+ .np-input-chip-disabled::before {
210
197
  opacity: 0.12;
211
- border-width: 0;
198
+ }
199
+ .np-input-chip-selected.np-input-chip-disabled::before {
200
+ background-color: var(--np-color-on-surface);
212
201
  }
213
202
  </style>
@@ -1,4 +1,4 @@
1
1
  import type { InputChipProps } from './types.ts';
2
- declare const InputChip: import("svelte").Component<InputChipProps, {}, "element" | "group" | "selected">;
2
+ declare const InputChip: import("svelte").Component<InputChipProps, {}, "element" | "selected">;
3
3
  type InputChip = ReturnType<typeof InputChip>;
4
4
  export default InputChip;
@@ -42,8 +42,6 @@ export interface InputChipProps extends HTMLAttributes<HTMLDivElement> {
42
42
  element?: HTMLDivElement;
43
43
  name?: string;
44
44
  value?: string;
45
- group?: (string | number)[] | null;
46
- defaultSelected?: boolean | null;
47
45
  onremove?: (event: MouseEvent & {
48
46
  currentTarget: EventTarget & HTMLButtonElement;
49
47
  }) => void;
@@ -2,6 +2,7 @@
2
2
  import Ripple from '../ripple/Ripple.svelte'
3
3
  import type { FocusEventHandler } from 'svelte/elements'
4
4
  import type { ItemProps } from './types.ts'
5
+ import { onMount } from 'svelte'
5
6
 
6
7
  let {
7
8
  selected = false,
@@ -16,7 +17,24 @@
16
17
  }: ItemProps = $props()
17
18
 
18
19
  let focused = $state(false)
20
+ let visible = $state(false)
19
21
  let element: HTMLButtonElement | HTMLAnchorElement | undefined = $state()
22
+ onMount(() => {
23
+ const observer = new IntersectionObserver((entries) => {
24
+ entries.forEach((entry) => {
25
+ if (entry.isIntersecting) {
26
+ visible = true
27
+ observer.disconnect()
28
+ }
29
+ })
30
+ })
31
+
32
+ if (element) {
33
+ observer.observe(element)
34
+ }
35
+
36
+ return () => observer.disconnect()
37
+ })
20
38
  </script>
21
39
 
22
40
  {#snippet content()}
@@ -67,8 +85,10 @@
67
85
  class={['np-item', selected && 'selected', attributes.class]}
68
86
  bind:this={element}
69
87
  >
70
- {@render content()}
71
- <Ripple forceHover={focused} />
88
+ {#if visible}
89
+ {@render content()}
90
+ <Ripple forceHover={focused} />
91
+ {/if}
72
92
  </button>
73
93
  {:else if attributes.variant === 'link'}
74
94
  <a
@@ -84,8 +104,10 @@
84
104
  class={['np-item', selected && 'selected', attributes.class]}
85
105
  bind:this={element}
86
106
  >
87
- {@render content()}
88
- <Ripple forceHover={focused} />
107
+ {#if visible}
108
+ {@render content()}
109
+ <Ripple forceHover={focused} />
110
+ {/if}
89
111
  </a>
90
112
  {/if}
91
113
 
@@ -44,7 +44,7 @@
44
44
  }
45
45
  let selectedOption = $state(options.filter((option) => option.selected))
46
46
 
47
- let useVirtualList = $derived(options.length > 500)
47
+ let useVirtualList = $derived(options.length > 4000)
48
48
 
49
49
  let errorTextRaw: string = $state(errorText)
50
50
  let errorRaw = $state(error)
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { isFirstInvalidControlInForm } from './report-validity.js'
3
+ import type { FocusEventHandler } from 'svelte/elements'
3
4
  import type { TextFieldProps } from './types.ts'
4
5
 
5
6
  let {
@@ -15,11 +16,14 @@
15
16
  style,
16
17
  noAsterisk = false,
17
18
  variant = 'filled',
18
- placeholder = ' ',
19
19
  element = $bindable(),
20
+ populated = false,
20
21
  inputElement = $bindable(),
21
22
  reportValidity = $bindable(),
22
23
  checkValidity = $bindable(),
24
+ children,
25
+ onfocus,
26
+ onblur,
23
27
  ...attributes
24
28
  }: TextFieldProps = $props()
25
29
 
@@ -27,6 +31,7 @@
27
31
  let errorTextRaw: string = $state(errorText)
28
32
  let focusOnInvalid = $state(true)
29
33
  let doValidity = $state(false)
34
+ let focused = $state(false)
30
35
 
31
36
  reportValidity = () => {
32
37
  if (inputElement) {
@@ -89,14 +94,26 @@
89
94
  })
90
95
  </script>
91
96
 
92
- <label
97
+ <div
93
98
  style={(variant === 'outlined'
94
99
  ? '--_label-text-color:var(--np-outlined-text-field-label-text-color);--top-space:1rem;--bottom-space:1rem;--floating-label-top:-0.5rem;--floating-label-left:-2.25rem;--_focus-outline-width:3px;'
95
100
  : !label?.length
96
- ? '--_label-text-color:var(--np-filled-text-field-label-text-color);--top-space:1rem;--bottom-space:1rem; '
97
- : '--_label-text-color:var(--np-filled-text-field-label-text-color); ') + style}
101
+ ? '--_label-text-color:var(--np-filled-text-field-label-text-color);--np-input-chip-outline-color:var(--np-color-outline);--top-space:1rem;--bottom-space:1rem; '
102
+ : '--_label-text-color:var(--np-filled-text-field-label-text-color);--np-input-chip-outline-color:var(--np-color-outline); ' +
103
+ (children ? '--top-space:2rem;--bottom-space:1rem;' : '')) + style}
98
104
  class={['text-field', attributes.class]}
99
105
  bind:this={element}
106
+ role="button"
107
+ tabindex="-1"
108
+ onfocus={() => {
109
+ inputElement?.focus()
110
+ }}
111
+ onkeydown={(event) => {
112
+ if (event.key === 'Enter' || event.key === ' ') {
113
+ event.preventDefault()
114
+ inputElement?.focus()
115
+ }
116
+ }}
100
117
  >
101
118
  <div
102
119
  class="field"
@@ -106,6 +123,8 @@
106
123
  class:with-start={start}
107
124
  class:with-end={end}
108
125
  class:disabled={attributes.disabled}
126
+ class:populated={(value !== '' && value !== undefined && value !== null) || populated}
127
+ class:focused
109
128
  class:outlined={variant === 'outlined'}
110
129
  >
111
130
  <div class="container-overflow">
@@ -119,7 +138,9 @@
119
138
  <div class="outline-start"></div>
120
139
  {#if label?.length}
121
140
  <div class="label-wrapper">
122
- <span class="label">{label}{noAsterisk || !attributes.required ? '' : '*'} </span>
141
+ <span class="label" aria-disabled={attributes.disabled}
142
+ >{label}{noAsterisk || !attributes.required ? '' : '*'}
143
+ </span>
123
144
  </div>
124
145
  <div class="outline-notch">
125
146
  <span class="notch np-hidden" aria-hidden="true"
@@ -140,7 +161,9 @@
140
161
  {#if variant === 'filled'}
141
162
  <div class="label-wrapper">
142
163
  {#if label?.length}
143
- <span class="label">{label}{noAsterisk || !attributes.required ? '' : '*'} </span>
164
+ <span class="label" aria-disabled={attributes.disabled}
165
+ >{label}{noAsterisk || !attributes.required ? '' : '*'}
166
+ </span>
144
167
  {/if}
145
168
  </div>
146
169
  {/if}
@@ -149,10 +172,17 @@
149
172
  <textarea
150
173
  aria-label={label}
151
174
  {...attributes}
175
+ onfocus={(event) => {
176
+ focused = true
177
+ ;(onfocus as FocusEventHandler<HTMLTextAreaElement>)?.(event)
178
+ }}
179
+ onblur={(event) => {
180
+ focused = false
181
+ ;(onblur as FocusEventHandler<HTMLTextAreaElement>)?.(event)
182
+ }}
152
183
  bind:value
153
184
  bind:this={inputElement}
154
185
  class="input"
155
- {placeholder}
156
186
  rows={attributes.rows || 2}
157
187
  ></textarea>
158
188
  {:else}
@@ -162,13 +192,21 @@
162
192
  {prefixText}
163
193
  </span>
164
194
  {/if}
195
+ {@render children?.()}
165
196
  <input
166
197
  aria-label={label}
167
198
  {...attributes}
168
199
  bind:value
169
200
  bind:this={inputElement}
201
+ onfocus={(event) => {
202
+ focused = true
203
+ ;(onfocus as FocusEventHandler<HTMLInputElement>)?.(event)
204
+ }}
205
+ onblur={(event) => {
206
+ focused = false
207
+ ;(onblur as FocusEventHandler<HTMLInputElement>)?.(event)
208
+ }}
170
209
  class="input"
171
- {placeholder}
172
210
  aria-invalid={errorRaw}
173
211
  />
174
212
  {#if suffixText}
@@ -198,7 +236,7 @@
198
236
  </div>
199
237
  {/if}
200
238
  </div>
201
- </label>
239
+ </div>
202
240
 
203
241
  <style>
204
242
  .active-indicator {
@@ -356,6 +394,7 @@
356
394
  top: var(--_focus-outline-width, 3px);
357
395
  inset-inline-start: var(--_focus-outline-width, 0);
358
396
  }
397
+
359
398
  .content * {
360
399
  all: unset;
361
400
  color: currentColor;
@@ -366,6 +405,17 @@
366
405
  white-space: revert;
367
406
  }
368
407
 
408
+ .input-wrapper {
409
+ display: flex;
410
+ flex-wrap: wrap;
411
+ align-items: baseline;
412
+ }
413
+
414
+ .input-wrapper > * {
415
+ all: inherit;
416
+ padding: 0;
417
+ }
418
+
369
419
  .middle {
370
420
  align-items: stretch;
371
421
  align-self: baseline;
@@ -376,6 +426,7 @@
376
426
  caret-color: var(--np-color-primary);
377
427
  overflow-x: hidden;
378
428
  text-align: inherit;
429
+ flex: 1;
379
430
 
380
431
  &::placeholder {
381
432
  color: currentColor;
@@ -399,8 +450,7 @@
399
450
  .no-label .content,
400
451
  .field:has(input:focus-visible) .content,
401
452
  .field:has(textarea:focus-visible) .content,
402
- .field:has(input:not(:placeholder-shown)) .content,
403
- .field:has(textarea:not(:placeholder-shown)) .content {
453
+ .field.populated .content {
404
454
  opacity: 1;
405
455
  }
406
456
 
@@ -429,18 +479,18 @@
429
479
  margin-bottom: var(--bottom-space, 0.5rem);
430
480
  }
431
481
 
432
- .input-wrapper {
433
- display: flex;
482
+ .content .input-wrapper .input,
483
+ .content .input-wrapper .prefix,
484
+ .content .input-wrapper .suffix {
485
+ margin-top: var(--top-space, 1.5rem);
486
+ margin-bottom: var(--bottom-space, 0.5rem);
434
487
  }
435
-
436
- .input-wrapper > * {
437
- all: inherit;
438
- padding: 0;
488
+ .content .input-wrapper .input {
489
+ min-width: 100px;
439
490
  }
440
-
441
- .content .input-wrapper {
442
- padding-top: var(--top-space, 1.5rem);
443
- padding-bottom: var(--bottom-space, 0.5rem);
491
+ :global(.content .input-wrapper .np-chip-set) {
492
+ margin-top: calc(var(--top-space, 1.5rem) - 4px);
493
+ margin-right: 0.5rem;
444
494
  }
445
495
 
446
496
  .prefix {
@@ -456,6 +506,8 @@
456
506
  user-select: none;
457
507
  text-wrap: nowrap;
458
508
  width: min-content;
509
+ display: flex;
510
+ align-items: center;
459
511
  }
460
512
  .start {
461
513
  color: var(--np-color-on-surface-variant);
@@ -520,21 +572,13 @@
520
572
  .with-end .np-outline .label-wrapper {
521
573
  margin-inline-end: 3.25rem;
522
574
  }
523
- .with-start:has(input:focus-visible:not(:placeholder-shown)) .label-wrapper,
524
- .with-start:has(input:focus-visible) .label-wrapper,
525
- .with-start:has(input:not(:placeholder-shown)) .label-wrapper,
526
- .with-start:has(textarea:focus-visible:not(:placeholder-shown)) .label-wrapper,
527
- .with-start:has(textarea:focus-visible) .label-wrapper,
528
- .with-start:has(textarea:not(:placeholder-shown)) .label-wrapper {
575
+ .with-start.populated .with-start:has(input:focus-visible) .label-wrapper,
576
+ .with-start:has(textarea:focus-visible) .label-wrapper {
529
577
  right: -2.25rem;
530
578
  }
531
579
 
532
- .with-end:has(input:focus-visible:not(:placeholder-shown)) .label-wrapper,
533
- .with-end:has(input:focus-visible) .label-wrapper,
534
- .with-end:has(input:not(:placeholder-shown)) .label-wrapper,
535
- .with-end:has(textarea:focus-visible:not(:placeholder-shown)) .label-wrapper,
536
- .with-end:has(textarea:focus-visible) .label-wrapper,
537
- .with-end:has(textarea:not(:placeholder-shown)) .label-wrapper {
580
+ .with-end.populated .with-end:has(input:focus-visible) .label-wrapper,
581
+ .with-end:has(textarea:focus-visible) .label-wrapper {
538
582
  margin-inline-end: 1rem;
539
583
  }
540
584
  .notch {
@@ -549,30 +593,23 @@
549
593
  opacity: 0;
550
594
  }
551
595
 
552
- .field:has(input:not(:focus-visible):placeholder-shown) .label,
553
- .field:has(textarea:not(:focus-visible):placeholder-shown) .label {
596
+ .field:not(.populated) .label {
554
597
  position: absolute;
555
598
  top: 1rem;
556
599
  left: 0rem;
557
600
  }
558
- .field:has(input:focus-visible:not(:placeholder-shown)) .label,
601
+ .field.populated .label,
559
602
  .field:has(input:focus-visible) .label,
560
- .field:has(input:not(:placeholder-shown)) .label,
561
- .field:has(textarea:focus-visible:not(:placeholder-shown)) .label,
562
- .field:has(textarea:focus-visible) .label,
563
- .field:has(textarea:not(:placeholder-shown)) .label {
603
+ .field:has(textarea:focus-visible) .label {
564
604
  font-size: 0.75rem;
565
605
  line-height: 1rem;
566
606
  transform-origin: top left;
567
607
  position: absolute;
568
608
  top: var(--floating-label-top, 0.5rem);
569
609
  }
570
- .with-start:has(input:focus-visible:not(:placeholder-shown)) .label,
610
+ .with-start.populated .label,
571
611
  .with-start:has(input:focus-visible) .label,
572
- .with-start:has(input:not(:placeholder-shown)) .label,
573
- .with-start:has(textarea:focus-visible:not(:placeholder-shown)) .label,
574
- .with-start:has(textarea:focus-visible) .label,
575
- .with-start:has(textarea:not(:placeholder-shown)) .label {
612
+ .with-start:has(textarea:focus-visible) .label {
576
613
  left: var(--floating-label-left, 0);
577
614
  }
578
615
  .label {
@@ -610,8 +647,7 @@
610
647
  overflow: hidden;
611
648
  }
612
649
  .disabled.no-label .content,
613
- .disabled:has(input:not(:placeholder-shown)) .content,
614
- .disabled:has(textarea:not(:placeholder-shown)) .content {
650
+ .disabled.populated .content {
615
651
  opacity: 0.38;
616
652
  }
617
653
  .field,
@@ -687,8 +723,7 @@
687
723
  }
688
724
  .field:has(input:focus-visible) .outline-notch::before,
689
725
  .field:has(textarea:focus-visible) .outline-notch::before,
690
- .field:has(input:not(:placeholder-shown)) .outline-notch::before,
691
- .field:has(textarea:not(:placeholder-shown)) .outline-notch::before {
726
+ .field.populated .outline-notch::before {
692
727
  border-top-style: none;
693
728
  }
694
729
 
@@ -13,6 +13,7 @@ interface FieldProps {
13
13
  noAsterisk?: boolean;
14
14
  element?: HTMLSpanElement;
15
15
  inputElement?: HTMLInputElement | HTMLTextAreaElement;
16
+ populated?: boolean;
16
17
  reportValidity?: () => boolean;
17
18
  checkValidity?: () => boolean;
18
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "noph-ui",
3
- "version": "0.16.3",
3
+ "version": "0.16.5",
4
4
  "license": "MIT",
5
5
  "homepage": "https://noph.dev",
6
6
  "repository": {
@@ -52,27 +52,27 @@
52
52
  "svelte": "^5.20.0"
53
53
  },
54
54
  "devDependencies": {
55
- "@eslint/js": "^9.22.0",
55
+ "@eslint/js": "^9.23.0",
56
56
  "@material/material-color-utilities": "^0.3.0",
57
- "@playwright/test": "^1.51.0",
57
+ "@playwright/test": "^1.51.1",
58
58
  "@sveltejs/adapter-vercel": "^5.6.3",
59
- "@sveltejs/kit": "^2.19.0",
59
+ "@sveltejs/kit": "^2.20.2",
60
60
  "@sveltejs/package": "^2.3.10",
61
61
  "@sveltejs/vite-plugin-svelte": "^5.0.3",
62
62
  "@types/eslint": "^9.6.1",
63
- "eslint": "^9.22.0",
63
+ "eslint": "^9.23.0",
64
64
  "eslint-config-prettier": "^10.1.1",
65
- "eslint-plugin-svelte": "^3.1.0",
65
+ "eslint-plugin-svelte": "^3.3.3",
66
66
  "globals": "^16.0.0",
67
67
  "prettier": "^3.5.3",
68
68
  "prettier-plugin-svelte": "^3.3.3",
69
69
  "publint": "^0.3.9",
70
- "svelte": "^5.22.6",
70
+ "svelte": "^5.25.3",
71
71
  "svelte-check": "^4.1.5",
72
72
  "typescript": "^5.8.2",
73
- "typescript-eslint": "^8.26.1",
74
- "vite": "^6.2.1",
75
- "vitest": "^3.0.8"
73
+ "typescript-eslint": "^8.27.0",
74
+ "vite": "^6.2.2",
75
+ "vitest": "^3.0.9"
76
76
  },
77
77
  "svelte": "./dist/index.js",
78
78
  "types": "./dist/index.d.ts",