@braedonsaunders/appkit-ui 0.2.0 → 0.2.1
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/global-search.d.ts +1 -1
- package/global-search.d.ts.map +1 -1
- package/global-search.js +1 -1
- package/global-search.js.map +1 -1
- package/navigation-config-editor.d.ts +1 -1
- package/navigation-config-editor.js +1 -1
- package/navigation-config-editor.js.map +1 -1
- package/package.json +1 -1
- package/search-select.d.ts.map +1 -1
- package/search-select.js +12 -4
- package/search-select.js.map +1 -1
package/global-search.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export type GlobalSearchLabels = {
|
|
|
29
29
|
close: string;
|
|
30
30
|
resultCount: (count: number) => string;
|
|
31
31
|
};
|
|
32
|
-
/**
|
|
32
|
+
/** Shared search interaction with app-owned querying and navigation. */
|
|
33
33
|
export declare function GlobalSearch({ search, onNavigate, className, labels, minimumQueryLength, debounceMs, }: {
|
|
34
34
|
search: (query: string, signal: AbortSignal) => Promise<GlobalSearchResult>;
|
|
35
35
|
onNavigate: (hit: GlobalSearchHit) => void;
|
package/global-search.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-search.d.ts","sourceRoot":"","sources":["../src/global-search.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,eAAe,EAAE,CAAA;CAAE,CAAA;AACtF,MAAM,MAAM,kBAAkB,GAAG;IAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAC/E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;CACvC,CAAA;AAoBD,
|
|
1
|
+
{"version":3,"file":"global-search.d.ts","sourceRoot":"","sources":["../src/global-search.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,eAAe,EAAE,CAAA;CAAE,CAAA;AACtF,MAAM,MAAM,kBAAkB,GAAG;IAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAC/E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;CACvC,CAAA;AAoBD,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,UAAU,EACV,SAAS,EACT,MAAuB,EACvB,kBAAsB,EACtB,UAAgB,GACjB,EAAE;IACD,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC3E,UAAU,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAA;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,qBA6FA"}
|
package/global-search.js
CHANGED
|
@@ -22,7 +22,7 @@ function Highlight({ text, query }) {
|
|
|
22
22
|
return text;
|
|
23
23
|
return _jsxs(_Fragment, { children: [text.slice(0, index), _jsx("mark", { className: "bg-transparent font-semibold text-primary", children: text.slice(index, index + query.length) }), text.slice(index + query.length)] });
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
25
|
+
/** Shared search interaction with app-owned querying and navigation. */
|
|
26
26
|
export function GlobalSearch({ search, onNavigate, className, labels = DEFAULT_LABELS, minimumQueryLength = 2, debounceMs = 180, }) {
|
|
27
27
|
const inputRef = React.useRef(null);
|
|
28
28
|
const rootRef = React.useRef(null);
|
package/global-search.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-search.js","sourceRoot":"","sources":["../src/global-search.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AA2B5B,MAAM,cAAc,GAAuB;IACzC,WAAW,EAAE,SAAS;IACtB,SAAS,EAAE,wBAAwB;IACnC,KAAK,EAAE,cAAc;IACrB,SAAS,EAAE,YAAY;IACvB,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,KAAK,GAAG;IACjD,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE;CACnE,CAAA;AAED,SAAS,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAmC;IACjE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;IAC7D,IAAI,CAAC,KAAK,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IACpC,OAAO,8BAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAC,eAAM,SAAS,EAAC,2CAA2C,YAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,GAAQ,EAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAA;AAClL,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,YAAY,CAAC,EAC3B,MAAM,EACN,UAAU,EACV,SAAS,EACT,MAAM,GAAG,cAAc,EACvB,kBAAkB,GAAG,CAAC,EACtB,UAAU,GAAG,GAAG,GAQjB;IACC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAmB,IAAI,CAAC,CAAA;IACrD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAiB,IAAI,CAAC,CAAA;IAClD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,CAAA;IAC/B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC5C,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IACnD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,CAA4B,IAAI,CAAC,CAAA;IAC3E,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAE/F,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,SAAS,KAAK,CAAC,KAAoB;YACjC,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,GAAG,EAAE,CAAC;gBACxE,KAAK,CAAC,cAAc,EAAE,CAAA;gBACtB,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,CAAA;gBACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;gBAC1B,OAAO,CAAC,IAAI,CAAC,CAAA;YACf,CAAC;QACH,CAAC;QACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;QACzC,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;IAC3D,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,SAAS,aAAa,CAAC,KAAmB;YACxC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAc,CAAC;gBAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QACtE,CAAC;QACD,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAA;QACvD,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAA;IACzE,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;QACzB,IAAI,IAAI,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;YACrC,SAAS,CAAC,IAAI,CAAC,CAAA;YACf,UAAU,CAAC,KAAK,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;QACxC,UAAU,CAAC,IAAI,CAAC,CAAA;QAChB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE;YAC3C,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;gBAClD,SAAS,CAAC,IAAI,CAAC,CAAA;gBACf,SAAS,CAAC,CAAC,CAAC,CAAA;YACd,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAK,KAAe,CAAC,IAAI,KAAK,YAAY;oBAAE,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;YACjF,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO;oBAAE,UAAU,CAAC,KAAK,CAAC,CAAA;YACnD,CAAC;QACH,CAAC,EAAE,UAAU,CAAC,CAAA;QACd,OAAO,GAAG,EAAE,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC,CAAA;IACnE,CAAC,EAAE,CAAC,UAAU,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;IAEnD,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,GAAgC,EAAE,EAAE;QAChE,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,OAAO,CAAC,KAAK,CAAC,CAAA;QACd,QAAQ,CAAC,EAAE,CAAC,CAAA;QACZ,SAAS,CAAC,IAAI,CAAC,CAAA;QACf,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,CAAA;QACxB,UAAU,CAAC,GAAG,CAAC,CAAA;IACjB,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;IAEhB,SAAS,SAAS,CAAC,KAA4C;QAC7D,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;YAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;YAAC,OAAM;QAAC,CAAC;QAChF,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAM;QACxB,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;YAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAA;QAAC,CAAC;aACrG,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAA;QAAC,CAAC;aACtH,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;YAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;QAAC,CAAC;IAC9E,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,kBAAkB,CAAA;IACnE,IAAI,SAAS,GAAG,CAAC,CAAC,CAAA;IAClB,OAAO,CACL,eAAK,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,aACrD,eAAK,SAAS,EAAC,UAAU,aACvB,KAAC,MAAM,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,6EAA6E,GAAG,EAC5G,gBAAO,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,gBAAc,MAAM,CAAC,SAAS,EAAE,IAAI,EAAC,UAAU,mBAAgB,SAAS,mBAAiB,SAAS,EAAE,SAAS,EAAC,qPAAqP,GAAG,EACniB,cAAK,SAAS,EAAC,qEAAqE,YACjF,OAAO,CAAC,CAAC,CAAC,KAAC,OAAO,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,6BAA6B,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,iBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,CAAA,CAAC,CAAC,EAAE,SAAS,EAAC,4CAA4C,gBAAa,MAAM,CAAC,KAAK,YAAE,KAAC,CAAC,IAAC,IAAI,EAAE,EAAE,GAAI,GAAS,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,yHAAyH,wBAAS,GAC/a,IACF,EACL,SAAS,CAAC,CAAC,CAAC,CACX,cAAK,EAAE,EAAE,SAAS,EAAE,SAAS,EAAC,oJAAoJ,EAAC,IAAI,EAAC,SAAS,YAC9L,MAAM,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,8BAC3B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,eAAoB,SAAS,EAAC,gBAAgB,aAAC,cAAK,SAAS,EAAC,oFAAoF,YAAE,KAAK,CAAC,KAAK,GAAO,EAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,MAAM,QAAQ,GAAG,KAAK,KAAK,MAAM,CAAC,CAAC,OAAO,kBAAsC,IAAI,EAAC,QAAQ,EAAC,IAAI,EAAC,QAAQ,mBAAgB,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,mFAAmF,EAAE,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,wBAAwB,CAAC,aAAE,eAAM,SAAS,EAAE,EAAE,CAAC,oDAAoD,EAAE,QAAQ,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,4BAA4B,CAAC,YAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,KAAC,OAAO,IAAC,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,GAAI,CAAC,CAAC,CAAC,KAAC,MAAM,IAAC,IAAI,EAAE,EAAE,GAAI,GAAQ,EAAA,gBAAM,SAAS,EAAC,gBAAgB,aAAC,gBAAM,SAAS,EAAC,yBAAyB,aAAC,eAAM,SAAS,EAAC,0BAA0B,YAAC,KAAC,SAAS,IAAC,IAAI,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,GAAI,GAAO,EAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAC,KAAK,IAAC,OAAO,EAAC,WAAW,EAAC,SAAS,EAAC,aAAa,YAAE,GAAG,CAAC,KAAK,GAAS,CAAC,CAAC,CAAC,IAAI,IAAQ,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,sCAAsC,YAAE,GAAG,CAAC,QAAQ,GAAQ,CAAC,CAAC,CAAC,IAAI,IAAQ,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,uDAAuD,YAAE,GAAG,CAAC,IAAI,GAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAC,cAAc,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,uBAAuB,GAAG,CAAC,CAAC,CAAC,IAAI,KAApnC,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,EAAE,CAAumC,CAAA,CAAA,CAAC,CAAC,KAAv5C,KAAK,CAAC,EAAE,CAAs5C,CAAC,EACv8C,eAAK,SAAS,EAAC,6GAA6G,aAAC,2BAAM,cAAK,SAAS,EAAC,WAAW,6BAAS,OAAE,MAAM,CAAC,QAAQ,kBAAa,cAAK,SAAS,EAAC,WAAW,uBAAQ,OAAE,MAAM,CAAC,IAAI,kBAAa,cAAK,SAAS,EAAC,WAAW,oBAAU,OAAE,MAAM,CAAC,KAAK,IAAQ,EAAA,yBAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAQ,IAAM,IAC9W,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,eAAK,SAAS,EAAC,0DAA0D,aAAC,KAAC,OAAO,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,cAAc,GAAG,EAAC,MAAM,CAAC,SAAS,IAAO,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,8CAA8C,YAAE,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,GAAO,GACnQ,CACP,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAA;AACH,CAAC","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { CornerDownLeft, Loader2, Search, X } from 'lucide-react'\nimport { Badge } from './badge'\nimport { NavIcon } from './sidebar-nav'\nimport { cn } from './utils'\n\nexport type GlobalSearchHit = {\n id: string\n type: string\n title: string\n subtitle?: string\n href: string\n iconKey?: string\n badge?: string\n meta?: string\n}\n\nexport type GlobalSearchGroup = { id: string; label: string; hits: GlobalSearchHit[] }\nexport type GlobalSearchResult = { groups: GlobalSearchGroup[]; total: number }\nexport type GlobalSearchLabels = {\n placeholder: string\n ariaLabel: string\n clear: string\n searching: string\n noMatches: (query: string) => string\n navigate: string\n open: string\n close: string\n resultCount: (count: number) => string\n}\n\nconst DEFAULT_LABELS: GlobalSearchLabels = {\n placeholder: 'Search…',\n ariaLabel: 'Search the application',\n clear: 'Clear search',\n searching: 'Searching…',\n noMatches: (query) => `No matches for “${query}”`,\n navigate: 'navigate',\n open: 'open',\n close: 'close',\n resultCount: (count) => `${count} result${count === 1 ? '' : 's'}`,\n}\n\nfunction Highlight({ text, query }: { text: string; query: string }) {\n const index = text.toLowerCase().indexOf(query.toLowerCase())\n if (!query || index < 0) return text\n return <>{text.slice(0, index)}<mark className=\"bg-transparent font-semibold text-primary\">{text.slice(index, index + query.length)}</mark>{text.slice(index + query.length)}</>\n}\n\n/** Production sibling search interaction with app-owned querying and navigation. */\nexport function GlobalSearch({\n search,\n onNavigate,\n className,\n labels = DEFAULT_LABELS,\n minimumQueryLength = 2,\n debounceMs = 180,\n}: {\n search: (query: string, signal: AbortSignal) => Promise<GlobalSearchResult>\n onNavigate: (hit: GlobalSearchHit) => void\n className?: string\n labels?: GlobalSearchLabels\n minimumQueryLength?: number\n debounceMs?: number\n}) {\n const inputRef = React.useRef<HTMLInputElement>(null)\n const rootRef = React.useRef<HTMLDivElement>(null)\n const resultsId = React.useId()\n const [query, setQuery] = React.useState('')\n const [open, setOpen] = React.useState(false)\n const [loading, setLoading] = React.useState(false)\n const [result, setResult] = React.useState<GlobalSearchResult | null>(null)\n const [active, setActive] = React.useState(0)\n const hits = React.useMemo(() => result?.groups.flatMap((group) => group.hits) ?? [], [result])\n\n React.useEffect(() => {\n function onKey(event: KeyboardEvent) {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {\n event.preventDefault()\n inputRef.current?.focus()\n inputRef.current?.select()\n setOpen(true)\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [])\n\n React.useEffect(() => {\n function onPointerDown(event: PointerEvent) {\n if (!rootRef.current?.contains(event.target as Node)) setOpen(false)\n }\n document.addEventListener('pointerdown', onPointerDown)\n return () => document.removeEventListener('pointerdown', onPointerDown)\n }, [])\n\n React.useEffect(() => {\n const term = query.trim()\n if (term.length < minimumQueryLength) {\n setResult(null)\n setLoading(false)\n return\n }\n const controller = new AbortController()\n setLoading(true)\n const timeout = window.setTimeout(async () => {\n try {\n const next = await search(term, controller.signal)\n setResult(next)\n setActive(0)\n } catch (error) {\n if ((error as Error).name !== 'AbortError') setResult({ groups: [], total: 0 })\n } finally {\n if (!controller.signal.aborted) setLoading(false)\n }\n }, debounceMs)\n return () => { window.clearTimeout(timeout); controller.abort() }\n }, [debounceMs, minimumQueryLength, query, search])\n\n const go = React.useCallback((hit: GlobalSearchHit | undefined) => {\n if (!hit) return\n setOpen(false)\n setQuery('')\n setResult(null)\n inputRef.current?.blur()\n onNavigate(hit)\n }, [onNavigate])\n\n function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {\n if (event.key === 'Escape') { setOpen(false); inputRef.current?.blur(); return }\n if (!hits.length) return\n if (event.key === 'ArrowDown') { event.preventDefault(); setActive((value) => (value + 1) % hits.length) }\n else if (event.key === 'ArrowUp') { event.preventDefault(); setActive((value) => (value - 1 + hits.length) % hits.length) }\n else if (event.key === 'Enter') { event.preventDefault(); go(hits[active]) }\n }\n\n const showPanel = open && query.trim().length >= minimumQueryLength\n let itemIndex = -1\n return (\n <div ref={rootRef} className={cn('relative', className)}>\n <div className=\"relative\">\n <Search size={15} className=\"pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-fg-subtle\" />\n <input ref={inputRef} value={query} onChange={(event) => { setQuery(event.target.value); setOpen(true) }} onFocus={() => setOpen(true)} onKeyDown={onKeyDown} placeholder={labels.placeholder} aria-label={labels.ariaLabel} role=\"combobox\" aria-expanded={showPanel} aria-controls={resultsId} className=\"h-9 w-full rounded-lg border border-border bg-bg-subtle pr-16 pl-9 text-sm text-fg transition-colors placeholder:text-fg-subtle hover:border-border-strong focus:border-primary focus:bg-surface focus:ring-2 focus:ring-ring/20 focus:outline-none\" />\n <div className=\"absolute top-1/2 right-2.5 flex -translate-y-1/2 items-center gap-1\">\n {loading ? <Loader2 size={14} className=\"animate-spin text-fg-subtle\" /> : query ? <button type=\"button\" onClick={() => { setQuery(''); setResult(null); inputRef.current?.focus() }} className=\"rounded p-0.5 text-fg-subtle hover:text-fg\" aria-label={labels.clear}><X size={14} /></button> : <kbd className=\"hidden rounded border border-border bg-surface px-1.5 py-0.5 font-sans text-[10px] font-medium text-fg-subtle sm:inline\">⌘K</kbd>}\n </div>\n </div>\n {showPanel ? (\n <div id={resultsId} className=\"absolute top-[calc(100%+6px)] left-0 z-50 max-h-[70vh] w-full min-w-88 overflow-y-auto rounded-xl border border-border bg-elevated p-1.5 shadow-lg\" role=\"listbox\">\n {result && result.total > 0 ? <>\n {result.groups.map((group) => <div key={group.id} className=\"mb-1 last:mb-0\"><div className=\"px-2 pt-1.5 pb-1 text-[11px] font-semibold tracking-wider text-fg-subtle uppercase\">{group.label}</div>{group.hits.map((hit) => { itemIndex += 1; const index = itemIndex; const selected = index === active; return <button key={`${hit.type}-${hit.id}`} type=\"button\" role=\"option\" aria-selected={selected} onMouseEnter={() => setActive(index)} onMouseDown={(event) => { event.preventDefault(); go(hit) }} className={cn('flex w-full items-center gap-3 rounded-lg px-2 py-1.5 text-left transition-colors', selected ? 'bg-primary-subtle' : 'hover:bg-surface-hover')}><span className={cn('grid size-7 shrink-0 place-items-center rounded-md', selected ? 'bg-surface text-primary' : 'bg-bg-subtle text-fg-muted')}>{hit.iconKey ? <NavIcon iconKey={hit.iconKey} size={15} /> : <Search size={15} />}</span><span className=\"min-w-0 flex-1\"><span className=\"flex items-center gap-2\"><span className=\"truncate text-sm text-fg\"><Highlight text={hit.title} query={query.trim()} /></span>{hit.badge ? <Badge variant=\"secondary\" className=\"text-[10px]\">{hit.badge}</Badge> : null}</span>{hit.subtitle ? <span className=\"block truncate text-xs text-fg-muted\">{hit.subtitle}</span> : null}</span>{hit.meta ? <span className=\"shrink-0 font-mono text-xs tabular-nums text-fg-muted\">{hit.meta}</span> : null}{selected ? <CornerDownLeft size={13} className=\"shrink-0 text-primary\" /> : null}</button>})}</div>)}\n <div className=\"mt-1 flex items-center justify-between border-t border-border-subtle px-2 py-1.5 text-[11px] text-fg-subtle\"><span><kbd className=\"font-sans\">↑↓</kbd> {labels.navigate} <kbd className=\"font-sans\">↵</kbd> {labels.open} <kbd className=\"font-sans\">esc</kbd> {labels.close}</span><span>{labels.resultCount(result.total)}</span></div>\n </> : loading ? <div className=\"flex items-center gap-2 px-3 py-6 text-sm text-fg-subtle\"><Loader2 size={15} className=\"animate-spin\" />{labels.searching}</div> : <div className=\"px-3 py-6 text-center text-sm text-fg-subtle\">{labels.noMatches(query.trim())}</div>}\n </div>\n ) : null}\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"global-search.js","sourceRoot":"","sources":["../src/global-search.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { CornerDownLeft, Loader2, Search, X } from 'lucide-react'\nimport { Badge } from './badge'\nimport { NavIcon } from './sidebar-nav'\nimport { cn } from './utils'\n\nexport type GlobalSearchHit = {\n id: string\n type: string\n title: string\n subtitle?: string\n href: string\n iconKey?: string\n badge?: string\n meta?: string\n}\n\nexport type GlobalSearchGroup = { id: string; label: string; hits: GlobalSearchHit[] }\nexport type GlobalSearchResult = { groups: GlobalSearchGroup[]; total: number }\nexport type GlobalSearchLabels = {\n placeholder: string\n ariaLabel: string\n clear: string\n searching: string\n noMatches: (query: string) => string\n navigate: string\n open: string\n close: string\n resultCount: (count: number) => string\n}\n\nconst DEFAULT_LABELS: GlobalSearchLabels = {\n placeholder: 'Search…',\n ariaLabel: 'Search the application',\n clear: 'Clear search',\n searching: 'Searching…',\n noMatches: (query) => `No matches for “${query}”`,\n navigate: 'navigate',\n open: 'open',\n close: 'close',\n resultCount: (count) => `${count} result${count === 1 ? '' : 's'}`,\n}\n\nfunction Highlight({ text, query }: { text: string; query: string }) {\n const index = text.toLowerCase().indexOf(query.toLowerCase())\n if (!query || index < 0) return text\n return <>{text.slice(0, index)}<mark className=\"bg-transparent font-semibold text-primary\">{text.slice(index, index + query.length)}</mark>{text.slice(index + query.length)}</>\n}\n\n/** Shared search interaction with app-owned querying and navigation. */\nexport function GlobalSearch({\n search,\n onNavigate,\n className,\n labels = DEFAULT_LABELS,\n minimumQueryLength = 2,\n debounceMs = 180,\n}: {\n search: (query: string, signal: AbortSignal) => Promise<GlobalSearchResult>\n onNavigate: (hit: GlobalSearchHit) => void\n className?: string\n labels?: GlobalSearchLabels\n minimumQueryLength?: number\n debounceMs?: number\n}) {\n const inputRef = React.useRef<HTMLInputElement>(null)\n const rootRef = React.useRef<HTMLDivElement>(null)\n const resultsId = React.useId()\n const [query, setQuery] = React.useState('')\n const [open, setOpen] = React.useState(false)\n const [loading, setLoading] = React.useState(false)\n const [result, setResult] = React.useState<GlobalSearchResult | null>(null)\n const [active, setActive] = React.useState(0)\n const hits = React.useMemo(() => result?.groups.flatMap((group) => group.hits) ?? [], [result])\n\n React.useEffect(() => {\n function onKey(event: KeyboardEvent) {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {\n event.preventDefault()\n inputRef.current?.focus()\n inputRef.current?.select()\n setOpen(true)\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [])\n\n React.useEffect(() => {\n function onPointerDown(event: PointerEvent) {\n if (!rootRef.current?.contains(event.target as Node)) setOpen(false)\n }\n document.addEventListener('pointerdown', onPointerDown)\n return () => document.removeEventListener('pointerdown', onPointerDown)\n }, [])\n\n React.useEffect(() => {\n const term = query.trim()\n if (term.length < minimumQueryLength) {\n setResult(null)\n setLoading(false)\n return\n }\n const controller = new AbortController()\n setLoading(true)\n const timeout = window.setTimeout(async () => {\n try {\n const next = await search(term, controller.signal)\n setResult(next)\n setActive(0)\n } catch (error) {\n if ((error as Error).name !== 'AbortError') setResult({ groups: [], total: 0 })\n } finally {\n if (!controller.signal.aborted) setLoading(false)\n }\n }, debounceMs)\n return () => { window.clearTimeout(timeout); controller.abort() }\n }, [debounceMs, minimumQueryLength, query, search])\n\n const go = React.useCallback((hit: GlobalSearchHit | undefined) => {\n if (!hit) return\n setOpen(false)\n setQuery('')\n setResult(null)\n inputRef.current?.blur()\n onNavigate(hit)\n }, [onNavigate])\n\n function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {\n if (event.key === 'Escape') { setOpen(false); inputRef.current?.blur(); return }\n if (!hits.length) return\n if (event.key === 'ArrowDown') { event.preventDefault(); setActive((value) => (value + 1) % hits.length) }\n else if (event.key === 'ArrowUp') { event.preventDefault(); setActive((value) => (value - 1 + hits.length) % hits.length) }\n else if (event.key === 'Enter') { event.preventDefault(); go(hits[active]) }\n }\n\n const showPanel = open && query.trim().length >= minimumQueryLength\n let itemIndex = -1\n return (\n <div ref={rootRef} className={cn('relative', className)}>\n <div className=\"relative\">\n <Search size={15} className=\"pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-fg-subtle\" />\n <input ref={inputRef} value={query} onChange={(event) => { setQuery(event.target.value); setOpen(true) }} onFocus={() => setOpen(true)} onKeyDown={onKeyDown} placeholder={labels.placeholder} aria-label={labels.ariaLabel} role=\"combobox\" aria-expanded={showPanel} aria-controls={resultsId} className=\"h-9 w-full rounded-lg border border-border bg-bg-subtle pr-16 pl-9 text-sm text-fg transition-colors placeholder:text-fg-subtle hover:border-border-strong focus:border-primary focus:bg-surface focus:ring-2 focus:ring-ring/20 focus:outline-none\" />\n <div className=\"absolute top-1/2 right-2.5 flex -translate-y-1/2 items-center gap-1\">\n {loading ? <Loader2 size={14} className=\"animate-spin text-fg-subtle\" /> : query ? <button type=\"button\" onClick={() => { setQuery(''); setResult(null); inputRef.current?.focus() }} className=\"rounded p-0.5 text-fg-subtle hover:text-fg\" aria-label={labels.clear}><X size={14} /></button> : <kbd className=\"hidden rounded border border-border bg-surface px-1.5 py-0.5 font-sans text-[10px] font-medium text-fg-subtle sm:inline\">⌘K</kbd>}\n </div>\n </div>\n {showPanel ? (\n <div id={resultsId} className=\"absolute top-[calc(100%+6px)] left-0 z-50 max-h-[70vh] w-full min-w-88 overflow-y-auto rounded-xl border border-border bg-elevated p-1.5 shadow-lg\" role=\"listbox\">\n {result && result.total > 0 ? <>\n {result.groups.map((group) => <div key={group.id} className=\"mb-1 last:mb-0\"><div className=\"px-2 pt-1.5 pb-1 text-[11px] font-semibold tracking-wider text-fg-subtle uppercase\">{group.label}</div>{group.hits.map((hit) => { itemIndex += 1; const index = itemIndex; const selected = index === active; return <button key={`${hit.type}-${hit.id}`} type=\"button\" role=\"option\" aria-selected={selected} onMouseEnter={() => setActive(index)} onMouseDown={(event) => { event.preventDefault(); go(hit) }} className={cn('flex w-full items-center gap-3 rounded-lg px-2 py-1.5 text-left transition-colors', selected ? 'bg-primary-subtle' : 'hover:bg-surface-hover')}><span className={cn('grid size-7 shrink-0 place-items-center rounded-md', selected ? 'bg-surface text-primary' : 'bg-bg-subtle text-fg-muted')}>{hit.iconKey ? <NavIcon iconKey={hit.iconKey} size={15} /> : <Search size={15} />}</span><span className=\"min-w-0 flex-1\"><span className=\"flex items-center gap-2\"><span className=\"truncate text-sm text-fg\"><Highlight text={hit.title} query={query.trim()} /></span>{hit.badge ? <Badge variant=\"secondary\" className=\"text-[10px]\">{hit.badge}</Badge> : null}</span>{hit.subtitle ? <span className=\"block truncate text-xs text-fg-muted\">{hit.subtitle}</span> : null}</span>{hit.meta ? <span className=\"shrink-0 font-mono text-xs tabular-nums text-fg-muted\">{hit.meta}</span> : null}{selected ? <CornerDownLeft size={13} className=\"shrink-0 text-primary\" /> : null}</button>})}</div>)}\n <div className=\"mt-1 flex items-center justify-between border-t border-border-subtle px-2 py-1.5 text-[11px] text-fg-subtle\"><span><kbd className=\"font-sans\">↑↓</kbd> {labels.navigate} <kbd className=\"font-sans\">↵</kbd> {labels.open} <kbd className=\"font-sans\">esc</kbd> {labels.close}</span><span>{labels.resultCount(result.total)}</span></div>\n </> : loading ? <div className=\"flex items-center gap-2 px-3 py-6 text-sm text-fg-subtle\"><Loader2 size={15} className=\"animate-spin\" />{labels.searching}</div> : <div className=\"px-3 py-6 text-center text-sm text-fg-subtle\">{labels.noMatches(query.trim())}</div>}\n </div>\n ) : null}\n </div>\n )\n}\n"]}
|
|
@@ -19,7 +19,7 @@ export type NavigationConfigEditorProps = {
|
|
|
19
19
|
className?: string;
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
22
|
-
* Controlled tenant navigation editor
|
|
22
|
+
* Controlled tenant navigation editor.
|
|
23
23
|
* Persistence and authorization stay application-owned.
|
|
24
24
|
*/
|
|
25
25
|
export declare function NavigationConfigEditor({ registry, value, onChange, disabled, labels, className, }: NavigationConfigEditorProps): React.JSX.Element;
|
|
@@ -19,7 +19,7 @@ const DEFAULT_LABELS = {
|
|
|
19
19
|
drag: (label) => `Drag to reorder ${label}`,
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
22
|
-
* Controlled tenant navigation editor
|
|
22
|
+
* Controlled tenant navigation editor.
|
|
23
23
|
* Persistence and authorization stay application-owned.
|
|
24
24
|
*/
|
|
25
25
|
export function NavigationConfigEditor({ registry, value, onChange, disabled = false, labels, className, }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-config-editor.js","sourceRoot":"","sources":["../src/navigation-config-editor.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { ChevronDown, ChevronUp, GripVertical, LockKeyhole } from 'lucide-react'\nimport { Reorder, useDragControls } from 'framer-motion'\nimport { Button } from './button'\nimport { SettingsSection } from './settings-layout'\nimport { NavIcon } from './sidebar-nav'\nimport { Switch } from './switch'\nimport {\n reconcileNavigationConfig,\n stampKnownNavigationItems,\n type NavigationItemConfig,\n type NavigationRegistryItem,\n type TenantNavigationConfig,\n} from './navigation-config'\n\nexport type NavigationConfigEditorLabels = {\n title?: string\n description?: string\n visible?: string\n hidden?: string\n required?: string\n moveUp?: (label: string) => string\n moveDown?: (label: string) => string\n drag?: (label: string) => string\n}\n\nexport type NavigationConfigEditorProps = {\n registry: readonly NavigationRegistryItem[]\n value?: TenantNavigationConfig | null\n onChange: (config: TenantNavigationConfig) => void\n disabled?: boolean\n labels?: NavigationConfigEditorLabels\n className?: string\n}\n\nconst DEFAULT_LABELS: Required<NavigationConfigEditorLabels> = {\n title: 'Main navigation',\n description: 'Choose which destinations appear and drag them into the order your team uses.',\n visible: 'Visible',\n hidden: 'Hidden',\n required: 'Required',\n moveUp: (label) => `Move ${label} up`,\n moveDown: (label) => `Move ${label} down`,\n drag: (label) => `Drag to reorder ${label}`,\n}\n\n/**\n * Controlled tenant navigation editor extracted from the production sibling.\n * Persistence and authorization stay application-owned.\n */\nexport function NavigationConfigEditor({\n registry,\n value,\n onChange,\n disabled = false,\n labels,\n className,\n}: NavigationConfigEditorProps) {\n const text = { ...DEFAULT_LABELS, ...labels }\n const config = React.useMemo(\n () => reconcileNavigationConfig(value, registry),\n [registry, value],\n )\n const registryByKey = React.useMemo(\n () => new Map(registry.map((item) => [item.key, item])),\n [registry],\n )\n\n const emit = React.useCallback(\n (items: NavigationItemConfig[]) => {\n onChange(stampKnownNavigationItems({ version: 1, items }, registry))\n },\n [onChange, registry],\n )\n\n const move = React.useCallback(\n (index: number, direction: -1 | 1) => {\n const target = index + direction\n if (target < 0 || target >= config.items.length) return\n const items = [...config.items]\n const [item] = items.splice(index, 1)\n if (!item) return\n items.splice(target, 0, item)\n emit(items)\n },\n [config.items, emit],\n )\n\n return (\n <SettingsSection\n title={text.title}\n description={text.description}\n className={className}\n >\n <Reorder.Group\n axis=\"y\"\n values={config.items}\n onReorder={emit}\n as=\"div\"\n className=\"divide-y divide-border\"\n >\n {config.items.map((item, index) => {\n const registryItem = registryByKey.get(item.key)\n if (!registryItem) return null\n return (\n <NavigationConfigRow\n key={item.key}\n item={item}\n registryItem={registryItem}\n first={index === 0}\n last={index === config.items.length - 1}\n disabled={disabled}\n labels={text}\n onMoveUp={() => move(index, -1)}\n onMoveDown={() => move(index, 1)}\n onVisibilityChange={(visible) =>\n emit(\n config.items.map((candidate) =>\n candidate.key === item.key\n ? { key: candidate.key, ...(visible ? {} : { hidden: true }) }\n : candidate,\n ),\n )\n }\n />\n )\n })}\n </Reorder.Group>\n </SettingsSection>\n )\n}\n\nfunction NavigationConfigRow({\n item,\n registryItem,\n first,\n last,\n disabled,\n labels,\n onMoveUp,\n onMoveDown,\n onVisibilityChange,\n}: {\n item: NavigationItemConfig\n registryItem: NavigationRegistryItem\n first: boolean\n last: boolean\n disabled: boolean\n labels: Required<NavigationConfigEditorLabels>\n onMoveUp: () => void\n onMoveDown: () => void\n onVisibilityChange: (visible: boolean) => void\n}) {\n const dragControls = useDragControls()\n const visible = registryItem.required || !item.hidden\n\n return (\n <Reorder.Item\n value={item}\n dragListener={false}\n dragControls={dragControls}\n as=\"div\"\n className=\"flex items-center gap-3 bg-surface px-4 py-3\"\n >\n <button\n type=\"button\"\n disabled={disabled}\n aria-label={labels.drag(registryItem.label)}\n onPointerDown={(event) => dragControls.start(event)}\n className=\"cursor-grab touch-none rounded-md p-1 text-fg-subtle transition-colors hover:bg-surface-hover hover:text-fg disabled:cursor-not-allowed disabled:opacity-40 active:cursor-grabbing\"\n >\n <GripVertical className=\"size-4\" aria-hidden />\n </button>\n\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg bg-bg-subtle text-fg-muted\">\n <NavIcon iconKey={registryItem.iconKey} />\n </span>\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-medium text-fg\">{registryItem.label}</div>\n {registryItem.description ? (\n <div className=\"truncate text-xs text-fg-muted\">{registryItem.description}</div>\n ) : null}\n </div>\n\n <div className=\"flex shrink-0 items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={disabled || first}\n aria-label={labels.moveUp(registryItem.label)}\n onClick={onMoveUp}\n >\n <ChevronUp className=\"size-4\" aria-hidden />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={disabled || last}\n aria-label={labels.moveDown(registryItem.label)}\n onClick={onMoveDown}\n >\n <ChevronDown className=\"size-4\" aria-hidden />\n </Button>\n </div>\n\n {registryItem.required ? (\n <span\n className=\"flex min-w-20 items-center justify-end gap-1 text-xs font-medium text-fg-muted\"\n title={labels.required}\n >\n <LockKeyhole className=\"size-3.5\" aria-hidden />\n {labels.required}\n </span>\n ) : (\n <label className=\"flex min-w-20 items-center justify-end gap-2 text-xs font-medium text-fg-muted\">\n <span>{visible ? labels.visible : labels.hidden}</span>\n <Switch\n checked={visible}\n disabled={disabled}\n aria-label={`${registryItem.label}: ${visible ? labels.visible : labels.hidden}`}\n onChange={(event) => onVisibilityChange(event.target.checked)}\n />\n </label>\n )}\n </Reorder.Item>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"navigation-config-editor.js","sourceRoot":"","sources":["../src/navigation-config-editor.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { ChevronDown, ChevronUp, GripVertical, LockKeyhole } from 'lucide-react'\nimport { Reorder, useDragControls } from 'framer-motion'\nimport { Button } from './button'\nimport { SettingsSection } from './settings-layout'\nimport { NavIcon } from './sidebar-nav'\nimport { Switch } from './switch'\nimport {\n reconcileNavigationConfig,\n stampKnownNavigationItems,\n type NavigationItemConfig,\n type NavigationRegistryItem,\n type TenantNavigationConfig,\n} from './navigation-config'\n\nexport type NavigationConfigEditorLabels = {\n title?: string\n description?: string\n visible?: string\n hidden?: string\n required?: string\n moveUp?: (label: string) => string\n moveDown?: (label: string) => string\n drag?: (label: string) => string\n}\n\nexport type NavigationConfigEditorProps = {\n registry: readonly NavigationRegistryItem[]\n value?: TenantNavigationConfig | null\n onChange: (config: TenantNavigationConfig) => void\n disabled?: boolean\n labels?: NavigationConfigEditorLabels\n className?: string\n}\n\nconst DEFAULT_LABELS: Required<NavigationConfigEditorLabels> = {\n title: 'Main navigation',\n description: 'Choose which destinations appear and drag them into the order your team uses.',\n visible: 'Visible',\n hidden: 'Hidden',\n required: 'Required',\n moveUp: (label) => `Move ${label} up`,\n moveDown: (label) => `Move ${label} down`,\n drag: (label) => `Drag to reorder ${label}`,\n}\n\n/**\n * Controlled tenant navigation editor.\n * Persistence and authorization stay application-owned.\n */\nexport function NavigationConfigEditor({\n registry,\n value,\n onChange,\n disabled = false,\n labels,\n className,\n}: NavigationConfigEditorProps) {\n const text = { ...DEFAULT_LABELS, ...labels }\n const config = React.useMemo(\n () => reconcileNavigationConfig(value, registry),\n [registry, value],\n )\n const registryByKey = React.useMemo(\n () => new Map(registry.map((item) => [item.key, item])),\n [registry],\n )\n\n const emit = React.useCallback(\n (items: NavigationItemConfig[]) => {\n onChange(stampKnownNavigationItems({ version: 1, items }, registry))\n },\n [onChange, registry],\n )\n\n const move = React.useCallback(\n (index: number, direction: -1 | 1) => {\n const target = index + direction\n if (target < 0 || target >= config.items.length) return\n const items = [...config.items]\n const [item] = items.splice(index, 1)\n if (!item) return\n items.splice(target, 0, item)\n emit(items)\n },\n [config.items, emit],\n )\n\n return (\n <SettingsSection\n title={text.title}\n description={text.description}\n className={className}\n >\n <Reorder.Group\n axis=\"y\"\n values={config.items}\n onReorder={emit}\n as=\"div\"\n className=\"divide-y divide-border\"\n >\n {config.items.map((item, index) => {\n const registryItem = registryByKey.get(item.key)\n if (!registryItem) return null\n return (\n <NavigationConfigRow\n key={item.key}\n item={item}\n registryItem={registryItem}\n first={index === 0}\n last={index === config.items.length - 1}\n disabled={disabled}\n labels={text}\n onMoveUp={() => move(index, -1)}\n onMoveDown={() => move(index, 1)}\n onVisibilityChange={(visible) =>\n emit(\n config.items.map((candidate) =>\n candidate.key === item.key\n ? { key: candidate.key, ...(visible ? {} : { hidden: true }) }\n : candidate,\n ),\n )\n }\n />\n )\n })}\n </Reorder.Group>\n </SettingsSection>\n )\n}\n\nfunction NavigationConfigRow({\n item,\n registryItem,\n first,\n last,\n disabled,\n labels,\n onMoveUp,\n onMoveDown,\n onVisibilityChange,\n}: {\n item: NavigationItemConfig\n registryItem: NavigationRegistryItem\n first: boolean\n last: boolean\n disabled: boolean\n labels: Required<NavigationConfigEditorLabels>\n onMoveUp: () => void\n onMoveDown: () => void\n onVisibilityChange: (visible: boolean) => void\n}) {\n const dragControls = useDragControls()\n const visible = registryItem.required || !item.hidden\n\n return (\n <Reorder.Item\n value={item}\n dragListener={false}\n dragControls={dragControls}\n as=\"div\"\n className=\"flex items-center gap-3 bg-surface px-4 py-3\"\n >\n <button\n type=\"button\"\n disabled={disabled}\n aria-label={labels.drag(registryItem.label)}\n onPointerDown={(event) => dragControls.start(event)}\n className=\"cursor-grab touch-none rounded-md p-1 text-fg-subtle transition-colors hover:bg-surface-hover hover:text-fg disabled:cursor-not-allowed disabled:opacity-40 active:cursor-grabbing\"\n >\n <GripVertical className=\"size-4\" aria-hidden />\n </button>\n\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg bg-bg-subtle text-fg-muted\">\n <NavIcon iconKey={registryItem.iconKey} />\n </span>\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-medium text-fg\">{registryItem.label}</div>\n {registryItem.description ? (\n <div className=\"truncate text-xs text-fg-muted\">{registryItem.description}</div>\n ) : null}\n </div>\n\n <div className=\"flex shrink-0 items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={disabled || first}\n aria-label={labels.moveUp(registryItem.label)}\n onClick={onMoveUp}\n >\n <ChevronUp className=\"size-4\" aria-hidden />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={disabled || last}\n aria-label={labels.moveDown(registryItem.label)}\n onClick={onMoveDown}\n >\n <ChevronDown className=\"size-4\" aria-hidden />\n </Button>\n </div>\n\n {registryItem.required ? (\n <span\n className=\"flex min-w-20 items-center justify-end gap-1 text-xs font-medium text-fg-muted\"\n title={labels.required}\n >\n <LockKeyhole className=\"size-3.5\" aria-hidden />\n {labels.required}\n </span>\n ) : (\n <label className=\"flex min-w-20 items-center justify-end gap-2 text-xs font-medium text-fg-muted\">\n <span>{visible ? labels.visible : labels.hidden}</span>\n <Switch\n checked={visible}\n disabled={disabled}\n aria-label={`${registryItem.label}: ${visible ? labels.visible : labels.hidden}`}\n onChange={(event) => onVisibilityChange(event.target.checked)}\n />\n </label>\n )}\n </Reorder.Item>\n )\n}\n"]}
|
package/package.json
CHANGED
package/search-select.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-select.d.ts","sourceRoot":"","sources":["../src/search-select.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IAC9B,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,EAAE,CAAC,EAAE,MAAM,CAAA;IAEX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,OAAO,EACP,WAAuB,EACvB,iBAA6B,EAC7B,QAAgB,EAChB,SAAiB,EACjB,UAAU,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,MAAc,EACd,OAAe,EACf,aAAa,EACb,UAAoB,EACpB,cAAc,EACd,OAAe,EACf,EAAE,EACF,SAAkB,EAClB,cAA6B,EAC7B,cAA6B,EAC7B,WAAsB,EACtB,UAAoB,GACrB,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"search-select.d.ts","sourceRoot":"","sources":["../src/search-select.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IAC9B,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,EAAE,CAAC,EAAE,MAAM,CAAA;IAEX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,OAAO,EACP,WAAuB,EACvB,iBAA6B,EAC7B,QAAgB,EAChB,SAAiB,EACjB,UAAU,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,MAAc,EACd,OAAe,EACf,aAAa,EACb,UAAoB,EACpB,cAAc,EACd,OAAe,EACf,EAAE,EACF,SAAkB,EAClB,cAA6B,EAC7B,cAA6B,EAC7B,WAAsB,EACtB,UAAoB,GACrB,EAAE,iBAAiB,qBA0VnB"}
|
package/search-select.js
CHANGED
|
@@ -36,16 +36,24 @@ export function SearchSelect({ value, onChange, options, placeholder = 'Select
|
|
|
36
36
|
const below = window.innerHeight - r.bottom - margin;
|
|
37
37
|
const above = r.top - margin;
|
|
38
38
|
const desired = 320;
|
|
39
|
+
const width = Math.min(Math.max(r.width, 208), window.innerWidth - margin * 2);
|
|
40
|
+
const left = Math.min(Math.max(margin, r.left), Math.max(margin, window.innerWidth - width - margin));
|
|
39
41
|
if (below >= Math.min(desired, 180) || below >= above) {
|
|
40
|
-
setPos({
|
|
42
|
+
setPos({
|
|
43
|
+
edge: 'top',
|
|
44
|
+
offset: r.bottom + 6,
|
|
45
|
+
left,
|
|
46
|
+
width,
|
|
47
|
+
maxHeight: Math.max(160, Math.min(desired, Math.max(below, 160))),
|
|
48
|
+
});
|
|
41
49
|
}
|
|
42
50
|
else {
|
|
43
51
|
setPos({
|
|
44
52
|
edge: 'bottom',
|
|
45
53
|
offset: Math.max(margin, window.innerHeight - r.top + 6),
|
|
46
|
-
left
|
|
47
|
-
width
|
|
48
|
-
maxHeight: Math.min(desired, above),
|
|
54
|
+
left,
|
|
55
|
+
width,
|
|
56
|
+
maxHeight: Math.max(160, Math.min(desired, Math.max(above, 160))),
|
|
49
57
|
});
|
|
50
58
|
}
|
|
51
59
|
}
|
package/search-select.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-select.js","sourceRoot":"","sources":["../src/search-select.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\n// SearchSelect — a slick, mobile-first searchable select / typeahead.\n// • Desktop: anchored dropdown (portaled to <body>) with a search box + kbd nav.\n// • Mobile (<lg): an iOS/Android-style bottom sheet with large tap targets.\n// Animated, portal'd sheet, Esc + click-out + scroll-lock, disabled options,\n// optgroup-style headers (via SelectOption.group), hints, clearable, remote mode.\n// Fully tokenized; i18n strings are injectable props with English defaults.\n\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { Check, ChevronDown, Search, X } from 'lucide-react'\nimport { cn } from './utils'\n\nexport type SelectOption = {\n value: string\n label: string\n hint?: string\n disabled?: boolean\n /** Group header label; options sharing a group are batched under it. */\n group?: string\n}\n\nexport type SearchSelectProps = {\n value: string\n onChange: (value: string) => void\n options: SelectOption[]\n placeholder?: string\n searchPlaceholder?: string\n disabled?: boolean\n /** Adds a leading empty option (e.g. \"None\"). */\n clearable?: boolean\n emptyLabel?: string\n /** Title at the top of the mobile bottom sheet. */\n sheetTitle?: string\n ariaLabel?: string\n className?: string\n triggerClassName?: string\n /** Force the search box on/off. Auto: shown when >7 options or any groups. */\n searchable?: boolean\n /** Preserve server ordering + forward the query instead of local filtering. */\n remote?: boolean\n loading?: boolean\n statusMessage?: string\n statusTone?: 'muted' | 'error'\n onSearchChange?: (query: string) => void\n invalid?: boolean\n id?: string\n // Injectable labels (default English).\n noneLabel?: string\n noMatchesLabel?: string\n searchingLabel?: string\n selectLabel?: string\n closeLabel?: string\n}\n\nexport function SearchSelect({\n value,\n onChange,\n options,\n placeholder = 'Select…',\n searchPlaceholder = 'Search…',\n disabled = false,\n clearable = false,\n emptyLabel,\n sheetTitle,\n ariaLabel,\n className,\n triggerClassName,\n searchable,\n remote = false,\n loading = false,\n statusMessage,\n statusTone = 'muted',\n onSearchChange,\n invalid = false,\n id,\n noneLabel = 'None',\n noMatchesLabel = 'No matches',\n searchingLabel = 'Searching…',\n selectLabel = 'Select',\n closeLabel = 'Close',\n}: SearchSelectProps) {\n const [open, setOpen] = React.useState(false)\n const [query, setQuery] = React.useState('')\n const [highlight, setHighlight] = React.useState(0)\n const [isDesktop, setIsDesktop] = React.useState(true)\n const [mounted, setMounted] = React.useState(false)\n const wrapRef = React.useRef<HTMLDivElement>(null)\n const searchRef = React.useRef<HTMLInputElement>(null)\n const triggerRef = React.useRef<HTMLButtonElement>(null)\n const dropRef = React.useRef<HTMLDivElement>(null)\n // The menu is anchored from the edge that touches the trigger: `top` when it\n // opens downward, `bottom` when it opens upward. Anchoring an upward menu by\n // its top would place it a full maxHeight above the trigger no matter how\n // short the list actually is — a three-option menu would float hundreds of\n // pixels clear of the field it belongs to.\n const [pos, setPos] = React.useState<\n | { edge: 'top'; offset: number; left: number; width: number; maxHeight: number }\n | { edge: 'bottom'; offset: number; left: number; width: number; maxHeight: number }\n | null\n >(null)\n function place() {\n const r = triggerRef.current?.getBoundingClientRect()\n if (r) {\n // Collision-aware: open upward when the space below is tight, and clamp\n // the list to the available viewport space either way.\n const margin = 8\n const below = window.innerHeight - r.bottom - margin\n const above = r.top - margin\n const desired = 320\n if (below >= Math.min(desired, 180) || below >= above) {\n setPos({ edge: 'top', offset: r.bottom + 6, left: r.left, width: r.width, maxHeight: Math.min(desired, below) })\n } else {\n setPos({\n edge: 'bottom',\n offset: Math.max(margin, window.innerHeight - r.top + 6),\n left: r.left,\n width: r.width,\n maxHeight: Math.min(desired, above),\n })\n }\n }\n }\n\n React.useEffect(() => {\n setMounted(true)\n const mq = window.matchMedia('(min-width: 1024px)')\n const update = () => setIsDesktop(mq.matches)\n update()\n mq.addEventListener('change', update)\n return () => mq.removeEventListener('change', update)\n }, [])\n\n const allOptions = React.useMemo(\n () => (clearable ? [{ value: '', label: emptyLabel ?? noneLabel }, ...options] : options),\n [clearable, emptyLabel, noneLabel, options],\n )\n const selected = options.find((o) => o.value === value)\n const showEmpty = clearable && value === '' && !!emptyLabel\n const display = selected?.label ?? (showEmpty ? emptyLabel : placeholder)\n const isPlaceholder = !selected && !showEmpty\n\n const showSearch = searchable ?? (allOptions.length > 7 || allOptions.some((o) => o.group))\n\n const filtered = React.useMemo(() => {\n if (remote) return allOptions\n const q = query.trim().toLowerCase()\n if (!q) return allOptions\n return allOptions.filter(\n (o) => o.label.toLowerCase().includes(q) || o.group?.toLowerCase().includes(q),\n )\n }, [allOptions, query, remote])\n\n function firstEnabled(items: SelectOption[], start: number) {\n if (start >= 0 && start < items.length && !items[start]?.disabled) return start\n const fwd = items.findIndex((o) => !o.disabled)\n return fwd === -1 ? 0 : fwd\n }\n\n function openMenu() {\n if (disabled) return\n setQuery('')\n onSearchChange?.('')\n setHighlight(firstEnabled(allOptions, allOptions.findIndex((o) => o.value === value)))\n place()\n setOpen(true)\n setTimeout(() => searchRef.current?.focus(), 60)\n }\n function choose(v: string) {\n onChange(v)\n setOpen(false)\n }\n\n React.useEffect(() => {\n if (!open || !isDesktop) return\n function onDoc(e: MouseEvent) {\n const target = e.target as Node\n if (wrapRef.current?.contains(target) || dropRef.current?.contains(target)) return\n setOpen(false)\n }\n document.addEventListener('mousedown', onDoc)\n return () => document.removeEventListener('mousedown', onDoc)\n }, [open, isDesktop])\n\n React.useEffect(() => {\n if (!open || !isDesktop) return\n place()\n const onMove = () => place()\n window.addEventListener('scroll', onMove, true)\n window.addEventListener('resize', onMove)\n return () => {\n window.removeEventListener('scroll', onMove, true)\n window.removeEventListener('resize', onMove)\n }\n }, [open, isDesktop])\n\n React.useEffect(() => {\n if (!open) return\n const nextEnabled = (from: number, dir: 1 | -1) => {\n let index = from\n while (true) {\n index += dir\n if (index < 0 || index >= filtered.length) return from\n if (!filtered[index]?.disabled) return index\n }\n }\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape') setOpen(false)\n else if (e.key === 'ArrowDown') {\n e.preventDefault()\n setHighlight((h) => nextEnabled(h, 1))\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setHighlight((h) => nextEnabled(h, -1))\n } else if (e.key === 'Enter') {\n e.preventDefault()\n const o = filtered[highlight]\n if (o && !o.disabled) {\n onChange(o.value)\n setOpen(false)\n }\n }\n }\n document.addEventListener('keydown', onKey)\n let restore: (() => void) | undefined\n if (!isDesktop) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n restore = () => {\n document.body.style.overflow = prev\n }\n }\n return () => {\n document.removeEventListener('keydown', onKey)\n restore?.()\n }\n }, [filtered, highlight, isDesktop, onChange, open])\n\n const optionList = (\n <>\n <ul role=\"listbox\" className=\"py-1\">\n {filtered.length === 0 && !loading ? (\n <li role=\"option\" aria-disabled=\"true\" className=\"px-3 py-8 text-center text-sm text-fg-subtle\">\n {noMatchesLabel}\n </li>\n ) : null}\n {filtered.map((o, i) => {\n const active = o.value === value\n const prevGroup = i > 0 ? filtered[i - 1]?.group : undefined\n const header = o.group && o.group !== prevGroup ? o.group : null\n return (\n <li key={o.value || `__opt-${i}`} role=\"presentation\">\n {header ? (\n <div className=\"px-4 pb-1 pt-2.5 text-[11px] font-semibold tracking-wide text-fg-subtle uppercase lg:px-3\">\n {header}\n </div>\n ) : null}\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={active}\n disabled={o.disabled}\n onMouseEnter={() => !o.disabled && setHighlight(i)}\n onClick={() => !o.disabled && choose(o.value)}\n className={cn(\n 'flex h-12 w-full items-center gap-2.5 px-4 text-left text-[15px] transition-colors lg:h-9 lg:px-3 lg:text-sm',\n o.disabled\n ? 'cursor-not-allowed text-fg-subtle'\n : i === highlight\n ? 'bg-primary-subtle'\n : 'active:bg-surface-hover',\n !o.disabled && (active ? 'font-medium text-primary' : 'text-fg'),\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">\n {o.label}\n {o.hint ? <span className=\"ml-1.5 text-xs text-fg-subtle\">{o.hint}</span> : null}\n </span>\n {active ? <Check size={17} className=\"shrink-0 text-primary\" /> : null}\n </button>\n </li>\n )\n })}\n </ul>\n {loading || statusMessage ? (\n <div\n role={statusTone === 'error' ? 'alert' : 'status'}\n className={cn(\n 'border-t border-border px-3 py-2 text-xs',\n statusTone === 'error' ? 'text-danger' : 'text-fg-muted',\n )}\n >\n {loading ? searchingLabel : statusMessage}\n </div>\n ) : null}\n </>\n )\n\n const searchBox = (largeText: boolean) => (\n <div className=\"relative px-3 pt-3\">\n <Search size={16} className=\"absolute left-6 top-1/2 -translate-y-1/2 text-fg-subtle\" />\n <input\n ref={searchRef}\n value={query}\n onChange={(e) => {\n const next = e.target.value\n setQuery(next)\n onSearchChange?.(next)\n setHighlight(0)\n }}\n placeholder={searchPlaceholder}\n aria-label={searchPlaceholder}\n aria-busy={loading || undefined}\n className={cn(\n 'w-full rounded-lg border border-border bg-bg-subtle pl-9 pr-3 outline-none transition focus:border-primary focus:bg-surface focus:ring-2 focus:ring-ring/20',\n largeText ? 'h-11 text-base' : 'h-9 text-base sm:text-sm',\n )}\n />\n </div>\n )\n\n return (\n <div ref={wrapRef} className={cn('relative', className)}>\n <button\n ref={triggerRef}\n type=\"button\"\n id={id}\n onClick={() => (open ? setOpen(false) : openMenu())}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-invalid={invalid || undefined}\n className={cn(\n 'flex h-10 w-full items-center gap-2 rounded-lg border border-border bg-surface px-3 text-left text-sm shadow-sm transition',\n 'focus:border-primary focus:outline-none focus:ring-2 focus:ring-ring/25',\n disabled && 'cursor-not-allowed bg-bg-subtle opacity-70',\n open && 'border-primary ring-2 ring-ring/25',\n invalid && 'border-danger focus:border-danger focus:ring-danger/30',\n triggerClassName,\n )}\n >\n <span className={cn('min-w-0 flex-1 truncate', isPlaceholder ? 'text-fg-subtle' : 'text-fg')}>\n {display}\n </span>\n <ChevronDown\n size={16}\n className={cn('shrink-0 text-fg-subtle transition-transform', open && 'rotate-180')}\n />\n </button>\n\n {mounted && open && isDesktop && pos\n ? createPortal(\n <div\n ref={dropRef}\n data-ui-overlay\n style={{\n position: 'fixed',\n [pos.edge]: pos.offset,\n left: pos.left,\n width: Math.max(pos.width, 208),\n maxHeight: pos.maxHeight,\n }}\n className=\"z-[60] flex flex-col overflow-hidden rounded-xl border border-border bg-elevated shadow-lg\"\n >\n {showSearch ? searchBox(false) : null}\n <div className={cn('min-h-0 flex-1 overflow-y-auto', showSearch && 'mt-1')}>{optionList}</div>\n </div>,\n document.body,\n )\n : null}\n\n {mounted && !isDesktop\n ? createPortal(\n <AnimatePresence>\n {open ? (\n <div data-ui-overlay className=\"fixed inset-0 z-[60]\">\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n className=\"absolute inset-0 bg-overlay/40 backdrop-blur-[2px]\"\n onClick={() => setOpen(false)}\n />\n <motion.div\n initial={{ y: '100%' }}\n animate={{ y: 0 }}\n exit={{ y: '100%' }}\n transition={{ type: 'spring', damping: 34, stiffness: 340, mass: 0.8 }}\n className=\"absolute inset-x-0 bottom-0 flex max-h-[82vh] flex-col rounded-t-2xl border-t border-border bg-surface shadow-lg\"\n >\n <div className=\"flex items-center justify-center pt-2.5\">\n <span className=\"h-1.5 w-10 rounded-full bg-border-strong\" />\n </div>\n <div className=\"flex items-center justify-between px-4 pb-1 pt-2\">\n <span className=\"text-base font-semibold text-fg\">{sheetTitle ?? selectLabel}</span>\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n aria-label={closeLabel}\n className=\"rounded-md p-1.5 text-fg-muted hover:bg-surface-hover\"\n >\n <X size={18} />\n </button>\n </div>\n {showSearch ? searchBox(true) : null}\n <div className=\"mt-1 min-h-0 flex-1 overflow-y-auto pb-[max(0.75rem,env(safe-area-inset-bottom))]\">\n {optionList}\n </div>\n </motion.div>\n </div>\n ) : null}\n </AnimatePresence>,\n document.body,\n )\n : null}\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"search-select.js","sourceRoot":"","sources":["../src/search-select.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\n// SearchSelect — a slick, mobile-first searchable select / typeahead.\n// • Desktop: anchored dropdown (portaled to <body>) with a search box + kbd nav.\n// • Mobile (<lg): an iOS/Android-style bottom sheet with large tap targets.\n// Animated, portal'd sheet, Esc + click-out + scroll-lock, disabled options,\n// optgroup-style headers (via SelectOption.group), hints, clearable, remote mode.\n// Fully tokenized; i18n strings are injectable props with English defaults.\n\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { Check, ChevronDown, Search, X } from 'lucide-react'\nimport { cn } from './utils'\n\nexport type SelectOption = {\n value: string\n label: string\n hint?: string\n disabled?: boolean\n /** Group header label; options sharing a group are batched under it. */\n group?: string\n}\n\nexport type SearchSelectProps = {\n value: string\n onChange: (value: string) => void\n options: SelectOption[]\n placeholder?: string\n searchPlaceholder?: string\n disabled?: boolean\n /** Adds a leading empty option (e.g. \"None\"). */\n clearable?: boolean\n emptyLabel?: string\n /** Title at the top of the mobile bottom sheet. */\n sheetTitle?: string\n ariaLabel?: string\n className?: string\n triggerClassName?: string\n /** Force the search box on/off. Auto: shown when >7 options or any groups. */\n searchable?: boolean\n /** Preserve server ordering + forward the query instead of local filtering. */\n remote?: boolean\n loading?: boolean\n statusMessage?: string\n statusTone?: 'muted' | 'error'\n onSearchChange?: (query: string) => void\n invalid?: boolean\n id?: string\n // Injectable labels (default English).\n noneLabel?: string\n noMatchesLabel?: string\n searchingLabel?: string\n selectLabel?: string\n closeLabel?: string\n}\n\nexport function SearchSelect({\n value,\n onChange,\n options,\n placeholder = 'Select…',\n searchPlaceholder = 'Search…',\n disabled = false,\n clearable = false,\n emptyLabel,\n sheetTitle,\n ariaLabel,\n className,\n triggerClassName,\n searchable,\n remote = false,\n loading = false,\n statusMessage,\n statusTone = 'muted',\n onSearchChange,\n invalid = false,\n id,\n noneLabel = 'None',\n noMatchesLabel = 'No matches',\n searchingLabel = 'Searching…',\n selectLabel = 'Select',\n closeLabel = 'Close',\n}: SearchSelectProps) {\n const [open, setOpen] = React.useState(false)\n const [query, setQuery] = React.useState('')\n const [highlight, setHighlight] = React.useState(0)\n const [isDesktop, setIsDesktop] = React.useState(true)\n const [mounted, setMounted] = React.useState(false)\n const wrapRef = React.useRef<HTMLDivElement>(null)\n const searchRef = React.useRef<HTMLInputElement>(null)\n const triggerRef = React.useRef<HTMLButtonElement>(null)\n const dropRef = React.useRef<HTMLDivElement>(null)\n // The menu is anchored from the edge that touches the trigger: `top` when it\n // opens downward, `bottom` when it opens upward. Anchoring an upward menu by\n // its top would place it a full maxHeight above the trigger no matter how\n // short the list actually is — a three-option menu would float hundreds of\n // pixels clear of the field it belongs to.\n const [pos, setPos] = React.useState<\n | { edge: 'top'; offset: number; left: number; width: number; maxHeight: number }\n | { edge: 'bottom'; offset: number; left: number; width: number; maxHeight: number }\n | null\n >(null)\n function place() {\n const r = triggerRef.current?.getBoundingClientRect()\n if (r) {\n // Collision-aware: open upward when the space below is tight, and clamp\n // the list to the available viewport space either way.\n const margin = 8\n const below = window.innerHeight - r.bottom - margin\n const above = r.top - margin\n const desired = 320\n const width = Math.min(Math.max(r.width, 208), window.innerWidth - margin * 2)\n const left = Math.min(Math.max(margin, r.left), Math.max(margin, window.innerWidth - width - margin))\n if (below >= Math.min(desired, 180) || below >= above) {\n setPos({\n edge: 'top',\n offset: r.bottom + 6,\n left,\n width,\n maxHeight: Math.max(160, Math.min(desired, Math.max(below, 160))),\n })\n } else {\n setPos({\n edge: 'bottom',\n offset: Math.max(margin, window.innerHeight - r.top + 6),\n left,\n width,\n maxHeight: Math.max(160, Math.min(desired, Math.max(above, 160))),\n })\n }\n }\n }\n\n React.useEffect(() => {\n setMounted(true)\n const mq = window.matchMedia('(min-width: 1024px)')\n const update = () => setIsDesktop(mq.matches)\n update()\n mq.addEventListener('change', update)\n return () => mq.removeEventListener('change', update)\n }, [])\n\n const allOptions = React.useMemo(\n () => (clearable ? [{ value: '', label: emptyLabel ?? noneLabel }, ...options] : options),\n [clearable, emptyLabel, noneLabel, options],\n )\n const selected = options.find((o) => o.value === value)\n const showEmpty = clearable && value === '' && !!emptyLabel\n const display = selected?.label ?? (showEmpty ? emptyLabel : placeholder)\n const isPlaceholder = !selected && !showEmpty\n\n const showSearch = searchable ?? (allOptions.length > 7 || allOptions.some((o) => o.group))\n\n const filtered = React.useMemo(() => {\n if (remote) return allOptions\n const q = query.trim().toLowerCase()\n if (!q) return allOptions\n return allOptions.filter(\n (o) => o.label.toLowerCase().includes(q) || o.group?.toLowerCase().includes(q),\n )\n }, [allOptions, query, remote])\n\n function firstEnabled(items: SelectOption[], start: number) {\n if (start >= 0 && start < items.length && !items[start]?.disabled) return start\n const fwd = items.findIndex((o) => !o.disabled)\n return fwd === -1 ? 0 : fwd\n }\n\n function openMenu() {\n if (disabled) return\n setQuery('')\n onSearchChange?.('')\n setHighlight(firstEnabled(allOptions, allOptions.findIndex((o) => o.value === value)))\n place()\n setOpen(true)\n setTimeout(() => searchRef.current?.focus(), 60)\n }\n function choose(v: string) {\n onChange(v)\n setOpen(false)\n }\n\n React.useEffect(() => {\n if (!open || !isDesktop) return\n function onDoc(e: MouseEvent) {\n const target = e.target as Node\n if (wrapRef.current?.contains(target) || dropRef.current?.contains(target)) return\n setOpen(false)\n }\n document.addEventListener('mousedown', onDoc)\n return () => document.removeEventListener('mousedown', onDoc)\n }, [open, isDesktop])\n\n React.useEffect(() => {\n if (!open || !isDesktop) return\n place()\n const onMove = () => place()\n window.addEventListener('scroll', onMove, true)\n window.addEventListener('resize', onMove)\n return () => {\n window.removeEventListener('scroll', onMove, true)\n window.removeEventListener('resize', onMove)\n }\n }, [open, isDesktop])\n\n React.useEffect(() => {\n if (!open) return\n const nextEnabled = (from: number, dir: 1 | -1) => {\n let index = from\n while (true) {\n index += dir\n if (index < 0 || index >= filtered.length) return from\n if (!filtered[index]?.disabled) return index\n }\n }\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape') setOpen(false)\n else if (e.key === 'ArrowDown') {\n e.preventDefault()\n setHighlight((h) => nextEnabled(h, 1))\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setHighlight((h) => nextEnabled(h, -1))\n } else if (e.key === 'Enter') {\n e.preventDefault()\n const o = filtered[highlight]\n if (o && !o.disabled) {\n onChange(o.value)\n setOpen(false)\n }\n }\n }\n document.addEventListener('keydown', onKey)\n let restore: (() => void) | undefined\n if (!isDesktop) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n restore = () => {\n document.body.style.overflow = prev\n }\n }\n return () => {\n document.removeEventListener('keydown', onKey)\n restore?.()\n }\n }, [filtered, highlight, isDesktop, onChange, open])\n\n const optionList = (\n <>\n <ul role=\"listbox\" className=\"py-1\">\n {filtered.length === 0 && !loading ? (\n <li role=\"option\" aria-disabled=\"true\" className=\"px-3 py-8 text-center text-sm text-fg-subtle\">\n {noMatchesLabel}\n </li>\n ) : null}\n {filtered.map((o, i) => {\n const active = o.value === value\n const prevGroup = i > 0 ? filtered[i - 1]?.group : undefined\n const header = o.group && o.group !== prevGroup ? o.group : null\n return (\n <li key={o.value || `__opt-${i}`} role=\"presentation\">\n {header ? (\n <div className=\"px-4 pb-1 pt-2.5 text-[11px] font-semibold tracking-wide text-fg-subtle uppercase lg:px-3\">\n {header}\n </div>\n ) : null}\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={active}\n disabled={o.disabled}\n onMouseEnter={() => !o.disabled && setHighlight(i)}\n onClick={() => !o.disabled && choose(o.value)}\n className={cn(\n 'flex h-12 w-full items-center gap-2.5 px-4 text-left text-[15px] transition-colors lg:h-9 lg:px-3 lg:text-sm',\n o.disabled\n ? 'cursor-not-allowed text-fg-subtle'\n : i === highlight\n ? 'bg-primary-subtle'\n : 'active:bg-surface-hover',\n !o.disabled && (active ? 'font-medium text-primary' : 'text-fg'),\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">\n {o.label}\n {o.hint ? <span className=\"ml-1.5 text-xs text-fg-subtle\">{o.hint}</span> : null}\n </span>\n {active ? <Check size={17} className=\"shrink-0 text-primary\" /> : null}\n </button>\n </li>\n )\n })}\n </ul>\n {loading || statusMessage ? (\n <div\n role={statusTone === 'error' ? 'alert' : 'status'}\n className={cn(\n 'border-t border-border px-3 py-2 text-xs',\n statusTone === 'error' ? 'text-danger' : 'text-fg-muted',\n )}\n >\n {loading ? searchingLabel : statusMessage}\n </div>\n ) : null}\n </>\n )\n\n const searchBox = (largeText: boolean) => (\n <div className=\"relative px-3 pt-3\">\n <Search size={16} className=\"absolute left-6 top-1/2 -translate-y-1/2 text-fg-subtle\" />\n <input\n ref={searchRef}\n value={query}\n onChange={(e) => {\n const next = e.target.value\n setQuery(next)\n onSearchChange?.(next)\n setHighlight(0)\n }}\n placeholder={searchPlaceholder}\n aria-label={searchPlaceholder}\n aria-busy={loading || undefined}\n className={cn(\n 'w-full rounded-lg border border-border bg-bg-subtle pl-9 pr-3 outline-none transition focus:border-primary focus:bg-surface focus:ring-2 focus:ring-ring/20',\n largeText ? 'h-11 text-base' : 'h-9 text-base sm:text-sm',\n )}\n />\n </div>\n )\n\n return (\n <div ref={wrapRef} className={cn('relative', className)}>\n <button\n ref={triggerRef}\n type=\"button\"\n id={id}\n onClick={() => (open ? setOpen(false) : openMenu())}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-invalid={invalid || undefined}\n className={cn(\n 'flex h-10 w-full items-center gap-2 rounded-lg border border-border bg-surface px-3 text-left text-sm shadow-sm transition',\n 'focus:border-primary focus:outline-none focus:ring-2 focus:ring-ring/25',\n disabled && 'cursor-not-allowed bg-bg-subtle opacity-70',\n open && 'border-primary ring-2 ring-ring/25',\n invalid && 'border-danger focus:border-danger focus:ring-danger/30',\n triggerClassName,\n )}\n >\n <span className={cn('min-w-0 flex-1 truncate', isPlaceholder ? 'text-fg-subtle' : 'text-fg')}>\n {display}\n </span>\n <ChevronDown\n size={16}\n className={cn('shrink-0 text-fg-subtle transition-transform', open && 'rotate-180')}\n />\n </button>\n\n {mounted && open && isDesktop && pos\n ? createPortal(\n <div\n ref={dropRef}\n data-ui-overlay\n style={{\n position: 'fixed',\n [pos.edge]: pos.offset,\n left: pos.left,\n width: Math.max(pos.width, 208),\n maxHeight: pos.maxHeight,\n }}\n className=\"z-[60] flex flex-col overflow-hidden rounded-xl border border-border bg-elevated shadow-lg\"\n >\n {showSearch ? searchBox(false) : null}\n <div className={cn('min-h-0 flex-1 overflow-y-auto', showSearch && 'mt-1')}>{optionList}</div>\n </div>,\n document.body,\n )\n : null}\n\n {mounted && !isDesktop\n ? createPortal(\n <AnimatePresence>\n {open ? (\n <div data-ui-overlay className=\"fixed inset-0 z-[60]\">\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n className=\"absolute inset-0 bg-overlay/40 backdrop-blur-[2px]\"\n onClick={() => setOpen(false)}\n />\n <motion.div\n initial={{ y: '100%' }}\n animate={{ y: 0 }}\n exit={{ y: '100%' }}\n transition={{ type: 'spring', damping: 34, stiffness: 340, mass: 0.8 }}\n className=\"absolute inset-x-0 bottom-0 flex max-h-[82vh] flex-col rounded-t-2xl border-t border-border bg-surface shadow-lg\"\n >\n <div className=\"flex items-center justify-center pt-2.5\">\n <span className=\"h-1.5 w-10 rounded-full bg-border-strong\" />\n </div>\n <div className=\"flex items-center justify-between px-4 pb-1 pt-2\">\n <span className=\"text-base font-semibold text-fg\">{sheetTitle ?? selectLabel}</span>\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n aria-label={closeLabel}\n className=\"rounded-md p-1.5 text-fg-muted hover:bg-surface-hover\"\n >\n <X size={18} />\n </button>\n </div>\n {showSearch ? searchBox(true) : null}\n <div className=\"mt-1 min-h-0 flex-1 overflow-y-auto pb-[max(0.75rem,env(safe-area-inset-bottom))]\">\n {optionList}\n </div>\n </motion.div>\n </div>\n ) : null}\n </AnimatePresence>,\n document.body,\n )\n : null}\n </div>\n )\n}\n"]}
|