@plastic-js/tsumiki 0.1.15 → 0.1.17

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
@@ -65,16 +65,11 @@ function Example(){
65
65
  | `className` | `string` | — | CSS class for the trigger button |
66
66
  | `children` | `node` | — | Custom trigger content (replaces default label + chevron) |
67
67
 
68
- > **Note on Trigger element:** The trigger renders `<div role="button" tabIndex={0}>` instead of a native `<button>`.
69
- > This is a workaround for a **Chrome iOS (WebKit) bug**: tapping a native `<button>` inside a scrollable container
70
- > with `-webkit-overflow-scrolling: touch` permanently pins `activeElement` to the button. After the trigger opens
71
- > the sheet, any subsequent `focus()` call on the filter `<input>` inside the list is silently ignored.
72
- > A `<div role="button">` is semantically equivalent for accessibility but avoids this WebKit focus-lock behaviour.
68
+ > **Note on Trigger element:** The trigger renders `<div role="button" tabIndex={0}>` instead of a native `<button>` as a defense-in-depth measure against a **Chrome iOS (WebKit) focus-lock bug**.
73
69
  >
74
- > Typically a native `<button>` would be preferred for keyboard tab navigation (`tabIndex` works natively).
75
- > On mobile, however, keyboard tab navigation is irrelevant — the sheet is operated via touch, and the Escape key
76
- > for closing is handled by the `Dialog`-like overlay, not by tab order. The `tabIndex={0}` on the `<div>` preserves
77
- > keyboard discoverability for assistive technology while working around the iOS focus trap.
70
+ > The primary fix is in `SelectMobile.Content`: the sheet is rendered inline (no `<Portal>`) so it stays inside the parent Dialog's focus-trap boundary. However, the `<div role="button">` is kept as an additional safeguard in WebKit, tapping a `<button>` inside a scrollable container with `-webkit-overflow-scrolling: touch` can pin `activeElement` to the button, causing subsequent `focus()` calls on the filter `<input>` to be silently ignored. A `<div role="button">` is semantically equivalent for accessibility but does not trigger this WebKit focus-lock behaviour.
71
+ >
72
+ > Typically a native `<button>` would be preferred for keyboard tab navigation (`tabIndex` works natively). On mobile, however, keyboard tab navigation is irrelevant — the sheet is operated via touch, and the Escape key for closing is handled by the `Dialog`-like overlay, not by tab order. The `tabIndex={0}` on the `<div>` preserves keyboard discoverability for assistive technology while working around the iOS focus trap.
78
73
 
79
74
  **SelectMobile.Content props:**
80
75
 
@@ -3,7 +3,9 @@ import { insert, jsx, mergeProps, setProp, template } from "@plastic-js/plastic/
3
3
  import { Loop, createComputed, createContext, createEffect, createSignal, splitProps, useContext } from "@plastic-js/plastic";
4
4
  import { collection } from "@zag-js/combobox";
5
5
  //#region src/components/FilterableSelect.jsx
6
- var _tmpl3 = template("<span></span>");
6
+ var _tmpl5 = template("<span></span>");
7
+ var _tmpl4 = template("<button type=\"button\" aria-label=\"Clear search\"></button>");
8
+ var _tmpl3 = template("<svg fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"2\" viewBox=\"0 0 24 24\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line></svg>");
7
9
  var _tmpl2 = template("<svg fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"2.5\" viewBox=\"0 0 24 24\"><polyline points=\"20 6 9 17 4 12\"></polyline></svg>");
8
10
  var _tmpl = template("<svg fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"2\" viewBox=\"0 0 24 24\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg>");
9
11
  var access = (value) => {
@@ -88,6 +90,27 @@ function ChevronIcon() {
88
90
  function CheckIcon() {
89
91
  return _tmpl2.cloneNode(true);
90
92
  }
93
+ function XIcon() {
94
+ return _tmpl3.cloneNode(true);
95
+ }
96
+ var ClearButton = (props = {}) => {
97
+ const combobox = useComboboxContext();
98
+ const handleClick = (event) => {
99
+ event.preventDefault();
100
+ event.stopPropagation();
101
+ combobox().setInputValue("");
102
+ props.onClick?.(event);
103
+ };
104
+ return () => {
105
+ const _el0 = _tmpl4.cloneNode(true);
106
+ insert(_el0, () => jsx(XIcon, {}));
107
+ setProp(_el0, "className", () => props.className);
108
+ setProp(_el0, "onClick", () => handleClick);
109
+ setProp(_el0, "onPointerDown", (e) => e.preventDefault());
110
+ setProp(_el0, "tabIndex", () => -1);
111
+ return _el0;
112
+ };
113
+ };
91
114
  var FilterableSelect = (props = {}) => {
92
115
  const [local, rest] = splitProps(props, [
93
116
  "items",
@@ -125,23 +148,29 @@ var FilterableSelect = (props = {}) => {
125
148
  get className() {
126
149
  return cn.control;
127
150
  },
128
- children: [jsx(Input, mergeProps({
129
- get className() {
130
- return cn.input;
131
- },
132
- get disabled() {
133
- return local.disabled;
134
- },
135
- get placeholder() {
136
- return local.placeholder ?? "Type to search...";
137
- }
138
- })), jsx(Combobox.Trigger, mergeProps({
139
- get className() {
140
- return cn.trigger;
141
- },
142
- type: "button",
143
- children: jsx(ChevronIcon, {})
144
- }))]
151
+ children: [
152
+ jsx(Input, mergeProps({
153
+ get className() {
154
+ return cn.input;
155
+ },
156
+ get disabled() {
157
+ return local.disabled;
158
+ },
159
+ get placeholder() {
160
+ return local.placeholder ?? "Type to search...";
161
+ }
162
+ })),
163
+ jsx(ClearButton, mergeProps({ get className() {
164
+ return cn.clear;
165
+ } })),
166
+ jsx(Combobox.Trigger, mergeProps({
167
+ get className() {
168
+ return cn.trigger;
169
+ },
170
+ type: "button",
171
+ children: jsx(ChevronIcon, {})
172
+ }))
173
+ ]
145
174
  })),
146
175
  jsx(Combobox.Content, mergeProps({
147
176
  get className() {
@@ -166,7 +195,7 @@ var FilterableSelect = (props = {}) => {
166
195
  },
167
196
  children: () => local.itemToString?.(item) ?? String(item)
168
197
  })), () => {
169
- const _el0 = _tmpl3.cloneNode(true);
198
+ const _el0 = _tmpl5.cloneNode(true);
170
199
  insert(_el0, () => jsx(CheckIcon, {}));
171
200
  setProp(_el0, "className", () => cn.check);
172
201
  return _el0;
@@ -1 +1 @@
1
- {"version":3,"file":"FilterableSelect.js","names":["Combobox","useComboboxContext","Loop","createComputed","createContext","createEffect","createSignal","splitProps","useContext","collection","createCollection","_tmpl3","_template","_tmpl2","_tmpl","access","value","FilterContext","defaultItemToValue","item","String","id","defaultItemToString","label","name","defaultFilter","query","itemToString","toLowerCase","includes","Root","props","local","rest","itemToValue","filter","rawValue","normalizedValue","handleValueChange","v","length","onValueChange","inputValue","filteredItems","q","trim","list","items","_jsx","Provider","_mergeProps","children","onInputValueChange","HighlightSingleMatch","combobox","api","open","highlightedValue","setHighlightValue","Input","onInput","event","getInputProps","onChange","Items","each","ChevronIcon","cloneNode","CheckIcon","FilterableSelect","cn","classNames","openOnClick","Label","className","Control","control","input","disabled","placeholder","Trigger","trigger","type","Content","content","Empty","empty","emptyText","Item","key","ItemText","itemText","_el0","_insert","_setProp","check"],"sources":["../../src/components/FilterableSelect.jsx"],"sourcesContent":["import { Combobox, useComboboxContext } from '@plastic-js/ark'\nimport {\n\tLoop, createComputed, createContext, createEffect, createSignal, splitProps, useContext,\n} from '@plastic-js/plastic'\nimport { collection as createCollection } from '@zag-js/combobox'\n\n// FilterableSelect — a filterable single-select built on the pure `Combobox`\n// primitive's underlying headless machine.\n//\n// This file owns two things:\n// 1. The filter plumbing. The headless component is \"controlled collection\"\n// only: it expects you to build a @zag-js collection yourself and narrow it\n// down as the user types. The internal <Root> keeps a query in sync with the\n// input, filters the list and feeds a reactive collection back to the\n// headless Root.\n// 2. The public API / contract. The default-exported <FilterableSelect> assembles\n// the full structure (label, control, input, trigger, content, items) and\n// exposes a flat prop API. It is intentionally style-agnostic: every visual\n// slot is themed through the `classNames` map, so the styling layer\n// only has to supply CSS.\n\nconst access = (value)=> {\n\treturn typeof value === 'function' ? value() : value\n}\n\n// Exposes the filtered items to <Items> without forcing the caller to thread\n// them through props.\nconst FilterContext = createContext(null)\n\nconst defaultItemToValue = item=> String(item?.value ?? item?.id ?? item)\nconst defaultItemToString = item=> String(item?.label ?? item?.name ?? item)\n\n// Substring match on the item's display string. Override via the `filter` prop\n// for fuzzy/score-based matching.\nconst defaultFilter = (item, query, itemToString)=> itemToString(item).toLowerCase().includes(query)\n\nconst Root = (props = {})=> {\n\tconst [local, rest] = splitProps(props, [\n\t\t'items', 'itemToValue', 'itemToString', 'filter', 'inputValue', 'onInputValueChange', 'children',\n\t])\n\n\tconst itemToValue = local.itemToValue ?? defaultItemToValue\n\tconst itemToString = local.itemToString ?? defaultItemToString\n\tconst filter = local.filter ?? defaultFilter\n\n\t// zag-js/combobox expects value to be an array (it calls .join(\",\")).\n\t// Single-select means a 0‑ or 1‑element array.\n\tconst rawValue = access(rest.value)\n\tconst normalizedValue = rawValue == null ? [] : [rawValue]\n\n\tconst handleValueChange = (value)=> {\n\t\tconst v = value.length >= 1 ? value[0] : null\n\t\trest.onValueChange?.(v)\n\t}\n\n\t// Internal query mirrors the input. We seed it from any `inputValue` the\n\t// caller passed so an initial/controlled value still filters correctly.\n\tconst query = createSignal(access(local.inputValue) ?? '')\n\n\t// Recomputes on typing and whenever the source `items` change. An empty\n\t// query shows everything; otherwise only the matches survive.\n\tconst filteredItems = createComputed(()=> {\n\t\tconst q = query().trim().toLowerCase()\n\t\tconst list = access(local.items) ?? []\n\t\treturn q ? list.filter(item=> filter(item, q, itemToString)) : list\n\t})\n\n\tconst collection = createComputed(()=> createCollection({\n\t\titems: filteredItems(),\n\t\titemToValue,\n\t\titemToString,\n\t}))\n\n\treturn (\n\t\t<FilterContext.Provider value={filteredItems}>\n\t\t\t<Combobox.Root\n\t\t\t\t{...rest}\n\t\t\t\tvalue={normalizedValue}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tcollection={collection}\n\t\t\t\tinputValue={query}\n\t\t\t\tonInputValueChange={(value)=> {\n\t\t\t\t\tquery(value)\n\t\t\t\t\tlocal.onInputValueChange?.(value)\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<HighlightSingleMatch itemToValue={itemToValue} items={filteredItems} />\n\t\t\t\t{local.children}\n\t\t\t</Combobox.Root>\n\t\t</FilterContext.Provider>\n\t)\n}\n\n// When the open list narrows to a single match, pre-highlight it so the user\n// can confirm with Enter/Tab without arrowing down. We nudge the machine's own\n// (uncontrolled) highlight imperatively rather than controlling `highlightedValue`\n// — controlling it would pin the highlight in place and fight the machine, e.g.\n// blocking the highlight-clear on the next keystroke and the select-and-close on\n// Enter. We only set it; the machine still clears/moves it on input and navigation.\nconst HighlightSingleMatch = (props = {})=> {\n\tconst combobox = useComboboxContext()\n\tcreateEffect(()=> {\n\t\tconst api = combobox()\n\t\tconst list = props.items()\n\t\tif (!api.open || list.length !== 1){ return }\n\t\tconst value = props.itemToValue(list[0])\n\t\tif (api.highlightedValue !== value){ api.setHighlightValue(value) }\n\t})\n\treturn null\n}\n\n// The headless input only fires zag's INPUT.CHANGE on the native `change`\n// event — which, unlike React's onChange, fires on blur, not per keystroke. So\n// typing never reached the machine and the list never filtered. We bridge the\n// per-keystroke `input` event to zag's own onChange handler so every character\n// drives the same INPUT.CHANGE (open + filter) behaviour.\nconst Input = (props = {})=> {\n\tconst combobox = useComboboxContext()\n\treturn (\n\t\t<Combobox.Input\n\t\t\t{...props}\n\t\t\tonInput={event=> combobox().getInputProps().onChange?.(event)}\n\t\t/>\n\t)\n}\n\n// Render-prop over the filtered items. Place inside <Combobox.Content> and\n// return a <Combobox.Item> for each match. Backed by <Loop> so the list stays\n// reactive — a bare thunk returned from a component is unwrapped once and would\n// never re-filter.\nconst Items = (props = {})=> {\n\tconst items = useContext(FilterContext)\n\treturn (\n\t\t<Loop each={items ?? []}>\n\t\t\t{item=> props.children?.(item)}\n\t\t</Loop>\n\t)\n}\n\nfunction ChevronIcon(){\n\treturn (\n\t\t<svg\n\t\t\tfill='none' stroke='currentColor' strokeLinecap='round' strokeLinejoin='round'\n\t\t\tstrokeWidth='2' viewBox='0 0 24 24'\n\t\t>\n\t\t\t<polyline points='6 9 12 15 18 9' />\n\t\t</svg>\n\t)\n}\n\nfunction CheckIcon(){\n\treturn (\n\t\t<svg\n\t\t\tfill='none' stroke='currentColor' strokeLinecap='round' strokeLinejoin='round'\n\t\t\tstrokeWidth='2.5' viewBox='0 0 24 24'\n\t\t>\n\t\t\t<polyline points='20 6 9 17 4 12' />\n\t\t</svg>\n\t)\n}\n\n// Public component. `classNames` themes each slot (control/input/trigger/content/\n// empty/item/itemText/check/label); anything not split here (e.g. `filter`,\n// `name`) flows through to the filter Root.\nconst FilterableSelect = (props = {})=> {\n\tconst [local, rest] = splitProps(props, [\n\t\t'items',\n\t\t'itemToString',\n\t\t'itemToValue',\n\t\t'placeholder',\n\t\t'label',\n\t\t'openOnClick',\n\t\t'disabled',\n\t\t'classNames',\n\t\t'emptyText',\n\t])\n\n\tconst cn = local.classNames ?? {}\n\n\t// `value`, `onValueChange`, `filter`, `name`, … are not transformed here, so\n\t// they ride `rest` straight through to the underlying machine.\n\treturn (\n\t\t<Root\n\t\t\titemToString={local.itemToString}\n\t\t\titemToValue={local.itemToValue}\n\t\t\titems={local.items}\n\t\t\topenOnClick={local.openOnClick ?? true}\n\t\t\t{...rest}\n\t\t>\n\t\t\t{local.label && (\n\t\t\t\t<Combobox.Label className={cn.label}>\n\t\t\t\t\t{local.label}\n\t\t\t\t</Combobox.Label>\n\t\t\t)}\n\t\t\t<Combobox.Control className={cn.control}>\n\t\t\t\t<Input\n\t\t\t\t\tclassName={cn.input}\n\t\t\t\t\tdisabled={local.disabled}\n\t\t\t\t\tplaceholder={local.placeholder ?? 'Type to search...'}\n\t\t\t\t/>\n\t\t\t\t<Combobox.Trigger className={cn.trigger} type='button'>\n\t\t\t\t\t<ChevronIcon />\n\t\t\t\t</Combobox.Trigger>\n\t\t\t</Combobox.Control>\n\t\t\t<Combobox.Content className={cn.content}>\n\t\t\t\t<Combobox.Empty className={cn.empty}>\n\t\t\t\t\t{local.emptyText ?? 'No results found'}\n\t\t\t\t</Combobox.Empty>\n\t\t\t\t<Items>\n\t\t\t\t\t{item=> (\n\t\t\t\t\t\t<Combobox.Item className={cn.item} item={item} key={local.itemToValue?.(item) ?? item}>\n\t\t\t\t\t\t\t<Combobox.ItemText className={cn.itemText}>\n\t\t\t\t\t\t\t\t{local.itemToString?.(item) ?? String(item)}\n\t\t\t\t\t\t\t</Combobox.ItemText>\n\t\t\t\t\t\t\t<span className={cn.check}>\n\t\t\t\t\t\t\t\t<CheckIcon />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</Combobox.Item>\n\t\t\t\t\t)}\n\t\t\t\t</Items>\n\t\t\t</Combobox.Content>\n\t\t</Root>\n\t)\n}\n\nexport default FilterableSelect\n"],"mappings":"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"}
1
+ {"version":3,"file":"FilterableSelect.js","names":["Combobox","useComboboxContext","Loop","createComputed","createContext","createEffect","createSignal","splitProps","useContext","collection","createCollection","_tmpl5","_template","_tmpl4","_tmpl3","_tmpl2","_tmpl","access","value","FilterContext","defaultItemToValue","item","String","id","defaultItemToString","label","name","defaultFilter","query","itemToString","toLowerCase","includes","Root","props","local","rest","itemToValue","filter","rawValue","normalizedValue","handleValueChange","v","length","onValueChange","inputValue","filteredItems","q","trim","list","items","_jsx","Provider","_mergeProps","children","onInputValueChange","HighlightSingleMatch","combobox","api","open","highlightedValue","setHighlightValue","Input","onInput","event","getInputProps","onChange","Items","each","ChevronIcon","cloneNode","CheckIcon","XIcon","ClearButton","handleClick","preventDefault","stopPropagation","setInputValue","onClick","_el0","_insert","_setProp","className","e","FilterableSelect","cn","classNames","openOnClick","Label","Control","control","input","disabled","placeholder","clear","Trigger","trigger","type","Content","content","Empty","empty","emptyText","Item","key","ItemText","itemText","check"],"sources":["../../src/components/FilterableSelect.jsx"],"sourcesContent":["import { Combobox, useComboboxContext } from '@plastic-js/ark'\nimport {\n\tLoop, createComputed, createContext, createEffect, createSignal, splitProps, useContext,\n} from '@plastic-js/plastic'\nimport { collection as createCollection } from '@zag-js/combobox'\n\n// FilterableSelect — a filterable single-select built on the pure `Combobox`\n// primitive's underlying headless machine.\n//\n// This file owns two things:\n// 1. The filter plumbing. The headless component is \"controlled collection\"\n// only: it expects you to build a @zag-js collection yourself and narrow it\n// down as the user types. The internal <Root> keeps a query in sync with the\n// input, filters the list and feeds a reactive collection back to the\n// headless Root.\n// 2. The public API / contract. The default-exported <FilterableSelect> assembles\n// the full structure (label, control, input, trigger, content, items) and\n// exposes a flat prop API. It is intentionally style-agnostic: every visual\n// slot is themed through the `classNames` map, so the styling layer\n// only has to supply CSS.\n\nconst access = (value)=> {\n\treturn typeof value === 'function' ? value() : value\n}\n\n// Exposes the filtered items to <Items> without forcing the caller to thread\n// them through props.\nconst FilterContext = createContext(null)\n\nconst defaultItemToValue = item=> String(item?.value ?? item?.id ?? item)\nconst defaultItemToString = item=> String(item?.label ?? item?.name ?? item)\n\n// Substring match on the item's display string. Override via the `filter` prop\n// for fuzzy/score-based matching.\nconst defaultFilter = (item, query, itemToString)=> itemToString(item).toLowerCase().includes(query)\n\nconst Root = (props = {})=> {\n\tconst [local, rest] = splitProps(props, [\n\t\t'items', 'itemToValue', 'itemToString', 'filter', 'inputValue', 'onInputValueChange', 'children',\n\t])\n\n\tconst itemToValue = local.itemToValue ?? defaultItemToValue\n\tconst itemToString = local.itemToString ?? defaultItemToString\n\tconst filter = local.filter ?? defaultFilter\n\n\t// zag-js/combobox expects value to be an array (it calls .join(\",\")).\n\t// Single-select means a 0‑ or 1‑element array.\n\tconst rawValue = access(rest.value)\n\tconst normalizedValue = rawValue == null ? [] : [rawValue]\n\n\tconst handleValueChange = (value)=> {\n\t\tconst v = value.length >= 1 ? value[0] : null\n\t\trest.onValueChange?.(v)\n\t}\n\n\t// Internal query mirrors the input. We seed it from any `inputValue` the\n\t// caller passed so an initial/controlled value still filters correctly.\n\tconst query = createSignal(access(local.inputValue) ?? '')\n\n\t// Recomputes on typing and whenever the source `items` change. An empty\n\t// query shows everything; otherwise only the matches survive.\n\tconst filteredItems = createComputed(()=> {\n\t\tconst q = query().trim().toLowerCase()\n\t\tconst list = access(local.items) ?? []\n\t\treturn q ? list.filter(item=> filter(item, q, itemToString)) : list\n\t})\n\n\tconst collection = createComputed(()=> createCollection({\n\t\titems: filteredItems(),\n\t\titemToValue,\n\t\titemToString,\n\t}))\n\n\treturn (\n\t\t<FilterContext.Provider value={filteredItems}>\n\t\t\t<Combobox.Root\n\t\t\t\t{...rest}\n\t\t\t\tvalue={normalizedValue}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tcollection={collection}\n\t\t\t\tinputValue={query}\n\t\t\t\tonInputValueChange={(value)=> {\n\t\t\t\t\tquery(value)\n\t\t\t\t\tlocal.onInputValueChange?.(value)\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<HighlightSingleMatch itemToValue={itemToValue} items={filteredItems} />\n\t\t\t\t{local.children}\n\t\t\t</Combobox.Root>\n\t\t</FilterContext.Provider>\n\t)\n}\n\n// When the open list narrows to a single match, pre-highlight it so the user\n// can confirm with Enter/Tab without arrowing down. We nudge the machine's own\n// (uncontrolled) highlight imperatively rather than controlling `highlightedValue`\n// — controlling it would pin the highlight in place and fight the machine, e.g.\n// blocking the highlight-clear on the next keystroke and the select-and-close on\n// Enter. We only set it; the machine still clears/moves it on input and navigation.\nconst HighlightSingleMatch = (props = {})=> {\n\tconst combobox = useComboboxContext()\n\tcreateEffect(()=> {\n\t\tconst api = combobox()\n\t\tconst list = props.items()\n\t\tif (!api.open || list.length !== 1){ return }\n\t\tconst value = props.itemToValue(list[0])\n\t\tif (api.highlightedValue !== value){ api.setHighlightValue(value) }\n\t})\n\treturn null\n}\n\n// The headless input only fires zag's INPUT.CHANGE on the native `change`\n// event — which, unlike React's onChange, fires on blur, not per keystroke. So\n// typing never reached the machine and the list never filtered. We bridge the\n// per-keystroke `input` event to zag's own onChange handler so every character\n// drives the same INPUT.CHANGE (open + filter) behaviour.\nconst Input = (props = {})=> {\n\tconst combobox = useComboboxContext()\n\treturn (\n\t\t<Combobox.Input\n\t\t\t{...props}\n\t\t\tonInput={event=> combobox().getInputProps().onChange?.(event)}\n\t\t/>\n\t)\n}\n\n// Render-prop over the filtered items. Place inside <Combobox.Content> and\n// return a <Combobox.Item> for each match. Backed by <Loop> so the list stays\n// reactive — a bare thunk returned from a component is unwrapped once and would\n// never re-filter.\nconst Items = (props = {})=> {\n\tconst items = useContext(FilterContext)\n\treturn (\n\t\t<Loop each={items ?? []}>\n\t\t\t{item=> props.children?.(item)}\n\t\t</Loop>\n\t)\n}\n\nfunction ChevronIcon(){\n\treturn (\n\t\t<svg\n\t\t\tfill='none' stroke='currentColor' strokeLinecap='round' strokeLinejoin='round'\n\t\t\tstrokeWidth='2' viewBox='0 0 24 24'\n\t\t>\n\t\t\t<polyline points='6 9 12 15 18 9' />\n\t\t</svg>\n\t)\n}\n\nfunction CheckIcon(){\n\treturn (\n\t\t<svg\n\t\t\tfill='none' stroke='currentColor' strokeLinecap='round' strokeLinejoin='round'\n\t\t\tstrokeWidth='2.5' viewBox='0 0 24 24'\n\t\t>\n\t\t\t<polyline points='20 6 9 17 4 12' />\n\t\t</svg>\n\t)\n}\n\nfunction XIcon(){\n\treturn (\n\t\t<svg\n\t\t\tfill='none' stroke='currentColor' strokeLinecap='round' strokeLinejoin='round'\n\t\t\tstrokeWidth='2' viewBox='0 0 24 24'\n\t\t>\n\t\t\t<line x1='18' y1='6' x2='6' y2='18' />\n\t\t\t<line x1='6' y1='6' x2='18' y2='18' />\n\t\t</svg>\n\t)\n}\n\n// Clears the filter query (not the selected value). Sits inside the control\n// next to the trigger chevron so the user can reset their search without\n// reaching for Backspace.\nconst ClearButton = (props = {})=> {\n\tconst combobox = useComboboxContext()\n\tconst handleClick = (event)=> {\n\t\tevent.preventDefault()\n\t\tevent.stopPropagation()\n\t\tcombobox().setInputValue('')\n\t\tprops.onClick?.(event)\n\t}\n\treturn (\n\t\t<button\n\t\t\ttype='button'\n\t\t\tclassName={props.className}\n\t\t\tonClick={handleClick}\n\t\t\tonPointerDown={e=> e.preventDefault()}\n\t\t\ttabIndex={-1}\n\t\t\taria-label='Clear search'\n\t\t>\n\t\t\t<XIcon />\n\t\t</button>\n\t)\n}\n\n// Public component. `classNames` themes each slot (control/input/trigger/content/\n// empty/item/itemText/check/label); anything not split here (e.g. `filter`,\n// `name`) flows through to the filter Root.\nconst FilterableSelect = (props = {})=> {\n\tconst [local, rest] = splitProps(props, [\n\t\t'items',\n\t\t'itemToString',\n\t\t'itemToValue',\n\t\t'placeholder',\n\t\t'label',\n\t\t'openOnClick',\n\t\t'disabled',\n\t\t'classNames',\n\t\t'emptyText',\n\t])\n\n\tconst cn = local.classNames ?? {}\n\n\t// `value`, `onValueChange`, `filter`, `name`, … are not transformed here, so\n\t// they ride `rest` straight through to the underlying machine.\n\treturn (\n\t\t<Root\n\t\t\titemToString={local.itemToString}\n\t\t\titemToValue={local.itemToValue}\n\t\t\titems={local.items}\n\t\t\topenOnClick={local.openOnClick ?? true}\n\t\t\t{...rest}\n\t\t>\n\t\t\t{local.label && (\n\t\t\t\t<Combobox.Label className={cn.label}>\n\t\t\t\t\t{local.label}\n\t\t\t\t</Combobox.Label>\n\t\t\t)}\n\t\t\t<Combobox.Control className={cn.control}>\n\t\t\t\t<Input\n\t\t\t\t\tclassName={cn.input}\n\t\t\t\t\tdisabled={local.disabled}\n\t\t\t\t\tplaceholder={local.placeholder ?? 'Type to search...'}\n\t\t\t\t/>\n\t\t\t\t<ClearButton className={cn.clear} />\n\t\t\t\t<Combobox.Trigger className={cn.trigger} type='button'>\n\t\t\t\t\t<ChevronIcon />\n\t\t\t\t</Combobox.Trigger>\n\t\t\t</Combobox.Control>\n\t\t\t<Combobox.Content className={cn.content}>\n\t\t\t\t<Combobox.Empty className={cn.empty}>\n\t\t\t\t\t{local.emptyText ?? 'No results found'}\n\t\t\t\t</Combobox.Empty>\n\t\t\t\t<Items>\n\t\t\t\t\t{item=> (\n\t\t\t\t\t\t<Combobox.Item className={cn.item} item={item} key={local.itemToValue?.(item) ?? item}>\n\t\t\t\t\t\t\t<Combobox.ItemText className={cn.itemText}>\n\t\t\t\t\t\t\t\t{local.itemToString?.(item) ?? String(item)}\n\t\t\t\t\t\t\t</Combobox.ItemText>\n\t\t\t\t\t\t\t<span className={cn.check}>\n\t\t\t\t\t\t\t\t<CheckIcon />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</Combobox.Item>\n\t\t\t\t\t)}\n\t\t\t\t</Items>\n\t\t\t</Combobox.Content>\n\t\t</Root>\n\t)\n}\n\nexport default FilterableSelect\n"],"mappings":"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"}
@@ -1,15 +1,19 @@
1
1
  import SelectMobile from "./SelectMobile/index.js";
2
- import { jsx, mergeProps, setProp, template } from "@plastic-js/plastic/jsx-runtime";
2
+ import { insert, jsx, mergeProps, setProp, template } from "@plastic-js/plastic/jsx-runtime";
3
3
  import { css } from "@emotion/css";
4
4
  import { createSignal, splitProps } from "@plastic-js/plastic";
5
5
  //#region src/components/FilterableSelectMobile.jsx
6
- var _tmpl2 = template("<div>No results</div>");
7
- var _tmpl = template("<input type=\"text\" placeholder=\"Filter…\">");
6
+ var _tmpl3 = template("<div>No results</div>");
7
+ var _tmpl2 = template("<div><input type=\"text\" placeholder=\"Filter…\"><button type=\"button\" aria-label=\"Clear search\"></button></div>");
8
+ var _tmpl = template("<svg fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"2\" viewBox=\"0 0 24 24\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line></svg>");
9
+ var inputWrapperClass = css({
10
+ position: "relative",
11
+ marginBottom: "6px"
12
+ });
8
13
  var inputClass = css({
9
14
  width: "100%",
10
15
  height: "42px",
11
- padding: "0 14px",
12
- marginBottom: "6px",
16
+ padding: "0 36px 0 14px",
13
17
  background: "rgba(255,255,255,0.06)",
14
18
  border: "1px solid var(--border)",
15
19
  borderRadius: "10px",
@@ -21,6 +25,31 @@ var inputClass = css({
21
25
  "&:focus": { borderColor: "var(--accent)" },
22
26
  "&::placeholder": { color: "var(--muted)" }
23
27
  });
28
+ var clearButtonClass = css({
29
+ position: "absolute",
30
+ right: "8px",
31
+ top: "50%",
32
+ transform: "translateY(-50%)",
33
+ width: "24px",
34
+ height: "24px",
35
+ padding: "4px",
36
+ display: "flex",
37
+ alignItems: "center",
38
+ justifyContent: "center",
39
+ background: "none",
40
+ border: "none",
41
+ borderRadius: "4px",
42
+ color: "var(--muted)",
43
+ cursor: "pointer",
44
+ "&:hover": { color: "var(--ink)" },
45
+ "& svg": {
46
+ width: "100%",
47
+ height: "100%"
48
+ }
49
+ });
50
+ function XIcon() {
51
+ return _tmpl.cloneNode(true);
52
+ }
24
53
  var noResultsClass = css({
25
54
  padding: "20px 14px",
26
55
  textAlign: "center",
@@ -86,23 +115,35 @@ var FilterableSelectMobile = (props) => {
86
115
  get backdropStyle() {
87
116
  return local.backdropStyle;
88
117
  },
89
- children: [(() => {
90
- const _el0 = _tmpl.cloneNode(true);
91
- setProp(_el0, "ref", (el) => {
118
+ children: [() => {
119
+ const _el0 = _tmpl2.cloneNode(true);
120
+ const _el1 = _el0.firstChild;
121
+ const _el2 = _el0.firstChild.nextSibling;
122
+ setProp(_el1, "ref", (el) => {
92
123
  inputEl = el;
93
124
  });
94
- setProp(_el0, "className", () => inputClass);
95
- setProp(_el0, "value", () => query);
96
- setProp(_el0, "onInput", (e) => query(e.target.value));
97
- setProp(_el0, "onClick", (e) => e.stopPropagation());
98
- setProp(_el0, "onPointerDown", (e) => {
125
+ setProp(_el1, "className", () => inputClass);
126
+ setProp(_el1, "value", () => query);
127
+ setProp(_el1, "onInput", (e) => query(e.target.value));
128
+ setProp(_el1, "onClick", (e) => e.stopPropagation());
129
+ setProp(_el1, "onPointerDown", (e) => {
99
130
  e.target.focus({ preventScroll: true });
100
131
  });
132
+ insert(_el2, () => jsx(XIcon, {}));
133
+ setProp(_el2, "className", () => clearButtonClass);
134
+ setProp(_el2, "onClick", (e) => {
135
+ e.stopPropagation();
136
+ query("");
137
+ inputEl?.focus();
138
+ });
139
+ setProp(_el2, "onPointerDown", (e) => e.preventDefault());
140
+ setProp(_el2, "tabIndex", () => -1);
141
+ setProp(_el0, "className", () => inputWrapperClass);
101
142
  return _el0;
102
- })(), () => {
143
+ }, () => {
103
144
  const list = filteredItems();
104
145
  if (list.length === 0) return (() => {
105
- const _el0 = _tmpl2.cloneNode(true);
146
+ const _el0 = _tmpl3.cloneNode(true);
106
147
  setProp(_el0, "className", () => noResultsClass);
107
148
  return _el0;
108
149
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"FilterableSelectMobile.js","names":["css","createSignal","splitProps","SelectMobile","_tmpl2","_template","_tmpl","inputClass","width","height","padding","marginBottom","background","border","borderRadius","color","fontSize","fontFamily","outline","boxSizing","borderColor","noResultsClass","textAlign","defaultItemToValue","item","value","defaultItemToLabel","label","String","defaultFilter","query","itemToLabel","toLowerCase","includes","FilterableSelectMobile","props","local","itemToValue","filter","inputEl","filteredItems","q","trim","list","items","handleOpenChange","isOpen","queueMicrotask","focus","_jsx","Root","_mergeProps","onValueChange","onOpenChange","children","Trigger","placeholder","Content","clearable","clearLabel","backdropClassName","backdropStyle","_el0","cloneNode","_setProp","el","e","target","stopPropagation","preventScroll","length","map","Item","key"],"sources":["../../src/components/FilterableSelectMobile.jsx"],"sourcesContent":["import { css } from '@emotion/css'\nimport { createSignal, splitProps } from '@plastic-js/plastic'\nimport { SelectMobile } from './SelectMobile/index.jsx'\n\n// ── Styles ───────────────────────────────────────────────────────────────\nconst inputClass = css({\n\twidth: '100%',\n\theight: '42px',\n\tpadding: '0 14px',\n\tmarginBottom: '6px',\n\tbackground: 'rgba(255,255,255,0.06)',\n\tborder: '1px solid var(--border)',\n\tborderRadius: '10px',\n\tcolor: 'var(--ink)',\n\tfontSize: '16px',\n\tfontFamily: 'inherit',\n\toutline: 'none',\n\tboxSizing: 'border-box',\n\t'&:focus': { borderColor: 'var(--accent)' },\n\t'&::placeholder': { color: 'var(--muted)' },\n})\n\nconst noResultsClass = css({\n\tpadding: '20px 14px',\n\ttextAlign: 'center',\n\tcolor: 'var(--muted)',\n\tfontSize: '14px',\n})\n\n// ── Default accessors ────────────────────────────────────────────────────\nconst defaultItemToValue = item=> item?.value ?? item\nconst defaultItemToLabel = item=> item?.label ?? String(item)\nconst defaultFilter = (item, query, itemToLabel)=> itemToLabel(item).toLowerCase().includes(query)\n\n// ── FilterableSelectMobile ───────────────────────────────────────────────\n//\n// A mobile bottom-sheet select with a filter input at the top of the list.\n// Wraps SelectMobile internally — exposes a flat single-component API.\n//\n// Props:\n// items data array (or getter)\n// value current value (string | getter)\n// onValueChange (value) => void\n// itemToValue (item) => string — default item.value ?? item\n// itemToLabel (item) => string — default item.label ?? String(item)\n// placeholder trigger placeholder text — default \"Select\"\n// filter (item, query, itemToLabel) => boolean — default substring match\n// clearable show a \"clear\" row\n// clearLabel label for the clear row\n// backdropClassName className for the backdrop\n// backdropStyle inline style for the backdrop\nconst FilterableSelectMobile = (props)=> {\n\tconst [local] = splitProps(props, [\n\t\t'items', 'value', 'onValueChange',\n\t\t'itemToValue', 'itemToLabel',\n\t\t'placeholder', 'filter',\n\t\t'clearable', 'clearLabel',\n\t\t'backdropClassName', 'backdropStyle',\n\t])\n\n\tconst itemToValue = local.itemToValue ?? defaultItemToValue\n\tconst itemToLabel = local.itemToLabel ?? defaultItemToLabel\n\tconst filter = local.filter ?? defaultFilter\n\n\tconst query = createSignal('')\n\tlet inputEl = null\n\n\t// Reactive filter — recomputes on typing and items changes.\n\tconst filteredItems = ()=> {\n\t\tconst q = query().trim().toLowerCase()\n\t\tconst list = typeof local.items === 'function' ? local.items() : (local.items ?? [])\n\t\treturn q ? list.filter(item=> filter(item, q, itemToLabel)) : list\n\t}\n\n\t// Auto-focus the input when the sheet opens; clear query on close.\n\tconst handleOpenChange = (isOpen)=> {\n\t\tif (isOpen){\n\t\t\tqueueMicrotask(()=> inputEl?.focus())\n\t\t} else {\n\t\t\tquery('')\n\t\t}\n\t}\n\n\treturn (\n\t\t<SelectMobile.Root\n\t\t\tvalue={local.value}\n\t\t\tonValueChange={local.onValueChange}\n\t\t\titems={local.items}\n\t\t\titemToValue={itemToValue}\n\t\t\titemToLabel={itemToLabel}\n\t\t\tonOpenChange={handleOpenChange}\n\t\t>\n\t\t\t<SelectMobile.Trigger placeholder={local.placeholder} />\n\t\t\t<SelectMobile.Content\n\t\t\t\tclearable={local.clearable}\n\t\t\t\tclearLabel={local.clearLabel}\n\t\t\t\tbackdropClassName={local.backdropClassName}\n\t\t\t\tbackdropStyle={local.backdropStyle}\n\t\t\t>\n\t\t\t\t<input\n\t\t\t\t\tref={el=> { inputEl = el }}\n\t\t\t\t\ttype='text'\n\t\t\t\t\tclassName={inputClass}\n\t\t\t\t\tplaceholder='Filter…'\n\t\t\t\t\tvalue={query}\n\t\t\t\t\tonInput={e=> query(e.target.value)}\n\t\t\t\t\tonClick={e=> e.stopPropagation()}\n\t\t\t\t\tonPointerDown={e=> {\n\t\t\t\t\t\t// iOS WebKit workaround: ensure focus moves to input even when\n\t\t\t\t\t\t// a previous button element holds focus (Chrome iOS bug).\n\t\t\t\t\t\te.target.focus({ preventScroll: true })\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t\t{()=> {\n\t\t\t\t\tconst list = filteredItems()\n\t\t\t\t\tif (list.length === 0){\n\t\t\t\t\t\treturn <div className={noResultsClass}>No results</div>\n\t\t\t\t\t}\n\t\t\t\t\treturn list.map(item => (\n\t\t\t\t\t\t<SelectMobile.Item item={item} key={itemToValue(item)} />\n\t\t\t\t\t))\n\t\t\t\t}}\n\t\t\t</SelectMobile.Content>\n\t\t</SelectMobile.Root>\n\t)\n}\n\nexport default FilterableSelectMobile"],"mappings":"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"}
1
+ {"version":3,"file":"FilterableSelectMobile.js","names":["css","createSignal","splitProps","SelectMobile","_tmpl3","_template","_tmpl2","_tmpl","inputWrapperClass","position","marginBottom","inputClass","width","height","padding","background","border","borderRadius","color","fontSize","fontFamily","outline","boxSizing","borderColor","clearButtonClass","right","top","transform","display","alignItems","justifyContent","cursor","XIcon","cloneNode","noResultsClass","textAlign","defaultItemToValue","item","value","defaultItemToLabel","label","String","defaultFilter","query","itemToLabel","toLowerCase","includes","FilterableSelectMobile","props","local","itemToValue","filter","inputEl","filteredItems","q","trim","list","items","handleOpenChange","isOpen","queueMicrotask","focus","_jsx","Root","_mergeProps","onValueChange","onOpenChange","children","Trigger","placeholder","Content","clearable","clearLabel","backdropClassName","backdropStyle","_el0","_el1","firstChild","_el2","nextSibling","_setProp","el","e","target","stopPropagation","preventScroll","_insert","preventDefault","length","map","Item","key"],"sources":["../../src/components/FilterableSelectMobile.jsx"],"sourcesContent":["import { css } from '@emotion/css'\nimport { createSignal, splitProps } from '@plastic-js/plastic'\nimport { SelectMobile } from './SelectMobile/index.jsx'\n\n// ── Styles ───────────────────────────────────────────────────────────────\nconst inputWrapperClass = css({\n\tposition: 'relative',\n\tmarginBottom: '6px',\n})\n\nconst inputClass = css({\n\twidth: '100%',\n\theight: '42px',\n\tpadding: '0 36px 0 14px',\n\tbackground: 'rgba(255,255,255,0.06)',\n\tborder: '1px solid var(--border)',\n\tborderRadius: '10px',\n\tcolor: 'var(--ink)',\n\tfontSize: '16px',\n\tfontFamily: 'inherit',\n\toutline: 'none',\n\tboxSizing: 'border-box',\n\t'&:focus': { borderColor: 'var(--accent)' },\n\t'&::placeholder': { color: 'var(--muted)' },\n})\n\nconst clearButtonClass = css({\n\tposition: 'absolute',\n\tright: '8px',\n\ttop: '50%',\n\ttransform: 'translateY(-50%)',\n\twidth: '24px',\n\theight: '24px',\n\tpadding: '4px',\n\tdisplay: 'flex',\n\talignItems: 'center',\n\tjustifyContent: 'center',\n\tbackground: 'none',\n\tborder: 'none',\n\tborderRadius: '4px',\n\tcolor: 'var(--muted)',\n\tcursor: 'pointer',\n\t'&:hover': { color: 'var(--ink)' },\n\t'& svg': { width: '100%', height: '100%' },\n})\n\nfunction XIcon(){\n\treturn (\n\t\t<svg\n\t\t\tfill='none' stroke='currentColor' strokeLinecap='round' strokeLinejoin='round'\n\t\t\tstrokeWidth='2' viewBox='0 0 24 24'\n\t\t>\n\t\t\t<line x1='18' y1='6' x2='6' y2='18' />\n\t\t\t<line x1='6' y1='6' x2='18' y2='18' />\n\t\t</svg>\n\t)\n}\n\nconst noResultsClass = css({\n\tpadding: '20px 14px',\n\ttextAlign: 'center',\n\tcolor: 'var(--muted)',\n\tfontSize: '14px',\n})\n\n// ── Default accessors ────────────────────────────────────────────────────\nconst defaultItemToValue = item=> item?.value ?? item\nconst defaultItemToLabel = item=> item?.label ?? String(item)\nconst defaultFilter = (item, query, itemToLabel)=> itemToLabel(item).toLowerCase().includes(query)\n\n// ── FilterableSelectMobile ───────────────────────────────────────────────\n//\n// A mobile bottom-sheet select with a filter input at the top of the list.\n// Wraps SelectMobile internally — exposes a flat single-component API.\n//\n// Props:\n// items data array (or getter)\n// value current value (string | getter)\n// onValueChange (value) => void\n// itemToValue (item) => string — default item.value ?? item\n// itemToLabel (item) => string — default item.label ?? String(item)\n// placeholder trigger placeholder text — default \"Select\"\n// filter (item, query, itemToLabel) => boolean — default substring match\n// clearable show a \"clear\" row\n// clearLabel label for the clear row\n// backdropClassName className for the backdrop\n// backdropStyle inline style for the backdrop\nconst FilterableSelectMobile = (props)=> {\n\tconst [local] = splitProps(props, [\n\t\t'items', 'value', 'onValueChange',\n\t\t'itemToValue', 'itemToLabel',\n\t\t'placeholder', 'filter',\n\t\t'clearable', 'clearLabel',\n\t\t'backdropClassName', 'backdropStyle',\n\t])\n\n\tconst itemToValue = local.itemToValue ?? defaultItemToValue\n\tconst itemToLabel = local.itemToLabel ?? defaultItemToLabel\n\tconst filter = local.filter ?? defaultFilter\n\n\tconst query = createSignal('')\n\tlet inputEl = null\n\n\t// Reactive filter — recomputes on typing and items changes.\n\tconst filteredItems = ()=> {\n\t\tconst q = query().trim().toLowerCase()\n\t\tconst list = typeof local.items === 'function' ? local.items() : (local.items ?? [])\n\t\treturn q ? list.filter(item=> filter(item, q, itemToLabel)) : list\n\t}\n\n\t// Auto-focus the input when the sheet opens; clear query on close.\n\tconst handleOpenChange = (isOpen)=> {\n\t\tif (isOpen){\n\t\t\tqueueMicrotask(()=> inputEl?.focus())\n\t\t} else {\n\t\t\tquery('')\n\t\t}\n\t}\n\n\treturn (\n\t\t<SelectMobile.Root\n\t\t\tvalue={local.value}\n\t\t\tonValueChange={local.onValueChange}\n\t\t\titems={local.items}\n\t\t\titemToValue={itemToValue}\n\t\t\titemToLabel={itemToLabel}\n\t\t\tonOpenChange={handleOpenChange}\n\t\t>\n\t\t\t<SelectMobile.Trigger placeholder={local.placeholder} />\n\t\t\t<SelectMobile.Content\n\t\t\t\tclearable={local.clearable}\n\t\t\t\tclearLabel={local.clearLabel}\n\t\t\t\tbackdropClassName={local.backdropClassName}\n\t\t\t\tbackdropStyle={local.backdropStyle}\n\t\t\t>\n\t\t\t\t<div className={inputWrapperClass}>\n\t\t\t\t\t<input\n\t\t\t\t\t\tref={el=> { inputEl = el }}\n\t\t\t\t\t\ttype='text'\n\t\t\t\t\t\tclassName={inputClass}\n\t\t\t\t\t\tplaceholder='Filter…'\n\t\t\t\t\t\tvalue={query}\n\t\t\t\t\t\tonInput={e=> query(e.target.value)}\n\t\t\t\t\t\tonClick={e=> e.stopPropagation()}\n\t\t\t\t\t\tonPointerDown={e=> {\n\t\t\t\t\t\t\t// Defense-in-depth for Chrome iOS (WebKit) focus-lock bug.\n\t\t\t\t\t\t\t// The primary fix is in SelectMobile.Content (no <Portal>).\n\t\t\t\t\t\t\t// This handler ensures focus moves to the input even if a\n\t\t\t\t\t\t\t// previous button element still holds focus.\n\t\t\t\t\t\t\te.target.focus({ preventScroll: true })\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\tclassName={clearButtonClass}\n\t\t\t\t\t\tonClick={e=> {\n\t\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\t\tquery('')\n\t\t\t\t\t\t\tinputEl?.focus()\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonPointerDown={e=> e.preventDefault()}\n\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\taria-label='Clear search'\n\t\t\t\t\t>\n\t\t\t\t\t\t<XIcon />\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t\t{()=> {\n\t\t\t\t\tconst list = filteredItems()\n\t\t\t\t\tif (list.length === 0){\n\t\t\t\t\t\treturn <div className={noResultsClass}>No results</div>\n\t\t\t\t\t}\n\t\t\t\t\treturn list.map(item => (\n\t\t\t\t\t\t<SelectMobile.Item item={item} key={itemToValue(item)} />\n\t\t\t\t\t))\n\t\t\t\t}}\n\t\t\t</SelectMobile.Content>\n\t\t</SelectMobile.Root>\n\t)\n}\n\nexport default FilterableSelectMobile"],"mappings":"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"}
@@ -2,7 +2,7 @@ import Icon from "../Icon.js";
2
2
  import { part } from "./anatomy.js";
3
3
  import { Fragment, jsx, mergeProps, setProp, template } from "@plastic-js/plastic/jsx-runtime";
4
4
  import { css } from "@emotion/css";
5
- import { Loop, Portal, createContext, createEffect, createSignal, splitProps, useContext } from "@plastic-js/plastic";
5
+ import { Loop, createContext, createEffect, createSignal, splitProps, useContext } from "@plastic-js/plastic";
6
6
  //#region src/components/SelectMobile/index.jsx
7
7
  var _tmpl = template("<span></span>");
8
8
  var chevronDownSvg = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>";
@@ -253,7 +253,7 @@ var Content = (props) => {
253
253
  "backdropStyle",
254
254
  "children"
255
255
  ]);
256
- return jsx(Portal, mergeProps({ children: jsx("div", mergeProps(() => part("positioner"), {
256
+ return jsx("div", mergeProps(() => part("positioner"), {
257
257
  "aria-hidden": () => !ctx.open(),
258
258
  get className() {
259
259
  return `${overlayClass} ${ctx.open() ? openClass : ""}`;
@@ -296,7 +296,7 @@ var Content = (props) => {
296
296
  }))
297
297
  }))] })
298
298
  }))] }))]
299
- })) }));
299
+ }));
300
300
  };
301
301
  var SelectMobile = Object.assign(Root, {
302
302
  Root,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["css","Loop","Portal","createContext","createEffect","createSignal","splitProps","useContext","Icon","part","_tmpl","_template","chevronDownSvg","checkSvg","triggerClass","flexShrink","width","height","display","alignItems","justifyContent","gap","background","border","borderRadius","padding","color","fontSize","fontFamily","textAlign","outline","cursor","borderColor","triggerValueClass","overflow","textOverflow","whiteSpace","triggerPlaceholderClass","triggerIndicatorClass","overlayClass","position","inset","zIndex","flexDirection","pointerEvents","backdropClass","opacity","transition","sheetClass","maxHeight","borderTop","boxShadow","paddingBottom","transform","openClass","grabberClass","grabberBarClass","listClass","overflowY","WebkitOverflowScrolling","itemClass","itemIndicatorClass","SelectMobileContext","useSelectMobile","ctx","Error","read","source","Root","props","local","rest","internalOpen","defaultOpen","open","v","undefined","onOpenChange","itemToValue","item","value","itemToLabel","label","items","isSelected","String","selectedItem","find","setValue","onValueChange","onKey","e","key","document","addEventListener","prevOverflow","body","style","removeEventListener","_jsx","Provider","_mergeProps","children","Trigger","className","onClick","role","tabIndex","_Fragment","placeholder","svg","Item","data-selected","type","Content","aria-hidden","backdropClassName","backdropStyle","_el0","cloneNode","_setProp","clearable","clearLabel","each","SelectMobile","Object","assign"],"sources":["../../../src/components/SelectMobile/index.jsx"],"sourcesContent":["import { css } from '@emotion/css'\nimport {\n\tLoop, Portal, createContext, createEffect, createSignal, splitProps, useContext,\n} from '@plastic-js/plastic'\nimport Icon from '../Icon.jsx'\nimport { part } from './anatomy.js'\n\nconst chevronDownSvg = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>'\nconst checkSvg = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6 9 17l-5-5\"/></svg>'\n\nconst triggerClass = css({\n\tflexShrink: 0,\n\twidth: '100%',\n\theight: '44px',\n\tdisplay: 'flex',\n\talignItems: 'center',\n\tjustifyContent: 'space-between',\n\tgap: '4px',\n\tbackground: 'var(--bg, rgba(0,0,0,0.2))',\n\tborder: '1px solid var(--border)',\n\tborderRadius: '10px',\n\tpadding: '0 10px',\n\tcolor: 'var(--ink)',\n\tfontSize: '15px',\n\tfontFamily: 'inherit',\n\ttextAlign: 'left',\n\toutline: 'none',\n\tcursor: 'pointer',\n\t'&:focus-visible': { borderColor: 'var(--accent)' },\n})\n\nconst triggerValueClass = css({\n\toverflow: 'hidden',\n\ttextOverflow: 'ellipsis',\n\twhiteSpace: 'nowrap',\n})\n\nconst triggerPlaceholderClass = css({\n\tcolor: 'var(--muted)',\n})\n\nconst triggerIndicatorClass = css({\n\tflexShrink: 0,\n\tcolor: 'var(--muted)',\n\tdisplay: 'flex',\n\t'& svg': { width: '16px', height: '16px' },\n})\n\nconst overlayClass = css({\n\tposition: 'fixed',\n\tinset: 0,\n\tzIndex: 1000,\n\tdisplay: 'flex',\n\tflexDirection: 'column',\n\tjustifyContent: 'flex-end',\n\tpointerEvents: 'none',\n})\n\nconst backdropClass = css({\n\tposition: 'absolute',\n\tinset: 0,\n\tbackground: 'rgba(0,0,0,0.5)',\n\topacity: 0,\n\ttransition: 'opacity 240ms ease',\n})\n\nconst sheetClass = css({\n\tposition: 'relative',\n\twidth: '100%',\n\tmaxHeight: '70vh',\n\tdisplay: 'flex',\n\tflexDirection: 'column',\n\tbackground: 'var(--surface)',\n\tborderTop: '1px solid var(--border)',\n\tborderRadius: '18px 18px 0 0',\n\tboxShadow: '0 -8px 32px rgba(0,0,0,0.4)',\n\tpaddingBottom: 'env(safe-area-inset-bottom, 0px)',\n\ttransform: 'translateY(100%)',\n\ttransition: 'transform 280ms cubic-bezier(0.32, 0.72, 0, 1)',\n})\n\nconst openClass = css({\n\tpointerEvents: 'auto',\n\t[`& .${backdropClass}`]: { opacity: 1 },\n\t[`& .${sheetClass}`]: { transform: 'translateY(0)' },\n})\n\nconst grabberClass = css({\n\tflexShrink: 0,\n\tdisplay: 'flex',\n\tjustifyContent: 'center',\n\tpadding: '10px 0 4px',\n})\n\nconst grabberBarClass = css({\n\twidth: '36px',\n\theight: '4px',\n\tborderRadius: '999px',\n\tbackground: 'var(--border)',\n})\n\nconst listClass = css({\n\toverflowY: 'auto',\n\tWebkitOverflowScrolling: 'touch',\n\tpadding: '0 8px 8px',\n})\n\nconst itemClass = css({\n\twidth: '100%',\n\tdisplay: 'flex',\n\talignItems: 'center',\n\tjustifyContent: 'space-between',\n\tgap: '10px',\n\theight: '52px',\n\tpadding: '0 14px',\n\tbackground: 'transparent',\n\tborder: 'none',\n\tborderRadius: '12px',\n\tcolor: 'var(--ink)',\n\tfontSize: '16px',\n\tfontFamily: 'inherit',\n\ttextAlign: 'left',\n\tcursor: 'pointer',\n\t'&:active': { background: 'rgba(255,255,255,0.06)' },\n})\n\nconst itemIndicatorClass = css({\n\tflexShrink: 0,\n\tcolor: 'var(--accent)',\n\tdisplay: 'flex',\n\t'& svg': { width: '18px', height: '18px' },\n})\n\nconst SelectMobileContext = createContext(null)\n\nconst useSelectMobile = ()=> {\n\tconst ctx = useContext(SelectMobileContext)\n\tif (!ctx){ throw new Error('SelectMobile parts must be used inside <SelectMobile.Root>') }\n\treturn ctx\n}\n\nconst read = (source)=> {\n\treturn typeof source === 'function' ? source() : source\n}\n\n// <SelectMobile.Root> — holds selection + open state, resolves item -> value/label.\n// Props:\n// value current value (string | getter)\n// onValueChange (value) => void — called when an item is chosen\n// items data array (or getter) supplied by the consumer\n// itemToValue (item) => string — default item.value\n// itemToLabel (item) => node — default item.label\n// open controlled open state (getter) — when provided, the component\n// is controlled and you must update it via onOpenChange\n// defaultOpen initial open state for uncontrolled mode (default false)\n// onOpenChange (isOpen) => void — called when open state changes\nconst Root = (props)=> {\n\tconst [local, rest] = splitProps(props, [\n\t\t'value', 'onValueChange', 'items', 'itemToValue', 'itemToLabel',\n\t\t'open', 'defaultOpen', 'onOpenChange', 'children',\n\t])\n\tconst internalOpen = createSignal(local.defaultOpen ?? false)\n\n\tconst open = (v)=> {\n\t\tif (v === undefined){\n\t\t\t// getter — controlled if `open` prop is provided, else internal\n\t\t\treturn local.open !== undefined ? read(local.open) : internalOpen()\n\t\t}\n\t\t// setter\n\t\tif (local.open !== undefined){\n\t\t\tlocal.onOpenChange?.(v)\n\t\t} else {\n\t\t\tinternalOpen(v)\n\t\t\tlocal.onOpenChange?.(v)\n\t\t}\n\t}\n\n\tconst itemToValue = (item)=> {\n\t\tif (item == null){ return undefined }\n\t\treturn local.itemToValue ? local.itemToValue(item) : item.value\n\t}\n\tconst itemToLabel = (item)=> {\n\t\tif (item == null){ return undefined }\n\t\treturn local.itemToLabel ? local.itemToLabel(item) : item.label\n\t}\n\tconst items = ()=> read(local.items) || []\n\tconst value = ()=> read(local.value) ?? ''\n\tconst isSelected = v=> String(value()) === String(v)\n\tconst selectedItem = ()=> items().find(item=> isSelected(itemToValue(item)))\n\n\tconst setValue = (v)=> {\n\t\tlocal.onValueChange?.(v)\n\t\topen(false)\n\t}\n\n\tcreateEffect(()=> {\n\t\tif (!open()){ return undefined }\n\t\tconst onKey = (e)=> { if (e.key === 'Escape'){ open(false) } }\n\t\tdocument.addEventListener('keydown', onKey)\n\t\tconst prevOverflow = document.body.style.overflow\n\t\tdocument.body.style.overflow = 'hidden'\n\t\treturn ()=> {\n\t\t\tdocument.removeEventListener('keydown', onKey)\n\t\t\tdocument.body.style.overflow = prevOverflow\n\t\t}\n\t})\n\n\t// Plastic components run once (fine-grained reactivity), so this object is\n\t// created a single time per mount — the React \"stable value\" rule misfires.\n\t// eslint-disable-next-line react/jsx-no-constructed-context-values\n\tconst ctx = {\n\t\topen, value, items, itemToValue, itemToLabel, isSelected, selectedItem, setValue,\n\t}\n\n\treturn (\n\t\t<SelectMobileContext.Provider value={ctx}>\n\t\t\t<div {...part('root')} {...rest}>\n\t\t\t\t{local.children}\n\t\t\t</div>\n\t\t</SelectMobileContext.Provider>\n\t)\n}\n\n// <SelectMobile.Trigger> — the button that opens the sheet. Shows the selected\n// item's label (or `placeholder`). Pass children to fully customize.\n//\n// NOTE: Uses <div role=\"button\"> instead of a native <button> to work around a\n// Chrome iOS (WebKit) bug where tapping a <button> inside a scrollable container\n// with -webkit-overflow-scrolling:touch permanently pins activeElement to the\n// button. After the trigger opens the sheet, any subsequent focus() call on the\n// search <input> inside the list is silently ignored — the browser refuses to\n// release focus from the button. A <div role=\"button\"> is semantically equivalent\n// for accessibility but doesn't trigger this WebKit focus-lock behaviour.\n//\n// The original <button> was chosen for keyboard tab accessibility (tabIndex\n// works natively on <button>). On mobile, however, keyboard navigation is\n// irrelevant — the sheet is operated via touch, and the Escape key for closing\n// is handled by the Dialog overlay, not by tab order. The <div role=\"button\">\n// with tabIndex={0} preserves keyboard discoverability for assistive technology\n// while avoiding the iOS focus trap.\nconst Trigger = (props)=> {\n\tconst ctx = useSelectMobile()\n\tconst [local, rest] = splitProps(props, ['placeholder', 'className', 'children'])\n\n\treturn (\n\t\t<div\n\t\t\t{...part('trigger')}\n\t\t\tclassName={`${triggerClass} ${local.className || ''}`}\n\t\t\tonClick={()=> ctx.open(true)}\n\t\t\trole='button'\n\t\t\ttabIndex={0}\n\t\t\t{...rest}\n\t\t>\n\t\t\t{local.children ?? (\n\t\t\t\t<>\n\t\t\t\t\t<span\n\t\t\t\t\t\t{...part('triggerValue')}\n\t\t\t\t\t\tclassName={`${triggerValueClass} ${ctx.selectedItem() ? '' : triggerPlaceholderClass}`}\n\t\t\t\t\t>\n\t\t\t\t\t\t{()=> {\n\t\t\t\t\t\t\tconst item = ctx.selectedItem()\n\t\t\t\t\t\t\treturn item !== undefined ? ctx.itemToLabel(item) : local.placeholder ?? 'Select'\n\t\t\t\t\t\t}}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span {...part('triggerIndicator')} className={triggerIndicatorClass}>\n\t\t\t\t\t\t<Icon svg={chevronDownSvg} />\n\t\t\t\t\t</span>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n\n// <SelectMobile.Item> — a selectable row. Provide `item` (resolved via Root's\n// accessors) or an explicit `value` + children for custom content.\nconst Item = (props)=> {\n\tconst ctx = useSelectMobile()\n\tconst [local, rest] = splitProps(props, ['item', 'value', 'className', 'children'])\n\tconst value = ()=> local.item !== undefined ? ctx.itemToValue(local.item) : local.value\n\n\treturn (\n\t\t<button\n\t\t\t{...part('item')}\n\t\t\tclassName={`${itemClass} ${local.className || ''}`}\n\t\t\tdata-selected={()=> ctx.isSelected(value()) ? '' : undefined}\n\t\t\tonClick={()=> ctx.setValue(value())}\n\t\t\ttype='button'\n\t\t\t{...rest}\n\t\t>\n\t\t\t<span {...part('itemText')}>\n\t\t\t\t{local.children ?? (local.item !== undefined && ctx.itemToLabel(local.item))}\n\t\t\t</span>\n\t\t\t{()=> ctx.isSelected(value()) && (\n\t\t\t\t<span {...part('itemIndicator')} className={itemIndicatorClass}>\n\t\t\t\t\t<Icon svg={checkSvg} />\n\t\t\t\t</span>\n\t\t\t)}\n\t\t</button>\n\t)\n}\n\n// <SelectMobile.Content> — the bottom sheet. With no children it auto-renders one\n// SelectMobile.Item per Root item; pass `clearable` to prepend a \"clear\" row.\n// Props:\n// clearable show a \"clear\" row at the top\n// clearLabel label for the clear row (default \"None\")\n// backdropClassName className passed to the backdrop (higher priority)\n// backdropStyle inline style passed to the backdrop (higher priority)\nconst Content = (props)=> {\n\tconst ctx = useSelectMobile()\n\tconst [local, rest] = splitProps(props, [\n\t\t'clearable', 'clearLabel', 'className',\n\t\t'backdropClassName', 'backdropStyle', 'children',\n\t])\n\n\treturn (\n\t\t<Portal>\n\t\t\t<div\n\t\t\t\t{...part('positioner')}\n\t\t\t\taria-hidden={()=> !ctx.open()}\n\t\t\t\tclassName={`${overlayClass} ${ctx.open() ? openClass : ''}`}\n\t\t\t>\n\t\t\t\t<div {...part('backdrop')} className={`${backdropClass} ${local.backdropClassName || ''}`} style={local.backdropStyle} onClick={()=> ctx.open(false)} />\n\t\t\t\t<div {...part('content')} className={`${sheetClass} ${local.className || ''}`} role='dialog' {...rest}>\n\t\t\t\t\t<div {...part('grabber')} className={grabberClass} onClick={()=> ctx.open(false)}>\n\t\t\t\t\t\t<span className={grabberBarClass} />\n\t\t\t\t\t</div>\n\t\t\t\t\t<div {...part('list')} className={listClass}>\n\t\t\t\t\t\t{local.children ?? (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t{()=> local.clearable && (\n\t\t\t\t\t\t\t\t\t<Item value=''>\n\t\t\t\t\t\t\t\t\t\t{local.clearLabel ?? 'None'}\n\t\t\t\t\t\t\t\t\t</Item>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<Loop each={ctx.items}>\n\t\t\t\t\t\t\t\t\t{item=> <Item item={item} key={ctx.itemToValue(item)} />}\n\t\t\t\t\t\t\t\t</Loop>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</Portal>\n\t)\n}\n\nconst SelectMobile = Object.assign(Root, {\n\tRoot, Trigger, Content, Item,\n})\n\nexport default SelectMobile\nexport {\n\tSelectMobile, Root, Trigger, Content, Item,\n}"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["css","Loop","Portal","createContext","createEffect","createSignal","splitProps","useContext","Icon","part","_tmpl","_template","chevronDownSvg","checkSvg","triggerClass","flexShrink","width","height","display","alignItems","justifyContent","gap","background","border","borderRadius","padding","color","fontSize","fontFamily","textAlign","outline","cursor","borderColor","triggerValueClass","overflow","textOverflow","whiteSpace","triggerPlaceholderClass","triggerIndicatorClass","overlayClass","position","inset","zIndex","flexDirection","pointerEvents","backdropClass","opacity","transition","sheetClass","maxHeight","borderTop","boxShadow","paddingBottom","transform","openClass","grabberClass","grabberBarClass","listClass","overflowY","WebkitOverflowScrolling","itemClass","itemIndicatorClass","SelectMobileContext","useSelectMobile","ctx","Error","read","source","Root","props","local","rest","internalOpen","defaultOpen","open","v","undefined","onOpenChange","itemToValue","item","value","itemToLabel","label","items","isSelected","String","selectedItem","find","setValue","onValueChange","onKey","e","key","document","addEventListener","prevOverflow","body","style","removeEventListener","_jsx","Provider","_mergeProps","children","Trigger","className","onClick","role","tabIndex","_Fragment","placeholder","svg","Item","data-selected","type","Content","aria-hidden","backdropClassName","backdropStyle","_el0","cloneNode","_setProp","clearable","clearLabel","each","SelectMobile","Object","assign"],"sources":["../../../src/components/SelectMobile/index.jsx"],"sourcesContent":["import { css } from '@emotion/css'\nimport {\n\tLoop, Portal, createContext, createEffect, createSignal, splitProps, useContext,\n} from '@plastic-js/plastic'\nimport Icon from '../Icon.jsx'\nimport { part } from './anatomy.js'\n\nconst chevronDownSvg = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>'\nconst checkSvg = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6 9 17l-5-5\"/></svg>'\n\nconst triggerClass = css({\n\tflexShrink: 0,\n\twidth: '100%',\n\theight: '44px',\n\tdisplay: 'flex',\n\talignItems: 'center',\n\tjustifyContent: 'space-between',\n\tgap: '4px',\n\tbackground: 'var(--bg, rgba(0,0,0,0.2))',\n\tborder: '1px solid var(--border)',\n\tborderRadius: '10px',\n\tpadding: '0 10px',\n\tcolor: 'var(--ink)',\n\tfontSize: '15px',\n\tfontFamily: 'inherit',\n\ttextAlign: 'left',\n\toutline: 'none',\n\tcursor: 'pointer',\n\t'&:focus-visible': { borderColor: 'var(--accent)' },\n})\n\nconst triggerValueClass = css({\n\toverflow: 'hidden',\n\ttextOverflow: 'ellipsis',\n\twhiteSpace: 'nowrap',\n})\n\nconst triggerPlaceholderClass = css({\n\tcolor: 'var(--muted)',\n})\n\nconst triggerIndicatorClass = css({\n\tflexShrink: 0,\n\tcolor: 'var(--muted)',\n\tdisplay: 'flex',\n\t'& svg': { width: '16px', height: '16px' },\n})\n\nconst overlayClass = css({\n\tposition: 'fixed',\n\tinset: 0,\n\tzIndex: 1000,\n\tdisplay: 'flex',\n\tflexDirection: 'column',\n\tjustifyContent: 'flex-end',\n\tpointerEvents: 'none',\n})\n\nconst backdropClass = css({\n\tposition: 'absolute',\n\tinset: 0,\n\tbackground: 'rgba(0,0,0,0.5)',\n\topacity: 0,\n\ttransition: 'opacity 240ms ease',\n})\n\nconst sheetClass = css({\n\tposition: 'relative',\n\twidth: '100%',\n\tmaxHeight: '70vh',\n\tdisplay: 'flex',\n\tflexDirection: 'column',\n\tbackground: 'var(--surface)',\n\tborderTop: '1px solid var(--border)',\n\tborderRadius: '18px 18px 0 0',\n\tboxShadow: '0 -8px 32px rgba(0,0,0,0.4)',\n\tpaddingBottom: 'env(safe-area-inset-bottom, 0px)',\n\ttransform: 'translateY(100%)',\n\ttransition: 'transform 280ms cubic-bezier(0.32, 0.72, 0, 1)',\n})\n\nconst openClass = css({\n\tpointerEvents: 'auto',\n\t[`& .${backdropClass}`]: { opacity: 1 },\n\t[`& .${sheetClass}`]: { transform: 'translateY(0)' },\n})\n\nconst grabberClass = css({\n\tflexShrink: 0,\n\tdisplay: 'flex',\n\tjustifyContent: 'center',\n\tpadding: '10px 0 4px',\n})\n\nconst grabberBarClass = css({\n\twidth: '36px',\n\theight: '4px',\n\tborderRadius: '999px',\n\tbackground: 'var(--border)',\n})\n\nconst listClass = css({\n\toverflowY: 'auto',\n\tWebkitOverflowScrolling: 'touch',\n\tpadding: '0 8px 8px',\n})\n\nconst itemClass = css({\n\twidth: '100%',\n\tdisplay: 'flex',\n\talignItems: 'center',\n\tjustifyContent: 'space-between',\n\tgap: '10px',\n\theight: '52px',\n\tpadding: '0 14px',\n\tbackground: 'transparent',\n\tborder: 'none',\n\tborderRadius: '12px',\n\tcolor: 'var(--ink)',\n\tfontSize: '16px',\n\tfontFamily: 'inherit',\n\ttextAlign: 'left',\n\tcursor: 'pointer',\n\t'&:active': { background: 'rgba(255,255,255,0.06)' },\n})\n\nconst itemIndicatorClass = css({\n\tflexShrink: 0,\n\tcolor: 'var(--accent)',\n\tdisplay: 'flex',\n\t'& svg': { width: '18px', height: '18px' },\n})\n\nconst SelectMobileContext = createContext(null)\n\nconst useSelectMobile = ()=> {\n\tconst ctx = useContext(SelectMobileContext)\n\tif (!ctx){ throw new Error('SelectMobile parts must be used inside <SelectMobile.Root>') }\n\treturn ctx\n}\n\nconst read = (source)=> {\n\treturn typeof source === 'function' ? source() : source\n}\n\n// <SelectMobile.Root> — holds selection + open state, resolves item -> value/label.\n// Props:\n// value current value (string | getter)\n// onValueChange (value) => void — called when an item is chosen\n// items data array (or getter) supplied by the consumer\n// itemToValue (item) => string — default item.value\n// itemToLabel (item) => node — default item.label\n// open controlled open state (getter) — when provided, the component\n// is controlled and you must update it via onOpenChange\n// defaultOpen initial open state for uncontrolled mode (default false)\n// onOpenChange (isOpen) => void — called when open state changes\nconst Root = (props)=> {\n\tconst [local, rest] = splitProps(props, [\n\t\t'value', 'onValueChange', 'items', 'itemToValue', 'itemToLabel',\n\t\t'open', 'defaultOpen', 'onOpenChange', 'children',\n\t])\n\tconst internalOpen = createSignal(local.defaultOpen ?? false)\n\n\tconst open = (v)=> {\n\t\tif (v === undefined){\n\t\t\t// getter — controlled if `open` prop is provided, else internal\n\t\t\treturn local.open !== undefined ? read(local.open) : internalOpen()\n\t\t}\n\t\t// setter\n\t\tif (local.open !== undefined){\n\t\t\tlocal.onOpenChange?.(v)\n\t\t} else {\n\t\t\tinternalOpen(v)\n\t\t\tlocal.onOpenChange?.(v)\n\t\t}\n\t}\n\n\tconst itemToValue = (item)=> {\n\t\tif (item == null){ return undefined }\n\t\treturn local.itemToValue ? local.itemToValue(item) : item.value\n\t}\n\tconst itemToLabel = (item)=> {\n\t\tif (item == null){ return undefined }\n\t\treturn local.itemToLabel ? local.itemToLabel(item) : item.label\n\t}\n\tconst items = ()=> read(local.items) || []\n\tconst value = ()=> read(local.value) ?? ''\n\tconst isSelected = v=> String(value()) === String(v)\n\tconst selectedItem = ()=> items().find(item=> isSelected(itemToValue(item)))\n\n\tconst setValue = (v)=> {\n\t\tlocal.onValueChange?.(v)\n\t\topen(false)\n\t}\n\n\tcreateEffect(()=> {\n\t\tif (!open()){ return undefined }\n\t\tconst onKey = (e)=> { if (e.key === 'Escape'){ open(false) } }\n\t\tdocument.addEventListener('keydown', onKey)\n\t\tconst prevOverflow = document.body.style.overflow\n\t\tdocument.body.style.overflow = 'hidden'\n\t\treturn ()=> {\n\t\t\tdocument.removeEventListener('keydown', onKey)\n\t\t\tdocument.body.style.overflow = prevOverflow\n\t\t}\n\t})\n\n\t// Plastic components run once (fine-grained reactivity), so this object is\n\t// created a single time per mount — the React \"stable value\" rule misfires.\n\t// eslint-disable-next-line react/jsx-no-constructed-context-values\n\tconst ctx = {\n\t\topen, value, items, itemToValue, itemToLabel, isSelected, selectedItem, setValue,\n\t}\n\n\treturn (\n\t\t<SelectMobileContext.Provider value={ctx}>\n\t\t\t<div {...part('root')} {...rest}>\n\t\t\t\t{local.children}\n\t\t\t</div>\n\t\t</SelectMobileContext.Provider>\n\t)\n}\n\n// <SelectMobile.Trigger> — the button that opens the sheet. Shows the selected\n// item's label (or `placeholder`). Pass children to fully customize.\n//\n// NOTE: Uses <div role=\"button\"> instead of a native <button> as a\n// defense-in-depth measure against a Chrome iOS (WebKit) focus-lock bug.\n//\n// The primary fix is in <SelectMobile.Content>: the sheet is rendered inline\n// (no <Portal>) so it stays inside the parent Dialog's focus-trap boundary.\n// However, the <div role=\"button\"> is kept as an additional safeguard — in\n// WebKit, tapping a <button> inside a scrollable container with\n// -webkit-overflow-scrolling:touch can pin activeElement to the button,\n// causing subsequent focus() calls on the filter <input> to be silently\n// ignored. A <div role=\"button\"> is semantically equivalent for accessibility\n// but does not trigger this WebKit focus-lock behaviour.\nconst Trigger = (props)=> {\n\tconst ctx = useSelectMobile()\n\tconst [local, rest] = splitProps(props, ['placeholder', 'className', 'children'])\n\n\treturn (\n\t\t<div\n\t\t\t{...part('trigger')}\n\t\t\tclassName={`${triggerClass} ${local.className || ''}`}\n\t\t\tonClick={()=> ctx.open(true)}\n\t\t\trole='button'\n\t\t\ttabIndex={0}\n\t\t\t{...rest}\n\t\t>\n\t\t\t{local.children ?? (\n\t\t\t\t<>\n\t\t\t\t\t<span\n\t\t\t\t\t\t{...part('triggerValue')}\n\t\t\t\t\t\tclassName={`${triggerValueClass} ${ctx.selectedItem() ? '' : triggerPlaceholderClass}`}\n\t\t\t\t\t>\n\t\t\t\t\t\t{()=> {\n\t\t\t\t\t\t\tconst item = ctx.selectedItem()\n\t\t\t\t\t\t\treturn item !== undefined ? ctx.itemToLabel(item) : local.placeholder ?? 'Select'\n\t\t\t\t\t\t}}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span {...part('triggerIndicator')} className={triggerIndicatorClass}>\n\t\t\t\t\t\t<Icon svg={chevronDownSvg} />\n\t\t\t\t\t</span>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n\n// <SelectMobile.Item> — a selectable row. Provide `item` (resolved via Root's\n// accessors) or an explicit `value` + children for custom content.\nconst Item = (props)=> {\n\tconst ctx = useSelectMobile()\n\tconst [local, rest] = splitProps(props, ['item', 'value', 'className', 'children'])\n\tconst value = ()=> local.item !== undefined ? ctx.itemToValue(local.item) : local.value\n\n\treturn (\n\t\t<button\n\t\t\t{...part('item')}\n\t\t\tclassName={`${itemClass} ${local.className || ''}`}\n\t\t\tdata-selected={()=> ctx.isSelected(value()) ? '' : undefined}\n\t\t\tonClick={()=> ctx.setValue(value())}\n\t\t\ttype='button'\n\t\t\t{...rest}\n\t\t>\n\t\t\t<span {...part('itemText')}>\n\t\t\t\t{local.children ?? (local.item !== undefined && ctx.itemToLabel(local.item))}\n\t\t\t</span>\n\t\t\t{()=> ctx.isSelected(value()) && (\n\t\t\t\t<span {...part('itemIndicator')} className={itemIndicatorClass}>\n\t\t\t\t\t<Icon svg={checkSvg} />\n\t\t\t\t</span>\n\t\t\t)}\n\t\t</button>\n\t)\n}\n\n// <SelectMobile.Content> — the bottom sheet. With no children it auto-renders one\n// SelectMobile.Item per Root item; pass `clearable` to prepend a \"clear\" row.\n// Props:\n// clearable show a \"clear\" row at the top\n// clearLabel label for the clear row (default \"None\")\n// backdropClassName className passed to the backdrop (higher priority)\n// backdropStyle inline style passed to the backdrop (higher priority)\n//\n// NOTE: No <Portal> wrapper. The overlay uses `position: fixed; inset: 0;\n// z-index: 1000` so it still covers the viewport without being portaled to\n// document.body. Rendering inline keeps the sheet inside the nearest focus-trap\n// boundary (e.g. a parent Dialog), which prevents zag-js / ark focus traps from\n// fighting the filter input's auto-focus. This is the primary fix for the\n// Chrome iOS focus-lock bug when SelectMobile is nested inside a Dialog.\nconst Content = (props)=> {\n\tconst ctx = useSelectMobile()\n\tconst [local, rest] = splitProps(props, [\n\t\t'clearable', 'clearLabel', 'className',\n\t\t'backdropClassName', 'backdropStyle', 'children',\n\t])\n\treturn (\n\t\t<div\n\t\t\t{...part('positioner')}\n\t\t\taria-hidden={()=> !ctx.open()}\n\t\t\tclassName={`${overlayClass} ${ctx.open() ? openClass : ''}`}\n\t\t>\n\t\t\t<div {...part('backdrop')} className={`${backdropClass} ${local.backdropClassName || ''}`} style={local.backdropStyle} onClick={()=> ctx.open(false)} />\n\t\t\t<div {...part('content')} className={`${sheetClass} ${local.className || ''}`} role='dialog' {...rest}>\n\t\t\t\t<div {...part('grabber')} className={grabberClass} onClick={()=> ctx.open(false)}>\n\t\t\t\t\t<span className={grabberBarClass} />\n\t\t\t\t</div>\n\t\t\t\t<div {...part('list')} className={listClass}>\n\t\t\t\t\t{local.children ?? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t{()=> local.clearable && (\n\t\t\t\t\t\t\t\t<Item value=''>\n\t\t\t\t\t\t\t\t\t{local.clearLabel ?? 'None'}\n\t\t\t\t\t\t\t\t</Item>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<Loop each={ctx.items}>\n\t\t\t\t\t\t\t\t{item=> <Item item={item} key={ctx.itemToValue(item)} />}\n\t\t\t\t\t\t\t</Loop>\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nconst SelectMobile = Object.assign(Root, {\n\tRoot, Trigger, Content, Item,\n})\n\nexport default SelectMobile\nexport {\n\tSelectMobile, Root, Trigger, Content, Item,\n}"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plastic-js/tsumiki",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "A UI component library for Plastic JS.",
5
5
  "license": "MIT",
6
6
  "author": "tigre",