noph-ui 0.17.11 → 0.17.13

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.
@@ -0,0 +1,146 @@
1
+ <script lang="ts">
2
+ import Item from '../list/Item.svelte'
3
+ import Menu from '../menu/Menu.svelte'
4
+ import VirtualList from '../select/VirtualList.svelte'
5
+ import { tick } from 'svelte'
6
+ import type { AutoCompleteOption, AutoCompleteProps } from './types.ts'
7
+ import TextField from '../text-field/TextField.svelte'
8
+
9
+ let {
10
+ options = [],
11
+ value = $bindable(),
12
+ variant = 'filled',
13
+ element = $bindable(),
14
+ onchange,
15
+ oninput,
16
+ reportValidity = $bindable(),
17
+ checkValidity = $bindable(),
18
+ clampMenuWidth = false,
19
+ children,
20
+ optionsFilter = (option) => {
21
+ return !value || option.value.includes(value)
22
+ },
23
+ oncomplete = (option) => {
24
+ value = option.value
25
+ console.log(menuElement)
26
+ menuElement?.hidePopover()
27
+ },
28
+ onblur,
29
+ onclick,
30
+ ...attributes
31
+ }: AutoCompleteProps = $props()
32
+
33
+ const uid = $props.id()
34
+ let displayOptions = $derived(options.filter(optionsFilter))
35
+ let useVirtualList = $derived(displayOptions.length > 4000)
36
+ let clientWidth = $state(0)
37
+ let menuOpen = $state(false)
38
+ let menuElement: HTMLDivElement | undefined = $state()
39
+
40
+ const handleOptionSelect = (event: Event, option: AutoCompleteOption) => {
41
+ value = option.value
42
+ menuElement?.hidePopover()
43
+ event.preventDefault()
44
+ // tick().then(() => {
45
+ // if (checkValidity?.()) {
46
+ // errorRaw = error
47
+ // errorTextRaw = errorText
48
+ // }
49
+ // element?.dispatchEvent(new Event('change', { bubbles: true }))
50
+ // })
51
+ }
52
+ $effect(() => {
53
+ if (displayOptions.length) {
54
+ menuElement?.showPopover()
55
+ } else {
56
+ menuElement?.hidePopover()
57
+ }
58
+ })
59
+ </script>
60
+
61
+ {#snippet item(option: AutoCompleteOption)}
62
+ <Item
63
+ onclick={() => {
64
+ oncomplete(option)
65
+ element?.focus()
66
+ }}
67
+ disabled={option.disabled}
68
+ onkeydown={(event) => {
69
+ if (event.key === 'ArrowDown') {
70
+ ;(event.currentTarget?.nextElementSibling as HTMLElement)?.focus()
71
+ event.preventDefault()
72
+ }
73
+ if (event.key === 'ArrowUp') {
74
+ ;(event.currentTarget?.previousElementSibling as HTMLElement)?.focus()
75
+ event.preventDefault()
76
+ }
77
+ if (event.key === 'Enter') {
78
+ oncomplete(option)
79
+ }
80
+ if (event.key === 'Tab') {
81
+ menuElement?.hidePopover()
82
+ }
83
+ }}
84
+ variant="button"
85
+ >{option.value}
86
+ </Item>
87
+ {/snippet}
88
+
89
+ <TextField
90
+ {variant}
91
+ bind:clientWidth
92
+ bind:value
93
+ style="anchor-name:--{uid};"
94
+ onclick={async (
95
+ event: MouseEvent & {
96
+ currentTarget: EventTarget & HTMLInputElement
97
+ },
98
+ ) => {
99
+ if (displayOptions.length) {
100
+ menuElement?.showPopover()
101
+ }
102
+ onclick?.(event)
103
+ }}
104
+ onfocus={() => {
105
+ if (displayOptions.length) {
106
+ menuElement?.showPopover()
107
+ }
108
+ }}
109
+ bind:reportValidity
110
+ bind:checkValidity
111
+ bind:element
112
+ {...attributes}>{@render children?.()}</TextField
113
+ >
114
+ <Menu
115
+ style="position-anchor:--{uid};{clampMenuWidth || useVirtualList
116
+ ? 'width'
117
+ : 'min-width'}:{clientWidth}px"
118
+ role="listbox"
119
+ --np-menu-justify-self="none"
120
+ --np-menu-position-area="bottom span-right"
121
+ --np-menu-margin="2px 0"
122
+ --np-menu-container-shape={variant === 'outlined'
123
+ ? 'var(--np-outlined-select-text-field-container-shape)'
124
+ : 'var(--np-filled-select-text-field-container-shape)'}
125
+ anchor={element}
126
+ ontoggle={({ newState }) => {
127
+ if (newState === 'open') {
128
+ menuOpen = true
129
+ } else {
130
+ menuOpen = false
131
+ }
132
+ }}
133
+ bind:element={menuElement}
134
+ >
135
+ {#if useVirtualList}
136
+ <VirtualList height="250px" itemHeight={56} items={displayOptions}>
137
+ {#snippet row(option)}
138
+ {@render item(option)}
139
+ {/snippet}
140
+ </VirtualList>
141
+ {:else}
142
+ {#each displayOptions as option, index (index)}
143
+ {@render item(option)}
144
+ {/each}
145
+ {/if}
146
+ </Menu>
@@ -0,0 +1,4 @@
1
+ import type { AutoCompleteProps } from './types.ts';
2
+ declare const AutoComplete: import("svelte").Component<AutoCompleteProps, {}, "element" | "value" | "reportValidity" | "checkValidity">;
3
+ type AutoComplete = ReturnType<typeof AutoComplete>;
4
+ export default AutoComplete;
@@ -0,0 +1 @@
1
+ export { default as AutoComplete } from './AutoComplete.svelte';
@@ -0,0 +1 @@
1
+ export { default as AutoComplete } from './AutoComplete.svelte';
@@ -0,0 +1,12 @@
1
+ import type { InputFieldProps } from '../types.ts';
2
+ export interface AutoCompleteOption {
3
+ value: string;
4
+ disabled?: boolean;
5
+ selected?: boolean | undefined | null;
6
+ }
7
+ export interface AutoCompleteProps extends Omit<InputFieldProps, 'clientWidth' | 'clientHeight'> {
8
+ options: AutoCompleteOption[];
9
+ optionsFilter?: (option: AutoCompleteOption) => boolean;
10
+ oncomplete?: (option: AutoCompleteOption) => void;
11
+ clampMenuWidth?: boolean;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
1
  import type { CheckboxProps } from './types.ts';
2
- declare const Checkbox: import("svelte").Component<CheckboxProps, {}, "element" | "group" | "indeterminate" | "checked">;
2
+ declare const Checkbox: import("svelte").Component<CheckboxProps, {}, "element" | "group" | "checked" | "indeterminate">;
3
3
  type Checkbox = ReturnType<typeof Checkbox>;
4
4
  export default Checkbox;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './autocomplete/index.js';
1
2
  export * from './button/index.js';
2
3
  export * from './card/index.js';
3
4
  export * from './checkbox/index.js';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './autocomplete/index.js';
1
2
  export * from './button/index.js';
2
3
  export * from './card/index.js';
3
4
  export * from './checkbox/index.js';
@@ -8,6 +8,7 @@
8
8
  showPopover = $bindable(),
9
9
  hidePopover = $bindable(),
10
10
  style,
11
+ popover = 'auto',
11
12
  ...attributes
12
13
  }: MenuProps = $props()
13
14
 
@@ -119,7 +120,7 @@
119
120
  menuOpen = newState === 'open'
120
121
  attributes.ontoggle?.(event)
121
122
  }}
122
- popover="auto"
123
+ {popover}
123
124
  class={['np-menu-container', attributes.class]}
124
125
  >
125
126
  <div class="np-menu">
@@ -367,7 +367,6 @@
367
367
  style="position-anchor:--{uid};{clampMenuWidth || useVirtualList
368
368
  ? 'width'
369
369
  : 'min-width'}:{clientWidth}px"
370
- popover="manual"
371
370
  role="listbox"
372
371
  --np-menu-justify-self="none"
373
372
  --np-menu-position-area="bottom span-right"
@@ -24,6 +24,8 @@
24
24
  children,
25
25
  onfocus,
26
26
  onblur,
27
+ clientWidth = $bindable(),
28
+ clientHeight = $bindable(),
27
29
  ...attributes
28
30
  }: TextFieldProps = $props()
29
31
 
@@ -95,6 +97,7 @@
95
97
  })
96
98
  </script>
97
99
 
100
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
98
101
  <div
99
102
  style={(variant === 'outlined'
100
103
  ? '--_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;'
@@ -104,11 +107,16 @@
104
107
  (children ? '--top-space:2rem;--bottom-space:1rem;' : '')) + style}
105
108
  class={['text-field', attributes.class]}
106
109
  bind:this={element}
110
+ bind:clientWidth
111
+ bind:clientHeight
107
112
  role="button"
108
- tabindex="-1"
113
+ tabindex="0"
109
114
  onfocus={() => {
110
115
  inputElement?.focus()
111
116
  }}
117
+ onclick={() => {
118
+ inputElement?.click()
119
+ }}
112
120
  >
113
121
  <div
114
122
  class="field"
@@ -403,6 +411,7 @@
403
411
 
404
412
  .input-wrapper {
405
413
  display: flex;
414
+ flex-wrap: wrap;
406
415
  align-items: baseline;
407
416
  min-width: 0;
408
417
  }
@@ -484,7 +493,7 @@
484
493
  margin-bottom: var(--bottom-space, 0.5rem);
485
494
  }
486
495
  .content .input-wrapper .input {
487
- min-width: 0;
496
+ min-width: 20px;
488
497
  }
489
498
  :global(.content .input-wrapper .np-chip-set) {
490
499
  margin-top: calc(var(--top-space, 1.5rem) - 4px);
@@ -1,4 +1,4 @@
1
1
  import type { TextFieldProps } from './types.ts';
2
- declare const TextField: import("svelte").Component<TextFieldProps, {}, "element" | "value" | "reportValidity" | "checkValidity" | "inputElement">;
2
+ declare const TextField: import("svelte").Component<TextFieldProps, {}, "element" | "value" | "reportValidity" | "checkValidity" | "inputElement" | "clientWidth" | "clientHeight">;
3
3
  type TextField = ReturnType<typeof TextField>;
4
4
  export default TextField;
@@ -16,6 +16,8 @@ interface FieldProps {
16
16
  populated?: boolean;
17
17
  reportValidity?: () => boolean;
18
18
  checkValidity?: () => boolean;
19
+ clientWidth?: number;
20
+ clientHeight?: number;
19
21
  }
20
22
  export interface InputFieldProps extends HTMLInputAttributes, FieldProps {
21
23
  type?: 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url' | 'datetime-local';
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './autocomplete/types.js';
1
2
  export * from './button/types.ts';
2
3
  export * from './card/types.ts';
3
4
  export * from './checkbox/types.ts';
package/dist/types.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './autocomplete/types.js';
1
2
  export * from './button/types.ts';
2
3
  export * from './card/types.ts';
3
4
  export * from './checkbox/types.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "noph-ui",
3
- "version": "0.17.11",
3
+ "version": "0.17.13",
4
4
  "license": "MIT",
5
5
  "homepage": "https://noph.dev",
6
6
  "repository": {
@@ -53,27 +53,27 @@
53
53
  "svelte": "^5.32.1"
54
54
  },
55
55
  "devDependencies": {
56
- "@eslint/js": "^9.28.0",
56
+ "@eslint/js": "^9.29.0",
57
57
  "@material/material-color-utilities": "^0.3.0",
58
- "@playwright/test": "^1.52.0",
58
+ "@playwright/test": "^1.53.0",
59
59
  "@sveltejs/adapter-vercel": "^5.7.2",
60
- "@sveltejs/kit": "^2.21.2",
60
+ "@sveltejs/kit": "^2.21.5",
61
61
  "@sveltejs/package": "^2.3.11",
62
62
  "@sveltejs/vite-plugin-svelte": "^5.1.0",
63
63
  "@types/eslint": "^9.6.1",
64
- "eslint": "^9.28.0",
64
+ "eslint": "^9.29.0",
65
65
  "eslint-config-prettier": "^10.1.5",
66
- "eslint-plugin-svelte": "^3.9.1",
66
+ "eslint-plugin-svelte": "^3.9.2",
67
67
  "globals": "^16.2.0",
68
68
  "prettier": "^3.5.3",
69
69
  "prettier-plugin-svelte": "^3.4.0",
70
70
  "publint": "^0.3.12",
71
- "svelte": "^5.33.16",
71
+ "svelte": "^5.34.3",
72
72
  "svelte-check": "^4.2.1",
73
73
  "typescript": "^5.8.3",
74
- "typescript-eslint": "^8.33.1",
74
+ "typescript-eslint": "^8.34.1",
75
75
  "vite": "^6.3.5",
76
- "vitest": "^3.2.2"
76
+ "vitest": "^3.2.3"
77
77
  },
78
78
  "svelte": "./dist/index.js",
79
79
  "types": "./dist/index.d.ts",