@cryptlex/web-components 6.7.0 → 6.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/id-search.d.ts +4 -3
- package/dist/components/id-search.js +1 -1
- package/dist/components/id-search.js.map +1 -1
- package/dist/components/sidebar.js +1 -1
- package/dist/components/sidebar.js.map +1 -1
- package/dist/utilities/use-mobile.js +1 -1
- package/dist/utilities/use-mobile.js.map +1 -1
- package/package.json +2 -2
|
@@ -47,7 +47,7 @@ type BaseSearchableResource = {
|
|
|
47
47
|
* @testing
|
|
48
48
|
* - Ensure API client is provided via context; assert keyboard navigation, open/close behavior, and `onBlur` call on popover close.
|
|
49
49
|
*/
|
|
50
|
-
declare function BaseIdSearchInput<T extends BaseSearchableResource, V>({ label, description, errorMessage, requiredIndicator, isDisabled, isInvalid, onBlur, path, onChange, value, renderLabel, accessor, defaultParams, className, emptyOption, ...props }: FormFieldProps & {
|
|
50
|
+
declare function BaseIdSearchInput<T extends BaseSearchableResource, V>({ label, description, errorMessage, requiredIndicator, isDisabled, isInvalid, onBlur, path, onChange, value, renderLabel, accessor, getItemLabel, defaultParams, className, emptyOption, ...props }: FormFieldProps & {
|
|
51
51
|
path: ClientPathsWithMethod<CtxClientType, 'get'>;
|
|
52
52
|
/** Disable interactions. */
|
|
53
53
|
isDisabled?: boolean;
|
|
@@ -55,6 +55,7 @@ declare function BaseIdSearchInput<T extends BaseSearchableResource, V>({ label,
|
|
|
55
55
|
isInvalid?: boolean;
|
|
56
56
|
/** Key used to access an alternate display accessor on item (kept for compatibility). */
|
|
57
57
|
accessor: keyof BaseSearchableResource;
|
|
58
|
+
getItemLabel?: (item: T) => React.ReactNode;
|
|
58
59
|
/** Controlled value. */
|
|
59
60
|
value: V;
|
|
60
61
|
/** Called when popover closes or the field blurs with the current value. */
|
|
@@ -87,7 +88,7 @@ declare function BaseIdSearchInput<T extends BaseSearchableResource, V>({ label,
|
|
|
87
88
|
* />
|
|
88
89
|
*
|
|
89
90
|
*/
|
|
90
|
-
export declare function SingleIdSearchInput<T extends BaseSearchableResource>({ accessor, ...props }: Omit<React.ComponentProps<typeof BaseIdSearchInput<T, string | null>>, 'onSelectionChange' | 'selectionMode' | 'selectedKeys' | 'renderLabel'>): import("react/jsx-runtime").JSX.Element;
|
|
91
|
+
export declare function SingleIdSearchInput<T extends BaseSearchableResource>({ accessor, getItemLabel, ...props }: Omit<React.ComponentProps<typeof BaseIdSearchInput<T, string | null>>, 'onSelectionChange' | 'selectionMode' | 'selectedKeys' | 'renderLabel'>): import("react/jsx-runtime").JSX.Element;
|
|
91
92
|
/**
|
|
92
93
|
* Multi-selection ID search input.
|
|
93
94
|
*
|
|
@@ -108,7 +109,7 @@ export declare function SingleIdSearchInput<T extends BaseSearchableResource>({
|
|
|
108
109
|
* @remarks
|
|
109
110
|
* - The `renderLabel` joins selected item names with commas for compact display.
|
|
110
111
|
*/
|
|
111
|
-
export declare function MultipleIdSearchInput<T extends BaseSearchableResource>({ accessor, ...props }: Omit<React.ComponentProps<typeof BaseIdSearchInput<T, string[]>>, 'renderLabel' | 'onSelectionChange' | 'selectionMode' | 'selectedKeys'>): import("react/jsx-runtime").JSX.Element;
|
|
112
|
+
export declare function MultipleIdSearchInput<T extends BaseSearchableResource>({ accessor, getItemLabel, ...props }: Omit<React.ComponentProps<typeof BaseIdSearchInput<T, string[]>>, 'renderLabel' | 'onSelectionChange' | 'selectionMode' | 'selectedKeys'>): import("react/jsx-runtime").JSX.Element;
|
|
112
113
|
/**
|
|
113
114
|
* Form-integrated single-select ID input (field wrapper).
|
|
114
115
|
*
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{jsx as
|
|
1
|
+
"use client";import{jsx as n,jsxs as M}from"react/jsx-runtime";import{useQuery as K}from"@tanstack/react-query";import{useId as _,useState as D}from"react";import{Select as G,Autocomplete as P}from"react-aria-components";import{Loader as Q}from"./loader.js";import{Menu as k,MenuItem as z}from"./menu.js";import{PopoverTrigger as H}from"./popover.js";import{SearchField as J}from"./searchfield.js";import{useCtxClient as U}from"../utilities/ctx-client.js";import{getEmptyLabel as q,isEmptyValue as W}from"../utilities/empty-option.js";import{getFieldErrorMessage as y}from"../utilities/form.js";import{useFieldContext as T}from"../utilities/form-context.js";import{FormField as X}from"./form.js";import{SelectTrigger as Y,SelectPopover as Z}from"./select.js";import{TagGroup as $,Tag as O}from"./tag-group.js";import"../utilities/theme.js";import"clsx";import"./icons.js";import"@icons-pack/react-simple-icons";import"./list-box.js";import"./button.js";import"class-variance-authority";import"@tanstack/react-form";import"./card.js";function g(t){return t.name??t.displayName??""}function ee(t){return Array.isArray(t)&&t.every(r=>{if(!r||typeof r!="object"||!("id"in r)||typeof r.id!="string")return!1;const e="name"in r&&typeof r.name=="string",o="displayName"in r&&typeof r.displayName=="string";return e||o})}function E({label:t,description:r,errorMessage:e,requiredIndicator:o,isDisabled:h,isInvalid:a,onBlur:l,path:s,onChange:m,value:i,renderLabel:d,accessor:f,getItemLabel:S,defaultParams:p,className:V,emptyOption:A,...b}){if(s==="/v3/me")throw Error('Path "/v3/me" is not supported with IdSearch since it is not a searchable resource.');const L=_(),v=b.id||L,w=U(),C=S??g,B=c=>{const u=C(c);return typeof u=="string"?u:g(c)},[I,R]=D(""),{data:N,isError:x,isFetching:F,error:j}=K({queryKey:["get",s,I],queryFn:async()=>{const u=(await w.GET(s,{params:{query:{search:I,...p?.query,limit:50,page:1},path:{...p?.path}}})).data;return u&&ee(u)?u:[]}});return n("div",{className:"group form-field","data-invalid":a?"":void 0,children:n(X,{label:t,description:r,errorMessage:e,requiredIndicator:o,htmlFor:v,children:n(G,{isInvalid:a,children:M(H,{onOpenChange:c=>{c||l?.(i)},children:[n(Y,{id:v,isDisabled:h,className:V??"w-full",children:d(i,N,A)}),n(Z,{placement:"bottom start",children:M(P,{inputValue:I,onInputChange:R,children:[n(J,{className:"p-2",autoFocus:!0}),F&&n("div",{className:"p-input",children:n(Q,{className:"mx-auto"})}),!F&&!x&&n(k,{...b,className:"max-h-48",items:N,renderEmptyState:()=>n("div",{className:"body-sm p-2",children:"No results found."}),children:c=>{const u=c[f]??c.id;return n(z,{id:u,textValue:B(c),children:C(c)},u)}}),x&&n("div",{className:"text-destructive p-icon body-sm",children:j.message})]})})]})})})})}function re({accessor:t,getItemLabel:r,...e}){const o=e.value?[e.value]:[],h=r??g;return n(E,{accessor:t,getItemLabel:r,selectedKeys:o,onSelectionChange:a=>{const l=Array.from(a).filter(s=>typeof s=="string")[0];e.onChange(l)},renderLabel:(a,l,s)=>{if(s&&W(a))return q(s);const m=l?.find(i=>(i[t]??i.id)===a);return m?h(m):""},selectionMode:"single",...e})}function te({accessor:t,getItemLabel:r,...e}){const o=e.value,h=r??g;return n(E,{...e,accessor:t,getItemLabel:r,selectedKeys:o,onSelectionChange:a=>{const l=Array.from(a).filter(s=>typeof s=="string");e.onChange(l.length>0?l:[])},selectionMode:"multiple",className:"w-full h-auto items-start",renderLabel:(a,l,s)=>{if(s&&(!a||a.length===0))return q(s);const m=a??[];return n($,{onRemove:i=>e.onChange(m.filter(d=>!i.has(d))),items:m.map(i=>{const d=l?.find(p=>(p[t]??p.id)===i),f=d?h(d):i,S=typeof f=="string"?f:d?g(d):i;return{id:i,name:f,textValue:S}}),children:i=>n(O,{textValue:i.textValue,children:i.name},i.id)})}})}function qe({isDisabled:t,...r}){const e=T({disabled:t});return n(re,{requiredIndicator:e.isRequired,isDisabled:t||e.isSubmitting,value:e.state.value,onBlur:o=>e.handleBlur(),onChange:o=>e.handleChange(o),isInvalid:!!y(e),errorMessage:y(e),...r})}function Te({isDisabled:t,...r}){const e=T({disabled:t});return n(te,{requiredIndicator:e.isRequired,isDisabled:t||e.isSubmitting,value:e.state.value,onBlur:o=>e.handleBlur(),onChange:o=>e.handleChange(o),isInvalid:!!y(e),errorMessage:y(e),...r})}export{te as MultipleIdSearchInput,re as SingleIdSearchInput,Te as TfMultipleIdSearchInput,qe as TfSingleIdSearchInput};
|
|
2
2
|
//# sourceMappingURL=id-search.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id-search.js","sources":["../../lib/components/id-search.tsx"],"sourcesContent":["'use client';\nimport { useQuery } from '@tanstack/react-query';\nimport { useId, useState } from 'react';\nimport { Select as AriaSelect, Autocomplete } from 'react-aria-components';\n\nimport type { ClientPathsWithMethod } from 'openapi-fetch';\nimport { Loader } from '../components/loader';\nimport { Menu, MenuItem } from '../components/menu';\nimport { PopoverTrigger } from '../components/popover';\nimport { SearchField } from '../components/searchfield';\nimport { useCtxClient, type CtxClientType } from '../utilities/ctx-client';\nimport { getEmptyLabel, isEmptyValue, type EmptyOption } from '../utilities/empty-option';\nimport { getFieldErrorMessage } from '../utilities/form';\nimport { useFieldContext } from '../utilities/form-context';\nimport { FormField, type FormFieldProps } from './form';\nimport { SelectPopover, SelectTrigger } from './select';\nimport { Tag, TagGroup } from './tag-group';\n\n/**\n * Minimal resource shape used by the ID search inputs.\n * `id` is required; the label comes from `name` or `displayName`.\n *\n * @example\n * const user: BaseSearchableResource = { id: 'u_123', name: 'Nabeel Farooq' };\n * const product: BaseSearchableResource = { id: 'p_123', displayName: 'My Product' };\n */\ntype BaseSearchableResource = {\n /** Unique identifier used as the input value. */\n id: string;\n /** Human-readable label shown to users. */\n name?: string;\n displayName?: string;\n};\n\n/** Resources commonly expose either `name` or `displayName` as their human-readable label. */\nfunction getResourceLabel(item: BaseSearchableResource): string {\n return item.name ?? item.displayName ?? '';\n}\n\n/**\n * Type guard to check if a value is an array of searchable resources\n */\nfunction isSearchableResourceArray<T extends BaseSearchableResource>(value: unknown): value is T[] {\n return (\n Array.isArray(value) &&\n value.every(item => {\n if (!item || typeof item !== 'object' || !('id' in item) || typeof item.id !== 'string') return false;\n const hasName = 'name' in item && typeof item.name === 'string';\n const hasDisplayName = 'displayName' in item && typeof item.displayName === 'string';\n return hasName || hasDisplayName;\n })\n );\n}\n\n/**\n * - Generic, accessible ID-search building block.\n * - Search (powered by react-query)\n * - Renders an accessible Autocomplete + Menu listbox\n * - Exposes a controlled `value`/`onChange` contract so callers (and wrappers) can manage state\n *\n * @template T - resource type extending `BaseSearchableResource` (must have `id`; label is `name` or `displayName`)\n * @template V - controlled value type (e.g. `string` for single-select or `string[]` for multi-select)\n *\n * @param props - props object (see inline property JSDoc for the most important fields)\n *\n * @remarks\n * - Search is automatically handled based on the `resource` prop using the API client from context\n * - When the popover closes, `onBlur` (if provided) is called with the current `value`.\n * - `renderLabel` must convert `value` to a readable string for the control button.\n * - `defaultParams` can be used to pass additional query parameters to the search endpoint\n *\n * @example\n * <BaseIdSearchInput\n * label=\"Owner\"\n * resource=\"user\"\n * value={ownerId}\n * onChange={setOwnerId}\n * renderLabel={(v, data) => data?.find(d => d.id === v)?.name ?? v}\n * />\n *\n * @testing\n * - Ensure API client is provided via context; assert keyboard navigation, open/close behavior, and `onBlur` call on popover close.\n */\nfunction BaseIdSearchInput<T extends BaseSearchableResource, V>({\n label,\n description,\n errorMessage,\n requiredIndicator,\n isDisabled,\n isInvalid,\n onBlur,\n path,\n onChange,\n value,\n renderLabel,\n accessor,\n defaultParams,\n className,\n emptyOption,\n ...props\n}: FormFieldProps & {\n path: ClientPathsWithMethod<CtxClientType, 'get'>;\n /** Disable interactions. */\n isDisabled?: boolean;\n /** Whether the field is invalid. */\n isInvalid?: boolean;\n /** Key used to access an alternate display accessor on item (kept for compatibility). */\n accessor: keyof BaseSearchableResource;\n /** Controlled value. */\n value: V;\n /** Called when popover closes or the field blurs with the current value. */\n onBlur?: (v: V) => void;\n /** Controlled change handler. */\n onChange: (v: V) => void;\n /** Render a human-readable label for the current value using the latest data. */\n renderLabel: (v: V, data: T[] | undefined, emptyOption?: EmptyOption) => string | React.ReactNode;\n /** Default parameters to include in the request. This is useful when using /v3/users?role='admin' or /v3/organizations/ID/user-groups */\n defaultParams?: Record<'path' | 'query', any>;\n /** Optional className to customize the trigger button styling. */\n className?: string;\n /* Optional empty option to show when value is empty. */\n emptyOption?: EmptyOption;\n} & Omit<React.ComponentProps<typeof Menu>, 'items' | 'className'>) {\n if (path === '/v3/me') {\n throw Error('Path \"/v3/me\" is not supported with IdSearch since it is not a searchable resource.');\n }\n\n const generatedId = useId();\n const fieldId = props.id || generatedId;\n const client = useCtxClient();\n\n const [search, _setSearch] = useState('');\n const { data, isError, isFetching, error } = useQuery({\n // This is the schema that openapi-react-query follows as of 0.5.1\n queryKey: ['get', path, search],\n queryFn: async (): Promise<T[]> => {\n const result = await client.GET(path, {\n params: {\n query: {\n search,\n ...defaultParams?.query,\n limit: 50,\n page: 1,\n },\n path: {\n ...defaultParams?.path,\n },\n },\n });\n\n // Type narrowing: result.data is inferred as never due to complex union types\n // We use unknown to bypass this and then validate with our type guard\n const responseData: unknown = result.data;\n if (responseData && isSearchableResourceArray<T>(responseData)) {\n return responseData;\n }\n return [];\n },\n });\n\n return (\n <div className=\"group form-field\" data-invalid={isInvalid ? '' : undefined}>\n <FormField {...{ label, description, errorMessage, requiredIndicator, htmlFor: fieldId }}>\n <AriaSelect isInvalid={isInvalid}>\n <PopoverTrigger\n onOpenChange={o => {\n if (!o) {\n // searchInputRef.current?.focus();\n onBlur?.(value);\n }\n }}\n >\n <SelectTrigger id={fieldId} isDisabled={isDisabled} className={className ?? 'w-full'}>\n {renderLabel(value, data, emptyOption)}\n </SelectTrigger>\n <SelectPopover placement=\"bottom start\">\n <Autocomplete inputValue={search} onInputChange={_setSearch}>\n <SearchField className={'p-2'} autoFocus />\n {isFetching && (\n <div className=\"p-input\">\n <Loader className=\"mx-auto\" />\n </div>\n )}\n {!isFetching && !isError && (\n <Menu\n {...props}\n className={'max-h-48'}\n items={data}\n renderEmptyState={() => <div className=\"body-sm p-2\">No results found.</div>}\n >\n {item => {\n const itemId = item[accessor] ?? item.id;\n return (\n <MenuItem key={itemId} id={itemId}>\n {getResourceLabel(item)}\n </MenuItem>\n );\n }}\n </Menu>\n )}\n {isError && <div className=\"text-destructive p-icon body-sm\">{error.message}</div>}\n </Autocomplete>\n </SelectPopover>\n </PopoverTrigger>\n </AriaSelect>\n </FormField>\n </div>\n );\n}\n\n/**\n * Single-selection ID search input.\n *\n * Thin, typed wrapper around `BaseIdSearchInput` specialized for the very common single-ID case.\n * Adapts the internal selection events into `onChange(id?: string)` and renders the selected label.\n *\n * @template T - resource type (extends BaseSearchableResource)\n *\n * @param props - Inherits `BaseIdSearchInput` props but uses `string[]` value type.\n *\n * @example\n * <SingleIdSearchInput\n * label=\"Reporter\"\n * value={reporterId}\n * onChange={setReporterId}\n * />\n *\n */\nexport function SingleIdSearchInput<T extends BaseSearchableResource>({\n accessor,\n ...props\n}: Omit<\n React.ComponentProps<typeof BaseIdSearchInput<T, string | null>>,\n 'onSelectionChange' | 'selectionMode' | 'selectedKeys' | 'renderLabel'\n>) {\n const selectedKeys = props.value ? [props.value] : [];\n\n return (\n <BaseIdSearchInput\n accessor={accessor}\n selectedKeys={selectedKeys}\n onSelectionChange={e => {\n const val = Array.from(e).filter(v => typeof v === 'string')[0];\n props.onChange(val);\n }}\n renderLabel={(v, d, emptyOption) => {\n // If value is empty and emptyOption is set, show the empty label\n if (emptyOption && isEmptyValue(v)) {\n return getEmptyLabel(emptyOption);\n }\n // Match on the same field the value was derived from (accessor), not always `id`.\n const match = d?.find(di => (di[accessor] ?? di.id) === v);\n return match ? getResourceLabel(match) : '';\n }}\n selectionMode=\"single\"\n {...props}\n />\n );\n}\n\n/**\n * Multi-selection ID search input.\n *\n * Thin wrapper around `BaseIdSearchInput` for the multiple-ID (`string[]`) case.\n * Adapts internal selection events into `onChange(ids: string[])`.\n *\n * @template T - resource type (extends BaseSearchableResource)\n *\n * @param props - Inherits `BaseIdSearchInput` props but uses `string[]` value type.\n *\n * @example\n * <MultipleIdSearchInput\n * label=\"Reviewers\"\n * value={reviewerIds}\n * onChange={setReviewerIds}\n * />\n *\n * @remarks\n * - The `renderLabel` joins selected item names with commas for compact display.\n */\nexport function MultipleIdSearchInput<T extends BaseSearchableResource>({\n accessor,\n ...props\n}: Omit<\n React.ComponentProps<typeof BaseIdSearchInput<T, string[]>>,\n 'renderLabel' | 'onSelectionChange' | 'selectionMode' | 'selectedKeys'\n>) {\n const selectedKeys = props.value;\n\n return (\n <BaseIdSearchInput\n {...props}\n accessor={accessor}\n selectedKeys={selectedKeys}\n onSelectionChange={e => {\n const vals = Array.from(e).filter(v => typeof v === 'string');\n props.onChange(vals.length > 0 ? vals : []);\n }}\n selectionMode=\"multiple\"\n className=\"w-full h-auto items-start\"\n renderLabel={(v, d, emptyOption) => {\n // If array is empty and emptyOption is set, show the empty label\n if (emptyOption && (!v || v.length === 0)) {\n return getEmptyLabel(emptyOption);\n }\n\n const values = v ?? [];\n return (\n <TagGroup\n onRemove={keys => props.onChange(values.filter(value => !keys.has(value)))}\n items={values.map(value => {\n // Match on the same field the value was derived from (accessor), not always `id`.\n const match = d?.find(di => (di[accessor] ?? di.id) === value);\n const name = match ? getResourceLabel(match) : value;\n return {\n id: value,\n name: name,\n };\n })}\n >\n {item => <Tag key={item.id}>{getResourceLabel(item)}</Tag>}\n </TagGroup>\n );\n }}\n />\n );\n}\n\n/**\n * Form-integrated single-select ID input (field wrapper).\n *\n * Integrates `SingleIdSearchInput` into the form system using `useFieldContext`.\n * - wires `value`, `onChange`, and `onBlur`\n * - disables the control while the form is submitting\n * - surfaces field-level error messages\n *\n * @example\n * <TfSingleIdSearchInput name=\"ownerId\" label=\"Owner\" />\n \n */\nexport function TfSingleIdSearchInput({\n isDisabled,\n ...props\n}: Omit<React.ComponentProps<typeof SingleIdSearchInput>, 'value' | 'onChange' | 'onBlur'>) {\n const field = useFieldContext<string | null>({ disabled: isDisabled });\n return (\n <SingleIdSearchInput\n requiredIndicator={field.isRequired}\n isDisabled={isDisabled || field.isSubmitting}\n value={field.state.value}\n onBlur={_ => field.handleBlur()}\n onChange={e => field.handleChange(e)}\n isInvalid={!!getFieldErrorMessage(field)}\n errorMessage={getFieldErrorMessage(field)}\n {...props}\n />\n );\n}\n\n/**\n * Form-integrated multi-select ID input (field wrapper).\n *\n * Integrates `MultipleIdSearchInput` into the form system using `useFieldContext`.\n * - wires `value`, `onChange`, and `onBlur`\n * - disables the control while the form is submitting\n * - surfaces field-level error messages\n *\n * @example\n * <TfMultipleIdSearchInput name=\"reviewerIds\" label=\"Reviewers\" />\n *\n */\nexport function TfMultipleIdSearchInput({\n isDisabled,\n ...props\n}: Omit<React.ComponentProps<typeof MultipleIdSearchInput>, 'value' | 'onChange'>) {\n const field = useFieldContext<string[]>({ disabled: isDisabled });\n return (\n <MultipleIdSearchInput\n requiredIndicator={field.isRequired}\n isDisabled={isDisabled || field.isSubmitting}\n value={field.state.value}\n onBlur={_ => field.handleBlur()}\n onChange={e => field.handleChange(e)}\n isInvalid={!!getFieldErrorMessage(field)}\n errorMessage={getFieldErrorMessage(field)}\n {...props}\n />\n );\n}\n"],"names":["getResourceLabel","item","isSearchableResourceArray","value","hasName","hasDisplayName","BaseIdSearchInput","label","description","errorMessage","requiredIndicator","isDisabled","isInvalid","onBlur","path","onChange","renderLabel","accessor","defaultParams","className","emptyOption","props","generatedId","useId","fieldId","client","useCtxClient","search","_setSearch","useState","data","isError","isFetching","error","useQuery","responseData","jsx","FormField","AriaSelect","jsxs","PopoverTrigger","o","SelectTrigger","SelectPopover","Autocomplete","SearchField","Loader","Menu","itemId","MenuItem","SingleIdSearchInput","selectedKeys","e","val","v","d","isEmptyValue","getEmptyLabel","match","di","MultipleIdSearchInput","vals","values","TagGroup","keys","name","Tag","TfSingleIdSearchInput","field","useFieldContext","_","getFieldErrorMessage","TfMultipleIdSearchInput"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"id-search.js","sources":["../../lib/components/id-search.tsx"],"sourcesContent":["'use client';\nimport { useQuery } from '@tanstack/react-query';\nimport { useId, useState } from 'react';\nimport { Select as AriaSelect, Autocomplete } from 'react-aria-components';\n\nimport type { ClientPathsWithMethod } from 'openapi-fetch';\nimport { Loader } from '../components/loader';\nimport { Menu, MenuItem } from '../components/menu';\nimport { PopoverTrigger } from '../components/popover';\nimport { SearchField } from '../components/searchfield';\nimport { useCtxClient, type CtxClientType } from '../utilities/ctx-client';\nimport { getEmptyLabel, isEmptyValue, type EmptyOption } from '../utilities/empty-option';\nimport { getFieldErrorMessage } from '../utilities/form';\nimport { useFieldContext } from '../utilities/form-context';\nimport { FormField, type FormFieldProps } from './form';\nimport { SelectPopover, SelectTrigger } from './select';\nimport { Tag, TagGroup } from './tag-group';\n\n/**\n * Minimal resource shape used by the ID search inputs.\n * `id` is required; the label comes from `name` or `displayName`.\n *\n * @example\n * const user: BaseSearchableResource = { id: 'u_123', name: 'Nabeel Farooq' };\n * const product: BaseSearchableResource = { id: 'p_123', displayName: 'My Product' };\n */\ntype BaseSearchableResource = {\n /** Unique identifier used as the input value. */\n id: string;\n /** Human-readable label shown to users. */\n name?: string;\n displayName?: string;\n};\n\n/** Resources commonly expose either `name` or `displayName` as their human-readable label. */\nfunction getResourceLabel(item: BaseSearchableResource): string {\n return item.name ?? item.displayName ?? '';\n}\n\n/**\n * Type guard to check if a value is an array of searchable resources\n */\nfunction isSearchableResourceArray<T extends BaseSearchableResource>(value: unknown): value is T[] {\n return (\n Array.isArray(value) &&\n value.every(item => {\n if (!item || typeof item !== 'object' || !('id' in item) || typeof item.id !== 'string') return false;\n const hasName = 'name' in item && typeof item.name === 'string';\n const hasDisplayName = 'displayName' in item && typeof item.displayName === 'string';\n return hasName || hasDisplayName;\n })\n );\n}\n\n/**\n * - Generic, accessible ID-search building block.\n * - Search (powered by react-query)\n * - Renders an accessible Autocomplete + Menu listbox\n * - Exposes a controlled `value`/`onChange` contract so callers (and wrappers) can manage state\n *\n * @template T - resource type extending `BaseSearchableResource` (must have `id`; label is `name` or `displayName`)\n * @template V - controlled value type (e.g. `string` for single-select or `string[]` for multi-select)\n *\n * @param props - props object (see inline property JSDoc for the most important fields)\n *\n * @remarks\n * - Search is automatically handled based on the `resource` prop using the API client from context\n * - When the popover closes, `onBlur` (if provided) is called with the current `value`.\n * - `renderLabel` must convert `value` to a readable string for the control button.\n * - `defaultParams` can be used to pass additional query parameters to the search endpoint\n *\n * @example\n * <BaseIdSearchInput\n * label=\"Owner\"\n * resource=\"user\"\n * value={ownerId}\n * onChange={setOwnerId}\n * renderLabel={(v, data) => data?.find(d => d.id === v)?.name ?? v}\n * />\n *\n * @testing\n * - Ensure API client is provided via context; assert keyboard navigation, open/close behavior, and `onBlur` call on popover close.\n */\nfunction BaseIdSearchInput<T extends BaseSearchableResource, V>({\n label,\n description,\n errorMessage,\n requiredIndicator,\n isDisabled,\n isInvalid,\n onBlur,\n path,\n onChange,\n value,\n renderLabel,\n accessor,\n getItemLabel,\n defaultParams,\n className,\n emptyOption,\n ...props\n}: FormFieldProps & {\n path: ClientPathsWithMethod<CtxClientType, 'get'>;\n /** Disable interactions. */\n isDisabled?: boolean;\n /** Whether the field is invalid. */\n isInvalid?: boolean;\n /** Key used to access an alternate display accessor on item (kept for compatibility). */\n accessor: keyof BaseSearchableResource;\n getItemLabel?: (item: T) => React.ReactNode;\n /** Controlled value. */\n value: V;\n /** Called when popover closes or the field blurs with the current value. */\n onBlur?: (v: V) => void;\n /** Controlled change handler. */\n onChange: (v: V) => void;\n /** Render a human-readable label for the current value using the latest data. */\n renderLabel: (v: V, data: T[] | undefined, emptyOption?: EmptyOption) => string | React.ReactNode;\n /** Default parameters to include in the request. This is useful when using /v3/users?role='admin' or /v3/organizations/ID/user-groups */\n defaultParams?: Record<'path' | 'query', any>;\n /** Optional className to customize the trigger button styling. */\n className?: string;\n /* Optional empty option to show when value is empty. */\n emptyOption?: EmptyOption;\n} & Omit<React.ComponentProps<typeof Menu>, 'items' | 'className'>) {\n if (path === '/v3/me') {\n throw Error('Path \"/v3/me\" is not supported with IdSearch since it is not a searchable resource.');\n }\n\n const generatedId = useId();\n const fieldId = props.id || generatedId;\n const client = useCtxClient();\n const itemLabel = getItemLabel ?? getResourceLabel;\n // Typeahead/search needs a plain string; fall back to the plain name when the label is a styled ReactNode.\n const itemTextValue = (item: T): string => {\n const label = itemLabel(item);\n return typeof label === 'string' ? label : getResourceLabel(item);\n };\n\n const [search, _setSearch] = useState('');\n const { data, isError, isFetching, error } = useQuery({\n // This is the schema that openapi-react-query follows as of 0.5.1\n queryKey: ['get', path, search],\n queryFn: async (): Promise<T[]> => {\n const result = await client.GET(path, {\n params: {\n query: {\n search,\n ...defaultParams?.query,\n limit: 50,\n page: 1,\n },\n path: {\n ...defaultParams?.path,\n },\n },\n });\n\n // Type narrowing: result.data is inferred as never due to complex union types\n // We use unknown to bypass this and then validate with our type guard\n const responseData: unknown = result.data;\n if (responseData && isSearchableResourceArray<T>(responseData)) {\n return responseData;\n }\n return [];\n },\n });\n\n return (\n <div className=\"group form-field\" data-invalid={isInvalid ? '' : undefined}>\n <FormField {...{ label, description, errorMessage, requiredIndicator, htmlFor: fieldId }}>\n <AriaSelect isInvalid={isInvalid}>\n <PopoverTrigger\n onOpenChange={o => {\n if (!o) {\n // searchInputRef.current?.focus();\n onBlur?.(value);\n }\n }}\n >\n <SelectTrigger id={fieldId} isDisabled={isDisabled} className={className ?? 'w-full'}>\n {renderLabel(value, data, emptyOption)}\n </SelectTrigger>\n <SelectPopover placement=\"bottom start\">\n <Autocomplete inputValue={search} onInputChange={_setSearch}>\n <SearchField className={'p-2'} autoFocus />\n {isFetching && (\n <div className=\"p-input\">\n <Loader className=\"mx-auto\" />\n </div>\n )}\n {!isFetching && !isError && (\n <Menu\n {...props}\n className={'max-h-48'}\n items={data}\n renderEmptyState={() => <div className=\"body-sm p-2\">No results found.</div>}\n >\n {item => {\n const itemId = item[accessor] ?? item.id;\n return (\n <MenuItem key={itemId} id={itemId} textValue={itemTextValue(item)}>\n {itemLabel(item)}\n </MenuItem>\n );\n }}\n </Menu>\n )}\n {isError && <div className=\"text-destructive p-icon body-sm\">{error.message}</div>}\n </Autocomplete>\n </SelectPopover>\n </PopoverTrigger>\n </AriaSelect>\n </FormField>\n </div>\n );\n}\n\n/**\n * Single-selection ID search input.\n *\n * Thin, typed wrapper around `BaseIdSearchInput` specialized for the very common single-ID case.\n * Adapts the internal selection events into `onChange(id?: string)` and renders the selected label.\n *\n * @template T - resource type (extends BaseSearchableResource)\n *\n * @param props - Inherits `BaseIdSearchInput` props but uses `string[]` value type.\n *\n * @example\n * <SingleIdSearchInput\n * label=\"Reporter\"\n * value={reporterId}\n * onChange={setReporterId}\n * />\n *\n */\nexport function SingleIdSearchInput<T extends BaseSearchableResource>({\n accessor,\n getItemLabel,\n ...props\n}: Omit<\n React.ComponentProps<typeof BaseIdSearchInput<T, string | null>>,\n 'onSelectionChange' | 'selectionMode' | 'selectedKeys' | 'renderLabel'\n>) {\n const selectedKeys = props.value ? [props.value] : [];\n const itemLabel = getItemLabel ?? getResourceLabel;\n\n return (\n <BaseIdSearchInput\n accessor={accessor}\n getItemLabel={getItemLabel}\n selectedKeys={selectedKeys}\n onSelectionChange={e => {\n const val = Array.from(e).filter(v => typeof v === 'string')[0];\n props.onChange(val);\n }}\n renderLabel={(v, d, emptyOption) => {\n // If value is empty and emptyOption is set, show the empty label\n if (emptyOption && isEmptyValue(v)) {\n return getEmptyLabel(emptyOption);\n }\n // Match on the same field the value was derived from (accessor), not always `id`.\n const match = d?.find(di => (di[accessor] ?? di.id) === v);\n return match ? itemLabel(match) : '';\n }}\n selectionMode=\"single\"\n {...props}\n />\n );\n}\n\n/**\n * Multi-selection ID search input.\n *\n * Thin wrapper around `BaseIdSearchInput` for the multiple-ID (`string[]`) case.\n * Adapts internal selection events into `onChange(ids: string[])`.\n *\n * @template T - resource type (extends BaseSearchableResource)\n *\n * @param props - Inherits `BaseIdSearchInput` props but uses `string[]` value type.\n *\n * @example\n * <MultipleIdSearchInput\n * label=\"Reviewers\"\n * value={reviewerIds}\n * onChange={setReviewerIds}\n * />\n *\n * @remarks\n * - The `renderLabel` joins selected item names with commas for compact display.\n */\nexport function MultipleIdSearchInput<T extends BaseSearchableResource>({\n accessor,\n getItemLabel,\n ...props\n}: Omit<\n React.ComponentProps<typeof BaseIdSearchInput<T, string[]>>,\n 'renderLabel' | 'onSelectionChange' | 'selectionMode' | 'selectedKeys'\n>) {\n const selectedKeys = props.value;\n const itemLabel = getItemLabel ?? getResourceLabel;\n\n return (\n <BaseIdSearchInput\n {...props}\n accessor={accessor}\n getItemLabel={getItemLabel}\n selectedKeys={selectedKeys}\n onSelectionChange={e => {\n const vals = Array.from(e).filter(v => typeof v === 'string');\n props.onChange(vals.length > 0 ? vals : []);\n }}\n selectionMode=\"multiple\"\n className=\"w-full h-auto items-start\"\n renderLabel={(v, d, emptyOption) => {\n // If array is empty and emptyOption is set, show the empty label\n if (emptyOption && (!v || v.length === 0)) {\n return getEmptyLabel(emptyOption);\n }\n\n const values = v ?? [];\n return (\n <TagGroup\n onRemove={keys => props.onChange(values.filter(value => !keys.has(value)))}\n items={values.map(value => {\n // Match on the same field the value was derived from (accessor), not always `id`.\n const match = d?.find(di => (di[accessor] ?? di.id) === value);\n const name = match ? itemLabel(match) : value;\n const textValue = typeof name === 'string' ? name : match ? getResourceLabel(match) : value;\n return {\n id: value,\n name,\n textValue,\n };\n })}\n >\n {item => (\n <Tag key={item.id} textValue={item.textValue}>\n {item.name}\n </Tag>\n )}\n </TagGroup>\n );\n }}\n />\n );\n}\n\n/**\n * Form-integrated single-select ID input (field wrapper).\n *\n * Integrates `SingleIdSearchInput` into the form system using `useFieldContext`.\n * - wires `value`, `onChange`, and `onBlur`\n * - disables the control while the form is submitting\n * - surfaces field-level error messages\n *\n * @example\n * <TfSingleIdSearchInput name=\"ownerId\" label=\"Owner\" />\n \n */\nexport function TfSingleIdSearchInput({\n isDisabled,\n ...props\n}: Omit<React.ComponentProps<typeof SingleIdSearchInput>, 'value' | 'onChange' | 'onBlur'>) {\n const field = useFieldContext<string | null>({ disabled: isDisabled });\n return (\n <SingleIdSearchInput\n requiredIndicator={field.isRequired}\n isDisabled={isDisabled || field.isSubmitting}\n value={field.state.value}\n onBlur={_ => field.handleBlur()}\n onChange={e => field.handleChange(e)}\n isInvalid={!!getFieldErrorMessage(field)}\n errorMessage={getFieldErrorMessage(field)}\n {...props}\n />\n );\n}\n\n/**\n * Form-integrated multi-select ID input (field wrapper).\n *\n * Integrates `MultipleIdSearchInput` into the form system using `useFieldContext`.\n * - wires `value`, `onChange`, and `onBlur`\n * - disables the control while the form is submitting\n * - surfaces field-level error messages\n *\n * @example\n * <TfMultipleIdSearchInput name=\"reviewerIds\" label=\"Reviewers\" />\n *\n */\nexport function TfMultipleIdSearchInput({\n isDisabled,\n ...props\n}: Omit<React.ComponentProps<typeof MultipleIdSearchInput>, 'value' | 'onChange'>) {\n const field = useFieldContext<string[]>({ disabled: isDisabled });\n return (\n <MultipleIdSearchInput\n requiredIndicator={field.isRequired}\n isDisabled={isDisabled || field.isSubmitting}\n value={field.state.value}\n onBlur={_ => field.handleBlur()}\n onChange={e => field.handleChange(e)}\n isInvalid={!!getFieldErrorMessage(field)}\n errorMessage={getFieldErrorMessage(field)}\n {...props}\n />\n );\n}\n"],"names":["getResourceLabel","item","isSearchableResourceArray","value","hasName","hasDisplayName","BaseIdSearchInput","label","description","errorMessage","requiredIndicator","isDisabled","isInvalid","onBlur","path","onChange","renderLabel","accessor","getItemLabel","defaultParams","className","emptyOption","props","generatedId","useId","fieldId","client","useCtxClient","itemLabel","itemTextValue","search","_setSearch","useState","data","isError","isFetching","error","useQuery","responseData","jsx","FormField","AriaSelect","jsxs","PopoverTrigger","o","SelectTrigger","SelectPopover","Autocomplete","SearchField","Loader","Menu","itemId","MenuItem","SingleIdSearchInput","selectedKeys","e","val","v","d","isEmptyValue","getEmptyLabel","match","di","MultipleIdSearchInput","vals","values","TagGroup","keys","name","textValue","Tag","TfSingleIdSearchInput","field","useFieldContext","_","getFieldErrorMessage","TfMultipleIdSearchInput"],"mappings":"ygCAmCA,SAASA,EAAiBC,EAAsC,CAC5D,OAAOA,EAAK,MAAQA,EAAK,aAAe,EAC5C,CAKA,SAASC,GAA4DC,EAA8B,CAC/F,OACI,MAAM,QAAQA,CAAK,GACnBA,EAAM,MAAMF,GAAQ,CAChB,GAAI,CAACA,GAAQ,OAAOA,GAAS,UAAY,EAAE,OAAQA,IAAS,OAAOA,EAAK,IAAO,SAAU,MAAO,GAChG,MAAMG,EAAU,SAAUH,GAAQ,OAAOA,EAAK,MAAS,SACjDI,EAAiB,gBAAiBJ,GAAQ,OAAOA,EAAK,aAAgB,SAC5E,OAAOG,GAAWC,CACtB,CAAC,CAET,CA+BA,SAASC,EAAuD,CAC5D,MAAAC,EACA,YAAAC,EACA,aAAAC,EACA,kBAAAC,EACA,WAAAC,EACA,UAAAC,EACA,OAAAC,EACA,KAAAC,EACA,SAAAC,EACA,MAAAZ,EACA,YAAAa,EACA,SAAAC,EACA,aAAAC,EACA,cAAAC,EACA,UAAAC,EACA,YAAAC,EACA,GAAGC,CACP,EAuBoE,CAChE,GAAIR,IAAS,SACT,MAAM,MAAM,qFAAqF,EAGrG,MAAMS,EAAcC,EAAA,EACdC,EAAUH,EAAM,IAAMC,EACtBG,EAASC,EAAA,EACTC,EAAYV,GAAgBlB,EAE5B6B,EAAiB5B,GAAoB,CACvC,MAAMM,EAAQqB,EAAU3B,CAAI,EAC5B,OAAO,OAAOM,GAAU,SAAWA,EAAQP,EAAiBC,CAAI,CACpE,EAEM,CAAC6B,EAAQC,CAAU,EAAIC,EAAS,EAAE,EAClC,CAAE,KAAAC,EAAM,QAAAC,EAAS,WAAAC,EAAY,MAAAC,CAAA,EAAUC,EAAS,CAElD,SAAU,CAAC,MAAOvB,EAAMgB,CAAM,EAC9B,QAAS,SAA0B,CAiB/B,MAAMQ,GAhBS,MAAMZ,EAAO,IAAIZ,EAAM,CAClC,OAAQ,CACJ,MAAO,CACH,OAAAgB,EACA,GAAGX,GAAe,MAClB,MAAO,GACP,KAAM,CAAA,EAEV,KAAM,CACF,GAAGA,GAAe,IAAA,CACtB,CACJ,CACH,GAIoC,KACrC,OAAImB,GAAgBpC,GAA6BoC,CAAY,EAClDA,EAEJ,CAAA,CACX,CAAA,CACH,EAED,OACIC,EAAC,OAAI,UAAU,mBAAmB,eAAc3B,EAAY,GAAK,OAC7D,SAAA2B,EAACC,EAAA,CAAgB,MAAAjC,EAAO,YAAAC,EAAa,aAAAC,EAAc,kBAAAC,EAAmB,QAASe,EAC3E,SAAAc,EAACE,EAAA,CAAW,UAAA7B,EACR,SAAA8B,EAACC,EAAA,CACG,aAAcC,GAAK,CACVA,GAED/B,IAASV,CAAK,CAEtB,EAEA,SAAA,CAAAoC,EAACM,EAAA,CAAc,GAAIpB,EAAS,WAAAd,EAAwB,UAAWS,GAAa,SACvE,SAAAJ,EAAYb,EAAO8B,EAAMZ,CAAW,CAAA,CACzC,EACAkB,EAACO,GAAc,UAAU,eACrB,WAACC,EAAA,CAAa,WAAYjB,EAAQ,cAAeC,EAC7C,SAAA,CAAAQ,EAACS,EAAA,CAAY,UAAW,MAAO,UAAS,GAAC,EACxCb,KACI,MAAA,CAAI,UAAU,UACX,SAAAI,EAACU,EAAA,CAAO,UAAU,SAAA,CAAU,CAAA,CAChC,EAEH,CAACd,GAAc,CAACD,GACbK,EAACW,EAAA,CACI,GAAG5B,EACJ,UAAW,WACX,MAAOW,EACP,iBAAkB,IAAMM,EAAC,MAAA,CAAI,UAAU,cAAc,SAAA,oBAAiB,EAErE,SAAAtC,GAAQ,CACL,MAAMkD,EAASlD,EAAKgB,CAAQ,GAAKhB,EAAK,GACtC,OACIsC,EAACa,EAAA,CAAsB,GAAID,EAAQ,UAAWtB,EAAc5B,CAAI,EAC3D,SAAA2B,EAAU3B,CAAI,CAAA,EADJkD,CAEf,CAER,CAAA,CAAA,EAGPjB,GAAWK,EAAC,MAAA,CAAI,UAAU,kCAAmC,WAAM,OAAA,CAAQ,CAAA,CAAA,CAChF,CAAA,CACJ,CAAA,CAAA,CAAA,CACJ,CACJ,EACJ,EACJ,CAER,CAoBO,SAASc,GAAsD,CAClE,SAAApC,EACA,aAAAC,EACA,GAAGI,CACP,EAGG,CACC,MAAMgC,EAAehC,EAAM,MAAQ,CAACA,EAAM,KAAK,EAAI,CAAA,EAC7CM,EAAYV,GAAgBlB,EAElC,OACIuC,EAACjC,EAAA,CACG,SAAAW,EACA,aAAAC,EACA,aAAAoC,EACA,kBAAmBC,GAAK,CACpB,MAAMC,EAAM,MAAM,KAAKD,CAAC,EAAE,OAAOE,GAAK,OAAOA,GAAM,QAAQ,EAAE,CAAC,EAC9DnC,EAAM,SAASkC,CAAG,CACtB,EACA,YAAa,CAACC,EAAGC,EAAGrC,IAAgB,CAEhC,GAAIA,GAAesC,EAAaF,CAAC,EAC7B,OAAOG,EAAcvC,CAAW,EAGpC,MAAMwC,EAAQH,GAAG,KAAKI,IAAOA,EAAG7C,CAAQ,GAAK6C,EAAG,MAAQL,CAAC,EACzD,OAAOI,EAAQjC,EAAUiC,CAAK,EAAI,EACtC,EACA,cAAc,SACb,GAAGvC,CAAA,CAAA,CAGhB,CAsBO,SAASyC,GAAwD,CACpE,SAAA9C,EACA,aAAAC,EACA,GAAGI,CACP,EAGG,CACC,MAAMgC,EAAehC,EAAM,MACrBM,EAAYV,GAAgBlB,EAElC,OACIuC,EAACjC,EAAA,CACI,GAAGgB,EACJ,SAAAL,EACA,aAAAC,EACA,aAAAoC,EACA,kBAAmBC,GAAK,CACpB,MAAMS,EAAO,MAAM,KAAKT,CAAC,EAAE,OAAOE,GAAK,OAAOA,GAAM,QAAQ,EAC5DnC,EAAM,SAAS0C,EAAK,OAAS,EAAIA,EAAO,EAAE,CAC9C,EACA,cAAc,WACd,UAAU,4BACV,YAAa,CAACP,EAAGC,EAAGrC,IAAgB,CAEhC,GAAIA,IAAgB,CAACoC,GAAKA,EAAE,SAAW,GACnC,OAAOG,EAAcvC,CAAW,EAGpC,MAAM4C,EAASR,GAAK,CAAA,EACpB,OACIlB,EAAC2B,EAAA,CACG,SAAUC,GAAQ7C,EAAM,SAAS2C,EAAO,OAAO9D,GAAS,CAACgE,EAAK,IAAIhE,CAAK,CAAC,CAAC,EACzE,MAAO8D,EAAO,IAAI9D,GAAS,CAEvB,MAAM0D,EAAQH,GAAG,KAAKI,IAAOA,EAAG7C,CAAQ,GAAK6C,EAAG,MAAQ3D,CAAK,EACvDiE,EAAOP,EAAQjC,EAAUiC,CAAK,EAAI1D,EAClCkE,EAAY,OAAOD,GAAS,SAAWA,EAAOP,EAAQ7D,EAAiB6D,CAAK,EAAI1D,EACtF,MAAO,CACH,GAAIA,EACJ,KAAAiE,EACA,UAAAC,CAAA,CAER,CAAC,EAEA,SAAApE,KACIqE,EAAA,CAAkB,UAAWrE,EAAK,UAC9B,SAAAA,EAAK,IAAA,EADAA,EAAK,EAEf,CAAA,CAAA,CAIhB,CAAA,CAAA,CAGZ,CAcO,SAASsE,GAAsB,CAClC,WAAA5D,EACA,GAAGW,CACP,EAA4F,CACxF,MAAMkD,EAAQC,EAA+B,CAAE,SAAU9D,EAAY,EACrE,OACI4B,EAACc,GAAA,CACG,kBAAmBmB,EAAM,WACzB,WAAY7D,GAAc6D,EAAM,aAChC,MAAOA,EAAM,MAAM,MACnB,OAAQE,GAAKF,EAAM,WAAA,EACnB,SAAUjB,GAAKiB,EAAM,aAAajB,CAAC,EACnC,UAAW,CAAC,CAACoB,EAAqBH,CAAK,EACvC,aAAcG,EAAqBH,CAAK,EACvC,GAAGlD,CAAA,CAAA,CAGhB,CAcO,SAASsD,GAAwB,CACpC,WAAAjE,EACA,GAAGW,CACP,EAAmF,CAC/E,MAAMkD,EAAQC,EAA0B,CAAE,SAAU9D,EAAY,EAChE,OACI4B,EAACwB,GAAA,CACG,kBAAmBS,EAAM,WACzB,WAAY7D,GAAc6D,EAAM,aAChC,MAAOA,EAAM,MAAM,MACnB,OAAQE,GAAKF,EAAM,WAAA,EACnB,SAAUjB,GAAKiB,EAAM,aAAajB,CAAC,EACnC,UAAW,CAAC,CAACoB,EAAqBH,CAAK,EACvC,aAAcG,EAAqBH,CAAK,EACvC,GAAGlD,CAAA,CAAA,CAGhB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{jsx as t,jsxs as h}from"react/jsx-runtime";import{createContext as _,useState as w,useCallback as y,useEffect as j,useMemo as
|
|
1
|
+
"use client";import{jsx as t,jsxs as h}from"react/jsx-runtime";import{createContext as _,useState as w,useCallback as y,useEffect as j,useMemo as B,use as D}from"react";import{Button as S}from"./button.js";import{DialogTrigger as I,DialogOverlay as T,DialogContent as z}from"./dialog.js";import{TooltipTrigger as E,Tooltip as G}from"./tooltip.js";import{classNames as i}from"../utilities/theme.js";import{useIsMobile as K}from"../utilities/use-mobile.js";import{IcMenu as L,IcLeft as R,IcRight as A}from"./icons.js";import"class-variance-authority";import"react-aria-components";import"./loader.js";import"clsx";import"@icons-pack/react-simple-icons";const H="/",N=_(null);function v(){const e=D(N);if(!e)throw new Error("useSidebar must be used within a SidebarProvider.");return e}function te({defaultOpen:e=!0,open:a,onOpenChange:r,className:n,style:o,children:s,...c}){const d=K(),[u,p]=w(!1),[C,k]=w(e),f=a??C,m=y(l=>{const b=typeof l=="function"?l(f):l;r?r(b):k(b)},[r,f]),g=y(()=>d?p(l=>!l):m(l=>!l),[d,m,p]);j(()=>{const l=b=>{b.key===H&&(b.metaKey||b.ctrlKey)&&(b.preventDefault(),g())};return window.addEventListener("keydown",l),()=>window.removeEventListener("keydown",l)},[g]);const x=f?"expanded":"collapsed",O=B(()=>({state:x,open:f,setOpen:m,isMobile:d,openMobile:u,setOpenMobile:p,toggleSidebar:g}),[x,f,m,d,u,p,g]);return t(N.Provider,{value:O,children:t("div",{className:i("group/sidebar-wrapper flex w-full has-[[data-variant=inset]]:bg-surface-primary",n),...c,children:s})})}function ie({side:e="left",variant:a="sidebar",collapsible:r="icon",className:n,children:o,...s}){const{isMobile:c,state:d,openMobile:u,setOpenMobile:p}=v();return r==="none"?t("div",{className:i("flex h-full w-(--sidebar-width) flex-col bg-surface-primary text-text-primary",n),...s,children:o}):c?t(I,{isOpen:u,onOpenChange:p,...s,children:t(T,{children:t(z,{"data-sidebar":"sidebar","data-mobile":"true",className:"w-(--sidebar-width) max-h-svh overflow-auto p-0 [&>button]:hidden border-r! border-stroke-primary! bg-surface-primary!",side:"left",closeButton:!1,children:()=>t("div",{className:"flex h-full w-full flex-col",children:o})})})}):h("div",{className:"group peer hidden text-text-primary md:block","data-state":d,"data-collapsible":d==="collapsed"?r:"","data-variant":a,"data-side":e,children:[t("div",{className:i("relative w-(--sidebar-width) bg-surface-primary transition-[width] duration-200 ease-linear","group-data-[collapsible=offcanvas]:w-0","group-data-[side=right]:rotate-180",a==="floating"?"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]":"group-data-[collapsible=icon]:w-(--sidebar-width-icon)")}),t("div",{className:i("absolute z-10 hidden w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",e==="left"?"left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]":"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",a==="floating"?"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]":"group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",n),...s,children:t("div",{"data-sidebar":"sidebar",className:"flex h-full w-full flex-col bg-surface-primary group-data-[variant=floating]:border group-data-[variant=floating]:border-border",children:o})})]})}function F({className:e,onClick:a,...r}){const{toggleSidebar:n,open:o,isMobile:s}=v();return t(S,{"data-sidebar":"trigger",variant:"neutral",size:"icon-sm",className:i(e,"!rounded-full p-icon"),onClick:c=>{a?.(c),n()},"aria-label":"trigger",...r,children:s?t(L,{}):o?t(R,{}):t(A,{})})}function re({className:e,...a}){return t("div",{"data-sidebar":"content",className:i("flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overflow-x-hidden",e),...a})}function ne({className:e,...a}){return t("div",{"data-sidebar":"group",className:i("group/section relative flex w-full min-w-0 flex-col px-3 group-data-[collapsible=icon]:items-center",e),...a})}function oe({className:e,...a}){return t("div",{"data-sidebar":"group-label",className:i("flex h-input shrink-0 items-center overflow-hidden px-2 text-xs select-none font-medium uppercase text-muted-foreground outline-none ring-ring transition-[height,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-icon [&>svg]:shrink-0","group-data-[collapsible=icon]:pointer-events-none group-data-[collapsible=icon]:!h-0 group-data-[collapsible=icon]:min-h-0 group-data-[collapsible=icon]:opacity-0",e),...a})}function le({className:e,...a}){return t("div",{"data-sidebar":"group-content",className:i("w-full body-sm",e),...a})}function se({className:e,...a}){return t("ul",{"data-sidebar":"menu",className:i("flex w-full min-w-0 flex-col gap-px",e),...a})}function de({className:e,...a}){return t("li",{"data-sidebar":"menu-item",className:i("group/menu-item relative list-none group-data-[collapsible=icon]:[&>*:not([data-sidebar=menu-button])]:hidden",e),...a})}function ue({className:e,...a}){return t("button",{"data-sidebar":"menu-action",className:i("absolute top-0 right-0 btn btn-ghost h-input","group-data-[collapsible=icon]:hidden",e),...a})}function ce({className:e,number:a,...r}){return t("div",{"data-sidebar":"menu-badge",className:i("absolute pointer-events-none top-0 right-0 font-light h-input select-none flex items-center justify-center body-sm tabular-nums","group-data-[collapsible=icon]:hidden",e),...r,children:new Intl.NumberFormat(navigator.language,{useGrouping:!0}).format(a)})}function be({className:e,...a}){return t("ul",{"data-sidebar":"menu-sub",className:i("ms-2 flex min-w-0 flex-col","group-data-[collapsible=icon]:hidden",e),...a})}function pe({className:e,...a}){return t("li",{className:i("list-none ms-0",e),...a})}const M="btn-tab relative rounded-md border-0 justify-start w-full group-data-[collapsible=icon]:justify-center";function fe({isActive:e=!1,className:a,children:r,icon:n,leading:o,...s}){const{isMobile:c,state:d}=v(),u=!c&&d==="collapsed";return h(E,{isDisabled:!u,children:[h(S,{size:u?"icon":void 0,"data-sidebar":"menu-button",...e&&{"data-selected":!0},className:i("peer/menu-button group/active group-has-[[data-sidebar=menu-action]]/menu-item:pr-input",M,a),...s,children:[t("span",{"aria-hidden":!0,className:"absolute inset-y-2 -left-1 w-[7px] rounded-full bg-primary opacity-0 group-data-[selected=true]/active:opacity-100"}),o!==void 0?o:n?t(n,{}):null,!u&&r]}),t(G,{placement:"right",children:t("span",{className:"flex items-center gap-2",children:r})})]})}function me({isActive:e,className:a,...r}){return t("button",{"data-sidebar":"menu-sub-button","data-active":e,className:i(M,a),...r})}function ge({className:e,logo:a,children:r,showTrigger:n=!0,...o}){return h("div",{"data-sidebar":"header",className:i("flex items-center h-header select-none shrink-0 border-b px-2 gap-2 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0",e),...o,children:[a&&t("div",{className:"flex items-center justify-center size-input shrink-0",children:a}),r&&t("div",{className:"flex-1 min-w-0 group-data-[collapsible=icon]:hidden",children:r}),n&&t(F,{className:"hidden bg-surface-primary lg:flex lg:absolute lg:-right-4 lg:z-50"})]})}function he({className:e,...a}){return t("div",{"data-sidebar":"footer",className:i("mt-auto flex shrink-0 select-none items-center gap-2 border-t py-2 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0",e),...a})}function ve({className:e,...a}){return t("div",{className:i(e,"flex flex-col flex-1 overflow-auto"),...a})}function xe({className:e,...a}){return t("div",{className:i(e,"flex-1 overflow-auto"),...a})}function we({className:e,...a}){return t("nav",{className:i("flex items-center h-header bg-surface-primary p-2 border-b",e),...a})}export{ie as Sidebar,re as SidebarContent,N as SidebarContext,he as SidebarFooter,ne as SidebarGroup,le as SidebarGroupContent,oe as SidebarGroupLabel,ge as SidebarHeader,se as SidebarMenu,ue as SidebarMenuAction,ce as SidebarMenuBadge,fe as SidebarMenuButton,de as SidebarMenuItem,be as SidebarMenuSub,me as SidebarMenuSubButton,pe as SidebarMenuSubItem,ve as SidebarOutlet,xe as SidebarOutletContent,we as SidebarOutletHeader,te as SidebarProvider,F as SidebarTrigger,M as sidebarMenuButtonClasses,v as useSidebar};
|
|
2
2
|
//# sourceMappingURL=sidebar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar.js","sources":["../../lib/components/sidebar.tsx"],"sourcesContent":["'use client';\nimport { createContext, use, useCallback, useEffect, useMemo, useState } from 'react';\nimport { Button } from '../components/button';\nimport { DialogContent, DialogOverlay, DialogTrigger } from '../components/dialog';\nimport { Tooltip, TooltipTrigger } from '../components/tooltip';\nimport { classNames } from '../utilities/theme';\nimport { useIsMobile } from '../utilities/use-mobile';\nimport { IcLeft, IcMenu, IcRight, type CtxIcon } from './icons';\n\nconst SIDEBAR_KEYBOARD_SHORTCUT = '/';\n\ntype SidebarContextProps = {\n state: 'expanded' | 'collapsed';\n open: boolean;\n setOpen: (open: boolean) => void;\n openMobile: boolean;\n setOpenMobile: (open: boolean) => void;\n isMobile: boolean;\n toggleSidebar: () => void;\n};\n\nexport const SidebarContext = createContext<SidebarContextProps | null>(null);\n\n/**\n * Get the current sidebar state and controls. Must be inside SidebarProvider.\n * @throws When used outside of SidebarProvider\n */\nexport function useSidebar() {\n const context = use(SidebarContext);\n if (!context) {\n throw new Error('useSidebar must be used within a SidebarProvider.');\n }\n\n return context;\n}\n\n/**\n * Wraps your app layout and manages sidebar state. Handles keyboard shortcuts (Cmd/Ctrl + /)\n * and mobile vs desktop behavior.\n *\n * @example\n * <SidebarProvider>\n * <Sidebar>...</Sidebar>\n * <SidebarOutlet>\n * <main>Your app content</main>\n * </SidebarOutlet>\n * </SidebarProvider>\n */\nexport function SidebarProvider({\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n className,\n style,\n children,\n ...props\n}: React.ComponentProps<'div'> & {\n /** Initial open state. Defaults to true */\n defaultOpen?: boolean;\n /** Control the open state yourself */\n open?: boolean;\n /** Callback when sidebar opens/closes */\n onOpenChange?: (open: boolean) => void;\n}) {\n const isMobile = useIsMobile();\n const [openMobile, setOpenMobile] = useState(false);\n\n // This is the internal state of the sidebar.\n // We use openProp and setOpenProp for control from outside the component.\n const [_open, _setOpen] = useState(defaultOpen);\n const open = openProp ?? _open;\n const setOpen = useCallback(\n (value: boolean | ((value: boolean) => boolean)) => {\n const openState = typeof value === 'function' ? value(open) : value;\n if (setOpenProp) {\n setOpenProp(openState);\n } else {\n _setOpen(openState);\n }\n },\n [setOpenProp, open]\n );\n\n // Helper to toggle the sidebar.\n const toggleSidebar = useCallback(() => {\n return isMobile ? setOpenMobile(open => !open) : setOpen(open => !open);\n }, [isMobile, setOpen, setOpenMobile]);\n\n // Adds a keyboard shortcut to toggle the sidebar.\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {\n event.preventDefault();\n toggleSidebar();\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleSidebar]);\n\n // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n // This makes it easier to style the sidebar with Tailwind classes.\n const state = open ? 'expanded' : 'collapsed';\n\n const contextValue = useMemo<SidebarContextProps>(\n () => ({\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n }),\n [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]\n );\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <div\n className={classNames(\n 'group/sidebar-wrapper flex w-full has-[[data-variant=inset]]:bg-surface-primary',\n className\n )}\n {...props}\n >\n {children}\n </div>\n </SidebarContext.Provider>\n );\n}\n\n/**\n * The actual sidebar panel. On mobile it becomes a modal dialog.\n */\nexport function Sidebar({\n side = 'left',\n variant = 'sidebar',\n collapsible = 'icon',\n className,\n children,\n ...props\n}: React.ComponentProps<'div'> & {\n /** Where to dock the sidebar. Default: left */\n side?: 'left' | 'right';\n /** Style variant. Default: sidebar */\n variant?: 'sidebar' | 'floating';\n /** How it collapses. Default: offcanvas */\n collapsible?: 'offcanvas' | 'icon' | 'none';\n}) {\n const { isMobile, state, openMobile, setOpenMobile } = useSidebar();\n\n if (collapsible === 'none') {\n return (\n <div\n className={classNames(\n 'flex h-full w-(--sidebar-width) flex-col bg-surface-primary text-text-primary',\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n }\n\n if (isMobile) {\n return (\n <DialogTrigger isOpen={openMobile} onOpenChange={setOpenMobile} {...props}>\n <DialogOverlay>\n <DialogContent\n data-sidebar=\"sidebar\"\n data-mobile=\"true\"\n className=\"w-(--sidebar-width) max-h-svh overflow-auto p-0 [&>button]:hidden border-r! border-stroke-primary! bg-surface-primary!\"\n side=\"left\"\n >\n {() => <div className=\"flex h-full w-full flex-col\">{children}</div>}\n </DialogContent>\n </DialogOverlay>\n </DialogTrigger>\n );\n }\n\n return (\n <div\n className=\"group peer hidden text-text-primary md:block\"\n data-state={state}\n data-collapsible={state === 'collapsed' ? collapsible : ''}\n data-variant={variant}\n data-side={side}\n >\n {/* This is what handles the sidebar gap on desktop */}\n <div\n className={classNames(\n 'relative w-(--sidebar-width) bg-surface-primary transition-[width] duration-200 ease-linear',\n 'group-data-[collapsible=offcanvas]:w-0',\n 'group-data-[side=right]:rotate-180',\n variant === 'floating'\n ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]'\n : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)'\n )}\n />\n <div\n className={classNames(\n 'absolute z-10 hidden w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex',\n side === 'left'\n ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'\n : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',\n // Adjust the padding for floating variant.\n variant === 'floating'\n ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]'\n : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',\n className\n )}\n {...props}\n >\n <div\n data-sidebar=\"sidebar\"\n className=\"flex h-full w-full flex-col bg-surface-primary group-data-[variant=floating]:border group-data-[variant=floating]:border-border\"\n >\n {children}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * Button that toggles the sidebar. Shows arrow icons.\n */\nexport function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<typeof Button>) {\n const { toggleSidebar, open, isMobile } = useSidebar();\n\n return (\n <Button\n data-sidebar=\"trigger\"\n variant=\"neutral\"\n size=\"icon-sm\"\n className={classNames(className, '!rounded-full p-icon')}\n onClick={event => {\n onClick?.(event);\n toggleSidebar();\n }}\n aria-label=\"trigger\"\n {...props}\n >\n {/* Use transition to rotate a single icon */}\n {isMobile ? <IcMenu /> : open ? <IcLeft /> : <IcRight />}\n </Button>\n );\n}\n\n/**\n * Scrollable container for sidebar content\n */\nexport function SidebarContent({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-sidebar=\"content\"\n className={classNames('flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overflow-x-hidden', className)}\n {...props}\n />\n );\n}\n\n/**\n * Groups related menu items together\n */\nexport function SidebarGroup({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-sidebar=\"group\"\n className={classNames(\n 'group/section relative flex w-full min-w-0 flex-col px-3 group-data-[collapsible=icon]:items-center',\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Label for a group.\n */\nexport function SidebarGroupLabel({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-sidebar=\"group-label\"\n className={classNames(\n 'flex h-input shrink-0 items-center overflow-hidden px-2 text-xs select-none font-medium uppercase text-muted-foreground outline-none ring-ring transition-[height,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-icon [&>svg]:shrink-0',\n 'group-data-[collapsible=icon]:pointer-events-none group-data-[collapsible=icon]:!h-0 group-data-[collapsible=icon]:min-h-0 group-data-[collapsible=icon]:opacity-0',\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Content wrapper for a group\n */\nexport function SidebarGroupContent({ className, ...props }: React.ComponentProps<'div'>) {\n return <div data-sidebar=\"group-content\" className={classNames('w-full body-sm', className)} {...props} />;\n}\n\n/**\n * Container for menu items\n */\nexport function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {\n return (\n <ul data-sidebar=\"menu\" className={classNames('flex w-full min-w-0 flex-col gap-px', className)} {...props} />\n );\n}\n\n/**\n * Single menu item wrapper\n */\nexport function SidebarMenuItem({ className, ...props }: React.ComponentProps<'li'>) {\n return (\n <li\n data-sidebar=\"menu-item\"\n className={classNames(\n 'group/menu-item relative list-none group-data-[collapsible=icon]:[&>*:not([data-sidebar=menu-button])]:hidden',\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Extra action button that appears on the right side of menu items\n */\nexport function SidebarMenuAction({\n className,\n ...props\n}: React.ComponentProps<'button'> & {\n /** Only show on hover */\n showOnHover?: boolean;\n}) {\n return (\n <button\n data-sidebar=\"menu-action\"\n className={classNames(\n 'absolute top-0 right-0 btn btn-ghost h-input',\n 'group-data-[collapsible=icon]:hidden',\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Shows a number badge on menu items. Auto-formats large numbers.\n */\nexport function SidebarMenuBadge({\n className,\n number,\n ...props\n}: Omit<React.ComponentProps<'div'>, 'children'> & {\n /** The count to display */\n number: number | bigint;\n}) {\n return (\n <div\n data-sidebar=\"menu-badge\"\n className={classNames(\n 'absolute pointer-events-none top-0 right-0 font-light h-input select-none flex items-center justify-center body-sm tabular-nums',\n 'group-data-[collapsible=icon]:hidden',\n className\n )}\n {...props}\n >\n {/* Format the number */}\n {new Intl.NumberFormat(navigator.language, { useGrouping: true }).format(number)}\n </div>\n );\n}\n\n/**\n * Nested submenu container\n */\nexport function SidebarMenuSub({ className, ...props }: React.ComponentProps<'ul'>) {\n return (\n <ul\n data-sidebar=\"menu-sub\"\n className={classNames('ms-2 flex min-w-0 flex-col', 'group-data-[collapsible=icon]:hidden', className)}\n {...props}\n />\n );\n}\n\n/**\n * Submenu item wrapper\n */\nexport function SidebarMenuSubItem({ className, ...props }: React.ComponentProps<'li'>) {\n return <li className={classNames('list-none ms-0', className)} {...props} />;\n}\n\nexport const sidebarMenuButtonClasses =\n 'btn-tab relative rounded-md border-0 justify-start w-full group-data-[collapsible=icon]:justify-center';\n\n// Common interaction logic for SidebarMenuButton and SidebarMenuSubButton\n/**\n * Main menu button. Pass isActive for current page.\n */\nexport function SidebarMenuButton({\n isActive = false,\n className,\n children,\n icon: Icon,\n leading,\n ...props\n}: Omit<React.ComponentProps<typeof Button>, 'children'> & {\n /** Mark as currently active page */\n icon?: CtxIcon;\n leading?: React.ReactNode;\n isActive?: boolean;\n children?: React.ReactNode;\n}) {\n const { isMobile, state } = useSidebar();\n const isCollapsed = !isMobile && state === 'collapsed';\n\n return (\n <TooltipTrigger isDisabled={!isCollapsed}>\n <Button\n size={isCollapsed ? 'icon' : undefined}\n data-sidebar=\"menu-button\"\n {...(isActive && { 'data-selected': true })}\n className={classNames(\n 'peer/menu-button group/active group-has-[[data-sidebar=menu-action]]/menu-item:pr-input',\n sidebarMenuButtonClasses,\n className\n )}\n {...props}\n >\n <span\n aria-hidden\n className=\"absolute inset-y-2 -left-1 w-[7px] rounded-full bg-primary opacity-0 group-data-[selected=true]/active:opacity-100\"\n />\n {leading !== undefined ? leading : Icon ? <Icon /> : null}\n {!isCollapsed && children}\n </Button>\n <Tooltip placement=\"right\">\n <span className=\"flex items-center gap-2\">{children}</span>\n </Tooltip>\n </TooltipTrigger>\n );\n}\n\n/**\n * Button for submenu items\n */\nexport function SidebarMenuSubButton({\n isActive,\n className,\n ...props\n}: React.ComponentProps<'button'> & {\n isActive?: boolean;\n}) {\n return (\n <button\n data-sidebar=\"menu-sub-button\"\n data-active={isActive}\n className={classNames(sidebarMenuButtonClasses, className)}\n {...props}\n />\n );\n}\n\n/**\n * Sticky header at the top of the sidebar. Displays a square logo slot aligned with the outlet header.\n */\nexport function SidebarHeader({\n className,\n logo,\n children,\n showTrigger = true,\n ...props\n}: React.ComponentProps<'div'> & {\n /** Square logo/icon to display */\n logo?: React.ReactNode;\n showTrigger?: boolean;\n}) {\n return (\n <div\n data-sidebar=\"header\"\n className={classNames(\n 'flex items-center h-header select-none shrink-0 border-b px-2 gap-2 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0',\n className\n )}\n {...props}\n >\n {logo && <div className=\"flex items-center justify-center size-input shrink-0\">{logo}</div>}\n {children && <div className=\"flex-1 min-w-0 group-data-[collapsible=icon]:hidden\">{children}</div>}\n {showTrigger && (\n <SidebarTrigger className={'hidden bg-surface-primary lg:flex lg:absolute lg:-right-4 lg:z-50'} />\n )}\n </div>\n );\n}\n\nexport function SidebarFooter({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-sidebar=\"footer\"\n className={classNames(\n 'mt-auto flex shrink-0 select-none items-center gap-2 border-t py-2 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0',\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n *\n * @example Basic usage\n * ```tsx\n * <SidebarOutlet>\n * <SidebarOutletHeader><SidebarTrigger/></SidebarOutletHeader>\n * <SidebarOutletContent><Outlet/></SidebarOutletContent>\n * </SidebarOutlet>\n * ```\n */\nexport function SidebarOutlet({ className, ...props }: React.ComponentProps<'div'>) {\n return <div className={classNames(className, 'flex flex-col flex-1 overflow-auto')} {...props} />;\n}\n\nexport function SidebarOutletContent({ className, ...props }: React.ComponentProps<'div'>) {\n return <div className={classNames(className, 'flex-1 overflow-auto')} {...props} />;\n}\n\nexport function SidebarOutletHeader({ className, ...props }: React.ComponentProps<'nav'>) {\n return (\n <nav\n className={classNames('flex items-center h-header bg-surface-primary p-2 border-b', className)}\n {...props}\n />\n );\n}\n"],"names":["SIDEBAR_KEYBOARD_SHORTCUT","SidebarContext","createContext","useSidebar","context","use","SidebarProvider","defaultOpen","openProp","setOpenProp","className","style","children","props","isMobile","useIsMobile","openMobile","setOpenMobile","useState","_open","_setOpen","open","setOpen","useCallback","value","openState","toggleSidebar","useEffect","handleKeyDown","event","state","contextValue","useMemo","jsx","classNames","Sidebar","side","variant","collapsible","DialogTrigger","DialogOverlay","DialogContent","jsxs","SidebarTrigger","onClick","Button","IcMenu","IcLeft","IcRight","SidebarContent","SidebarGroup","SidebarGroupLabel","SidebarGroupContent","SidebarMenu","SidebarMenuItem","SidebarMenuAction","SidebarMenuBadge","number","SidebarMenuSub","SidebarMenuSubItem","sidebarMenuButtonClasses","SidebarMenuButton","isActive","Icon","leading","isCollapsed","TooltipTrigger","Tooltip","SidebarMenuSubButton","SidebarHeader","logo","showTrigger","SidebarFooter","SidebarOutlet","SidebarOutletContent","SidebarOutletHeader"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sidebar.js","sources":["../../lib/components/sidebar.tsx"],"sourcesContent":["'use client';\nimport { createContext, use, useCallback, useEffect, useMemo, useState } from 'react';\nimport { Button } from '../components/button';\nimport { DialogContent, DialogOverlay, DialogTrigger } from '../components/dialog';\nimport { Tooltip, TooltipTrigger } from '../components/tooltip';\nimport { classNames } from '../utilities/theme';\nimport { useIsMobile } from '../utilities/use-mobile';\nimport { IcLeft, IcMenu, IcRight, type CtxIcon } from './icons';\n\nconst SIDEBAR_KEYBOARD_SHORTCUT = '/';\n\ntype SidebarContextProps = {\n state: 'expanded' | 'collapsed';\n open: boolean;\n setOpen: (open: boolean) => void;\n openMobile: boolean;\n setOpenMobile: (open: boolean) => void;\n isMobile: boolean;\n toggleSidebar: () => void;\n};\n\nexport const SidebarContext = createContext<SidebarContextProps | null>(null);\n\n/**\n * Get the current sidebar state and controls. Must be inside SidebarProvider.\n * @throws When used outside of SidebarProvider\n */\nexport function useSidebar() {\n const context = use(SidebarContext);\n if (!context) {\n throw new Error('useSidebar must be used within a SidebarProvider.');\n }\n\n return context;\n}\n\n/**\n * Wraps your app layout and manages sidebar state. Handles keyboard shortcuts (Cmd/Ctrl + /)\n * and mobile vs desktop behavior.\n *\n * @example\n * <SidebarProvider>\n * <Sidebar>...</Sidebar>\n * <SidebarOutlet>\n * <main>Your app content</main>\n * </SidebarOutlet>\n * </SidebarProvider>\n */\nexport function SidebarProvider({\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n className,\n style,\n children,\n ...props\n}: React.ComponentProps<'div'> & {\n /** Initial open state. Defaults to true */\n defaultOpen?: boolean;\n /** Control the open state yourself */\n open?: boolean;\n /** Callback when sidebar opens/closes */\n onOpenChange?: (open: boolean) => void;\n}) {\n const isMobile = useIsMobile();\n const [openMobile, setOpenMobile] = useState(false);\n\n // This is the internal state of the sidebar.\n // We use openProp and setOpenProp for control from outside the component.\n const [_open, _setOpen] = useState(defaultOpen);\n const open = openProp ?? _open;\n const setOpen = useCallback(\n (value: boolean | ((value: boolean) => boolean)) => {\n const openState = typeof value === 'function' ? value(open) : value;\n if (setOpenProp) {\n setOpenProp(openState);\n } else {\n _setOpen(openState);\n }\n },\n [setOpenProp, open]\n );\n\n // Helper to toggle the sidebar.\n const toggleSidebar = useCallback(() => {\n return isMobile ? setOpenMobile(open => !open) : setOpen(open => !open);\n }, [isMobile, setOpen, setOpenMobile]);\n\n // Adds a keyboard shortcut to toggle the sidebar.\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {\n event.preventDefault();\n toggleSidebar();\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleSidebar]);\n\n // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n // This makes it easier to style the sidebar with Tailwind classes.\n const state = open ? 'expanded' : 'collapsed';\n\n const contextValue = useMemo<SidebarContextProps>(\n () => ({\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n }),\n [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]\n );\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <div\n className={classNames(\n 'group/sidebar-wrapper flex w-full has-[[data-variant=inset]]:bg-surface-primary',\n className\n )}\n {...props}\n >\n {children}\n </div>\n </SidebarContext.Provider>\n );\n}\n\n/**\n * The actual sidebar panel. On mobile it becomes a modal dialog.\n */\nexport function Sidebar({\n side = 'left',\n variant = 'sidebar',\n collapsible = 'icon',\n className,\n children,\n ...props\n}: React.ComponentProps<'div'> & {\n /** Where to dock the sidebar. Default: left */\n side?: 'left' | 'right';\n /** Style variant. Default: sidebar */\n variant?: 'sidebar' | 'floating';\n /** How it collapses. Default: offcanvas */\n collapsible?: 'offcanvas' | 'icon' | 'none';\n}) {\n const { isMobile, state, openMobile, setOpenMobile } = useSidebar();\n\n if (collapsible === 'none') {\n return (\n <div\n className={classNames(\n 'flex h-full w-(--sidebar-width) flex-col bg-surface-primary text-text-primary',\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n }\n\n if (isMobile) {\n return (\n <DialogTrigger isOpen={openMobile} onOpenChange={setOpenMobile} {...props}>\n <DialogOverlay>\n <DialogContent\n data-sidebar=\"sidebar\"\n data-mobile=\"true\"\n className=\"w-(--sidebar-width) max-h-svh overflow-auto p-0 [&>button]:hidden border-r! border-stroke-primary! bg-surface-primary!\"\n side=\"left\"\n closeButton={false}\n >\n {() => <div className=\"flex h-full w-full flex-col\">{children}</div>}\n </DialogContent>\n </DialogOverlay>\n </DialogTrigger>\n );\n }\n\n return (\n <div\n className=\"group peer hidden text-text-primary md:block\"\n data-state={state}\n data-collapsible={state === 'collapsed' ? collapsible : ''}\n data-variant={variant}\n data-side={side}\n >\n {/* This is what handles the sidebar gap on desktop */}\n <div\n className={classNames(\n 'relative w-(--sidebar-width) bg-surface-primary transition-[width] duration-200 ease-linear',\n 'group-data-[collapsible=offcanvas]:w-0',\n 'group-data-[side=right]:rotate-180',\n variant === 'floating'\n ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]'\n : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)'\n )}\n />\n <div\n className={classNames(\n 'absolute z-10 hidden w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex',\n side === 'left'\n ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'\n : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',\n // Adjust the padding for floating variant.\n variant === 'floating'\n ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]'\n : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',\n className\n )}\n {...props}\n >\n <div\n data-sidebar=\"sidebar\"\n className=\"flex h-full w-full flex-col bg-surface-primary group-data-[variant=floating]:border group-data-[variant=floating]:border-border\"\n >\n {children}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * Button that toggles the sidebar. Shows arrow icons.\n */\nexport function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<typeof Button>) {\n const { toggleSidebar, open, isMobile } = useSidebar();\n\n return (\n <Button\n data-sidebar=\"trigger\"\n variant=\"neutral\"\n size=\"icon-sm\"\n className={classNames(className, '!rounded-full p-icon')}\n onClick={event => {\n onClick?.(event);\n toggleSidebar();\n }}\n aria-label=\"trigger\"\n {...props}\n >\n {/* Use transition to rotate a single icon */}\n {isMobile ? <IcMenu /> : open ? <IcLeft /> : <IcRight />}\n </Button>\n );\n}\n\n/**\n * Scrollable container for sidebar content\n */\nexport function SidebarContent({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-sidebar=\"content\"\n className={classNames('flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overflow-x-hidden', className)}\n {...props}\n />\n );\n}\n\n/**\n * Groups related menu items together\n */\nexport function SidebarGroup({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-sidebar=\"group\"\n className={classNames(\n 'group/section relative flex w-full min-w-0 flex-col px-3 group-data-[collapsible=icon]:items-center',\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Label for a group.\n */\nexport function SidebarGroupLabel({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-sidebar=\"group-label\"\n className={classNames(\n 'flex h-input shrink-0 items-center overflow-hidden px-2 text-xs select-none font-medium uppercase text-muted-foreground outline-none ring-ring transition-[height,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-icon [&>svg]:shrink-0',\n 'group-data-[collapsible=icon]:pointer-events-none group-data-[collapsible=icon]:!h-0 group-data-[collapsible=icon]:min-h-0 group-data-[collapsible=icon]:opacity-0',\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Content wrapper for a group\n */\nexport function SidebarGroupContent({ className, ...props }: React.ComponentProps<'div'>) {\n return <div data-sidebar=\"group-content\" className={classNames('w-full body-sm', className)} {...props} />;\n}\n\n/**\n * Container for menu items\n */\nexport function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {\n return (\n <ul data-sidebar=\"menu\" className={classNames('flex w-full min-w-0 flex-col gap-px', className)} {...props} />\n );\n}\n\n/**\n * Single menu item wrapper\n */\nexport function SidebarMenuItem({ className, ...props }: React.ComponentProps<'li'>) {\n return (\n <li\n data-sidebar=\"menu-item\"\n className={classNames(\n 'group/menu-item relative list-none group-data-[collapsible=icon]:[&>*:not([data-sidebar=menu-button])]:hidden',\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Extra action button that appears on the right side of menu items\n */\nexport function SidebarMenuAction({\n className,\n ...props\n}: React.ComponentProps<'button'> & {\n /** Only show on hover */\n showOnHover?: boolean;\n}) {\n return (\n <button\n data-sidebar=\"menu-action\"\n className={classNames(\n 'absolute top-0 right-0 btn btn-ghost h-input',\n 'group-data-[collapsible=icon]:hidden',\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Shows a number badge on menu items. Auto-formats large numbers.\n */\nexport function SidebarMenuBadge({\n className,\n number,\n ...props\n}: Omit<React.ComponentProps<'div'>, 'children'> & {\n /** The count to display */\n number: number | bigint;\n}) {\n return (\n <div\n data-sidebar=\"menu-badge\"\n className={classNames(\n 'absolute pointer-events-none top-0 right-0 font-light h-input select-none flex items-center justify-center body-sm tabular-nums',\n 'group-data-[collapsible=icon]:hidden',\n className\n )}\n {...props}\n >\n {/* Format the number */}\n {new Intl.NumberFormat(navigator.language, { useGrouping: true }).format(number)}\n </div>\n );\n}\n\n/**\n * Nested submenu container\n */\nexport function SidebarMenuSub({ className, ...props }: React.ComponentProps<'ul'>) {\n return (\n <ul\n data-sidebar=\"menu-sub\"\n className={classNames('ms-2 flex min-w-0 flex-col', 'group-data-[collapsible=icon]:hidden', className)}\n {...props}\n />\n );\n}\n\n/**\n * Submenu item wrapper\n */\nexport function SidebarMenuSubItem({ className, ...props }: React.ComponentProps<'li'>) {\n return <li className={classNames('list-none ms-0', className)} {...props} />;\n}\n\nexport const sidebarMenuButtonClasses =\n 'btn-tab relative rounded-md border-0 justify-start w-full group-data-[collapsible=icon]:justify-center';\n\n// Common interaction logic for SidebarMenuButton and SidebarMenuSubButton\n/**\n * Main menu button. Pass isActive for current page.\n */\nexport function SidebarMenuButton({\n isActive = false,\n className,\n children,\n icon: Icon,\n leading,\n ...props\n}: Omit<React.ComponentProps<typeof Button>, 'children'> & {\n /** Mark as currently active page */\n icon?: CtxIcon;\n leading?: React.ReactNode;\n isActive?: boolean;\n children?: React.ReactNode;\n}) {\n const { isMobile, state } = useSidebar();\n const isCollapsed = !isMobile && state === 'collapsed';\n\n return (\n <TooltipTrigger isDisabled={!isCollapsed}>\n <Button\n size={isCollapsed ? 'icon' : undefined}\n data-sidebar=\"menu-button\"\n {...(isActive && { 'data-selected': true })}\n className={classNames(\n 'peer/menu-button group/active group-has-[[data-sidebar=menu-action]]/menu-item:pr-input',\n sidebarMenuButtonClasses,\n className\n )}\n {...props}\n >\n <span\n aria-hidden\n className=\"absolute inset-y-2 -left-1 w-[7px] rounded-full bg-primary opacity-0 group-data-[selected=true]/active:opacity-100\"\n />\n {leading !== undefined ? leading : Icon ? <Icon /> : null}\n {!isCollapsed && children}\n </Button>\n <Tooltip placement=\"right\">\n <span className=\"flex items-center gap-2\">{children}</span>\n </Tooltip>\n </TooltipTrigger>\n );\n}\n\n/**\n * Button for submenu items\n */\nexport function SidebarMenuSubButton({\n isActive,\n className,\n ...props\n}: React.ComponentProps<'button'> & {\n isActive?: boolean;\n}) {\n return (\n <button\n data-sidebar=\"menu-sub-button\"\n data-active={isActive}\n className={classNames(sidebarMenuButtonClasses, className)}\n {...props}\n />\n );\n}\n\n/**\n * Sticky header at the top of the sidebar. Displays a square logo slot aligned with the outlet header.\n */\nexport function SidebarHeader({\n className,\n logo,\n children,\n showTrigger = true,\n ...props\n}: React.ComponentProps<'div'> & {\n /** Square logo/icon to display */\n logo?: React.ReactNode;\n showTrigger?: boolean;\n}) {\n return (\n <div\n data-sidebar=\"header\"\n className={classNames(\n 'flex items-center h-header select-none shrink-0 border-b px-2 gap-2 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0',\n className\n )}\n {...props}\n >\n {logo && <div className=\"flex items-center justify-center size-input shrink-0\">{logo}</div>}\n {children && <div className=\"flex-1 min-w-0 group-data-[collapsible=icon]:hidden\">{children}</div>}\n {showTrigger && (\n <SidebarTrigger className={'hidden bg-surface-primary lg:flex lg:absolute lg:-right-4 lg:z-50'} />\n )}\n </div>\n );\n}\n\nexport function SidebarFooter({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-sidebar=\"footer\"\n className={classNames(\n 'mt-auto flex shrink-0 select-none items-center gap-2 border-t py-2 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0',\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n *\n * @example Basic usage\n * ```tsx\n * <SidebarOutlet>\n * <SidebarOutletHeader><SidebarTrigger/></SidebarOutletHeader>\n * <SidebarOutletContent><Outlet/></SidebarOutletContent>\n * </SidebarOutlet>\n * ```\n */\nexport function SidebarOutlet({ className, ...props }: React.ComponentProps<'div'>) {\n return <div className={classNames(className, 'flex flex-col flex-1 overflow-auto')} {...props} />;\n}\n\nexport function SidebarOutletContent({ className, ...props }: React.ComponentProps<'div'>) {\n return <div className={classNames(className, 'flex-1 overflow-auto')} {...props} />;\n}\n\nexport function SidebarOutletHeader({ className, ...props }: React.ComponentProps<'nav'>) {\n return (\n <nav\n className={classNames('flex items-center h-header bg-surface-primary p-2 border-b', className)}\n {...props}\n />\n );\n}\n"],"names":["SIDEBAR_KEYBOARD_SHORTCUT","SidebarContext","createContext","useSidebar","context","use","SidebarProvider","defaultOpen","openProp","setOpenProp","className","style","children","props","isMobile","useIsMobile","openMobile","setOpenMobile","useState","_open","_setOpen","open","setOpen","useCallback","value","openState","toggleSidebar","useEffect","handleKeyDown","event","state","contextValue","useMemo","jsx","classNames","Sidebar","side","variant","collapsible","DialogTrigger","DialogOverlay","DialogContent","jsxs","SidebarTrigger","onClick","Button","IcMenu","IcLeft","IcRight","SidebarContent","SidebarGroup","SidebarGroupLabel","SidebarGroupContent","SidebarMenu","SidebarMenuItem","SidebarMenuAction","SidebarMenuBadge","number","SidebarMenuSub","SidebarMenuSubItem","sidebarMenuButtonClasses","SidebarMenuButton","isActive","Icon","leading","isCollapsed","TooltipTrigger","Tooltip","SidebarMenuSubButton","SidebarHeader","logo","showTrigger","SidebarFooter","SidebarOutlet","SidebarOutletContent","SidebarOutletHeader"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{useSyncExternalStore as r}from"react";const o=768,n=`(max-width: ${o-1}px)`;function c(e){const t=window.matchMedia(n);return t.addEventListener("change",e),()=>t.removeEventListener("change",e)}function i(){return window.matchMedia(n).matches}function s(){return!1}function u(){return r(c,i,s)}export{u as useIsMobile};
|
|
2
2
|
//# sourceMappingURL=use-mobile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-mobile.js","sources":["../../lib/utilities/use-mobile.tsx"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"use-mobile.js","sources":["../../lib/utilities/use-mobile.tsx"],"sourcesContent":["import { useSyncExternalStore } from 'react';\n\nconst MOBILE_BREAKPOINT = 768;\nconst MEDIA_QUERY = `(max-width: ${MOBILE_BREAKPOINT - 1}px)`;\n\nfunction subscribe(onChange: () => void) {\n const mql = window.matchMedia(MEDIA_QUERY);\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n}\n\nfunction getSnapshot(): boolean {\n return window.matchMedia(MEDIA_QUERY).matches;\n}\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\nexport function useIsMobile() {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n"],"names":["MOBILE_BREAKPOINT","MEDIA_QUERY","subscribe","onChange","mql","getSnapshot","getServerSnapshot","useIsMobile","useSyncExternalStore"],"mappings":"6CAEA,MAAMA,EAAoB,IACpBC,EAAc,eAAeD,EAAoB,CAAC,MAExD,SAASE,EAAUC,EAAsB,CACrC,MAAMC,EAAM,OAAO,WAAWH,CAAW,EACzC,OAAAG,EAAI,iBAAiB,SAAUD,CAAQ,EAChC,IAAMC,EAAI,oBAAoB,SAAUD,CAAQ,CAC3D,CAEA,SAASE,GAAuB,CAC5B,OAAO,OAAO,WAAWJ,CAAW,EAAE,OAC1C,CAEA,SAASK,GAA6B,CAClC,MAAO,EACX,CAEO,SAASC,GAAc,CAC1B,OAAOC,EAAqBN,EAAWG,EAAaC,CAAiB,CACzE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptlex/web-components",
|
|
3
|
-
"version": "6.7.
|
|
3
|
+
"version": "6.7.2",
|
|
4
4
|
"description": "React component library for Cryptlex web applications",
|
|
5
5
|
"author": "Cryptlex",
|
|
6
6
|
"type": "module",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"*.{ts,tsx,css,json}": "prettier --write"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@cryptlex/web-api-types": "2.
|
|
51
|
+
"@cryptlex/web-api-types": "2.166.0",
|
|
52
52
|
"@dnd-kit/core": "6.3.1",
|
|
53
53
|
"@dnd-kit/sortable": "10.0.0",
|
|
54
54
|
"@dnd-kit/utilities": "3.2.2",
|