@powerhousedao/design-system 6.2.0-dev.20 → 6.2.0-dev.21
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/{command-C_iXJ_6a.d.ts → command-B5BuEBfZ.d.ts} +14 -14
- package/dist/{command-C_iXJ_6a.d.ts.map → command-B5BuEBfZ.d.ts.map} +1 -1
- package/dist/connect/components/toast/toast.d.ts.map +1 -1
- package/dist/connect/components/toast/toast.js +5 -5
- package/dist/connect/components/toast/toast.js.map +1 -1
- package/dist/connect/index.d.ts +2 -2
- package/dist/connect/index.d.ts.map +1 -1
- package/dist/connect/index.js +530 -530
- package/dist/connect/index.js.map +1 -1
- package/dist/{enum-field-tdsq3ox_.d.ts → enum-field-Cct-i2Uz.d.ts} +2 -2
- package/dist/{enum-field-tdsq3ox_.d.ts.map → enum-field-Cct-i2Uz.d.ts.map} +1 -1
- package/dist/id-autocomplete-list-option-CB5B07Hi.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/json-viewer-DaTgmzkS.d.ts.map +1 -1
- package/dist/{radio-group-field-JewKPpm4.d.ts → radio-group-field-BN654-SO.d.ts} +2 -2
- package/dist/{radio-group-field-JewKPpm4.d.ts.map → radio-group-field-BN654-SO.d.ts.map} +1 -1
- package/dist/{src-BrrPQVce.js → src-CfIIbIOi.js} +11 -11
- package/dist/src-CfIIbIOi.js.map +1 -0
- package/dist/style.css +79 -16
- package/dist/ui/components/button/button.js +1 -1
- package/dist/ui/components/button/button.js.map +1 -1
- package/dist/ui/components/character-counter/character-counter.js +6 -6
- package/dist/ui/components/character-counter/character-counter.js.map +1 -1
- package/dist/ui/components/checkbox/checkbox-base.js +1 -1
- package/dist/ui/components/checkbox/checkbox-base.js.map +1 -1
- package/dist/ui/components/checkbox/checkbox.js +1 -1
- package/dist/ui/components/checkbox/checkbox.js.map +1 -1
- package/dist/ui/components/command/command.d.ts +1 -1
- package/dist/ui/components/command/command.js +5 -5
- package/dist/ui/components/command/command.js.map +1 -1
- package/dist/ui/components/enum-field/enum-field.d.ts +1 -1
- package/dist/ui/components/form-description/form-description.js +1 -1
- package/dist/ui/components/form-description/form-description.js.map +1 -1
- package/dist/ui/components/form-label/form-label.js +3 -3
- package/dist/ui/components/form-label/form-label.js.map +1 -1
- package/dist/ui/components/form-message/form-message.js +3 -3
- package/dist/ui/components/form-message/form-message.js.map +1 -1
- package/dist/ui/components/form-message/message-list.js +3 -3
- package/dist/ui/components/form-message/message-list.js.map +1 -1
- package/dist/ui/components/id-autocomplete/id-autocomplete-input-container.js +3 -3
- package/dist/ui/components/id-autocomplete/id-autocomplete-input-container.js.map +1 -1
- package/dist/ui/components/id-autocomplete/id-autocomplete-list-option.js +10 -10
- package/dist/ui/components/id-autocomplete/id-autocomplete-list-option.js.map +1 -1
- package/dist/ui/components/id-autocomplete/id-autocomplete-list.js +1 -1
- package/dist/ui/components/id-autocomplete/id-autocomplete-list.js.map +1 -1
- package/dist/ui/components/id-autocomplete/id-autocomplete.js +1 -1
- package/dist/ui/components/id-autocomplete/id-autocomplete.js.map +1 -1
- package/dist/ui/components/index.d.ts +3 -3
- package/dist/ui/components/input/input.js +1 -1
- package/dist/ui/components/input/input.js.map +1 -1
- package/dist/ui/components/input/splitted-input-diff.js +2 -2
- package/dist/ui/components/input/splitted-input-diff.js.map +1 -1
- package/dist/ui/components/input/subcomponent/input-diff.js +1 -1
- package/dist/ui/components/input/subcomponent/input-diff.js.map +1 -1
- package/dist/ui/components/input/subcomponent/text-diff.js +3 -3
- package/dist/ui/components/input/subcomponent/text-diff.js.map +1 -1
- package/dist/ui/components/json-viewer/json-viewer.js +6 -5
- package/dist/ui/components/json-viewer/json-viewer.js.map +1 -1
- package/dist/ui/components/popover/popover.js +1 -1
- package/dist/ui/components/popover/popover.js.map +1 -1
- package/dist/ui/components/radio-group-field/radio-group-field.d.ts +1 -1
- package/dist/ui/components/radio-group-field/radio.js +3 -3
- package/dist/ui/components/radio-group-field/radio.js.map +1 -1
- package/dist/ui/components/search-autocomplete/search-autocomplete.js +13 -13
- package/dist/ui/components/search-autocomplete/search-autocomplete.js.map +1 -1
- package/dist/ui/components/select-field/content.js +8 -8
- package/dist/ui/components/select-field/content.js.map +1 -1
- package/dist/ui/components/select-field/select-field.js +1 -1
- package/dist/ui/components/select-field/select-field.js.map +1 -1
- package/dist/ui/components/select-field/selected-content.js +7 -7
- package/dist/ui/components/select-field/selected-content.js.map +1 -1
- package/dist/ui/components/select-field/subcomponents/CommandItemList.d.ts.map +1 -1
- package/dist/ui/components/select-field/subcomponents/CommandItemList.js +9 -9
- package/dist/ui/components/select-field/subcomponents/CommandItemList.js.map +1 -1
- package/dist/ui/components/sidebar/sidebar.js +5 -5
- package/dist/ui/components/sidebar/sidebar.js.map +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-content-area.js +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-content-area.js.map +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-header.js +3 -3
- package/dist/ui/components/sidebar/subcomponents/sidebar-header.js.map +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-item.js +7 -7
- package/dist/ui/components/sidebar/subcomponents/sidebar-item.js.map +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-pinning-area.js +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-pinning-area.js.map +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-search/search-navigation-arrow.js +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-search/search-navigation-arrow.js.map +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-search/search-result-counter.js +4 -4
- package/dist/ui/components/sidebar/subcomponents/sidebar-search/search-result-counter.js.map +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-search/sidebar-search.js +5 -5
- package/dist/ui/components/sidebar/subcomponents/sidebar-search/sidebar-search.js.map +1 -1
- package/dist/ui/components/sidebar/subcomponents/sidebar-skeleton-item.js +2 -2
- package/dist/ui/components/sidebar/subcomponents/sidebar-skeleton-item.js.map +1 -1
- package/dist/ui/components/sidebar/subcomponents/status-icon.d.ts +1 -1
- package/dist/ui/components/sidebar/subcomponents/status-icon.d.ts.map +1 -1
- package/dist/ui/components/sidebar/subcomponents/status-icon.js +6 -6
- package/dist/ui/components/sidebar/subcomponents/status-icon.js.map +1 -1
- package/dist/ui/components/tooltip/tooltip.js +1 -1
- package/dist/ui/components/tooltip/tooltip.js.map +1 -1
- package/dist/ui/index.d.ts +3 -3
- package/package.json +4 -4
- package/theme.css +145 -17
- package/dist/src-BrrPQVce.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-field.js","names":["twMerge","React","useCallback","useId","Button","Command","FormDescription","FormGroup","FormLabel","FormMessageList","Popover","PopoverContent","PopoverTrigger","withFieldValidation","Content","SelectedContent","useSelectField","SelectFieldRaw","forwardRef","options","favoriteOptions","defaultValue","value","onChange","onBlur","id","propId","name","label","required","disabled","errors","warnings","multiple","selectionIcon","selectionIconPosition","searchable","description","placeholder","className","contentClassName","contentAlign","props","ref","prefix","selectedValues","isPopoverOpen","commandListRef","toggleOption","handleClear","toggleAll","handleOpenChange","onTriggerBlur","e","length","preventDefault","target","control","focus","open","document","activeElement","undefined","find","opt","SelectField","displayName"],"sources":["../../../../src/ui/components/select-field/select-field.tsx"],"sourcesContent":["import { twMerge } from \"tailwind-merge\";\nimport React, { useCallback, useId } from \"react\";\nimport type { FieldErrorHandling, InputBaseProps } from \"../../types.js\";\nimport { Button } from \"../button/button.js\";\nimport { Command } from \"../command/command.js\";\nimport type { SelectOption, SelectProps } from \"../enum-field/types.js\";\nimport { FormDescription } from \"../form-description/form-description.js\";\nimport { FormGroup } from \"../form-group/form-group.js\";\nimport { FormLabel } from \"../form-label/form-label.js\";\nimport { FormMessageList } from \"../form-message/message-list.js\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../popover/popover.js\";\nimport { withFieldValidation } from \"../with-field-validation/with-field-validation.js\";\nimport { Content } from \"./content.js\";\nimport { SelectedContent } from \"./selected-content.js\";\nimport { useSelectField } from \"./use-select-field.js\";\n\ntype SelectFieldBaseProps = Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n | keyof InputBaseProps<string | string[]>\n | keyof FieldErrorHandling\n | keyof SelectProps\n>;\n\nexport type SelectFieldProps = SelectFieldBaseProps &\n InputBaseProps<string | string[]> &\n FieldErrorHandling & {\n options?: SelectOption[];\n favoriteOptions?: SelectOption[];\n } & Omit<SelectProps, \"options\" | \"favoriteOptions\">;\n\nexport const SelectFieldRaw = React.forwardRef<\n HTMLButtonElement,\n SelectFieldProps\n>(\n (\n {\n // core functionality props\n options = [],\n favoriteOptions = [],\n defaultValue,\n value,\n onChange,\n onBlur,\n\n // form-related props\n id: propId,\n name,\n label,\n required,\n disabled,\n\n // validation props\n errors = [],\n warnings = [],\n\n // behavior props\n multiple,\n selectionIcon = \"auto\",\n selectionIconPosition = \"left\",\n searchable,\n\n // display props\n description,\n placeholder,\n className,\n contentClassName,\n contentAlign = \"start\",\n\n ...props\n },\n ref,\n ) => {\n const prefix = useId();\n const id = propId ?? `${prefix}-select`;\n\n const {\n selectedValues,\n isPopoverOpen,\n commandListRef,\n toggleOption,\n handleClear,\n toggleAll,\n handleOpenChange,\n } = useSelectField({\n options,\n multiple,\n defaultValue,\n value,\n onChange,\n });\n\n const onTriggerBlur = useCallback(\n (e: React.FocusEvent<HTMLButtonElement>) => {\n if (!isPopoverOpen) {\n // trigger the blur event when the trigger loses focus but the popover is not open,\n // because when the popover is open, the trigger loses focus but the select as a component still has the focus\n onBlur?.(e);\n }\n },\n [onBlur, isPopoverOpen],\n );\n\n return (\n <FormGroup>\n {label && (\n <FormLabel\n htmlFor={id}\n required={required}\n disabled={disabled}\n hasError={errors.length > 0}\n inline={false}\n onClick={(e) => {\n e.preventDefault();\n (e.target as HTMLLabelElement).control?.focus();\n }}\n >\n {label}\n </FormLabel>\n )}\n <Popover\n open={isPopoverOpen}\n onOpenChange={(open) => {\n handleOpenChange(open);\n // if the popover is closing and it was not by the trigger button\n if (!open && document.activeElement?.id !== id) {\n onBlur?.({ target: {} } as React.FocusEvent<HTMLButtonElement>);\n }\n }}\n >\n <PopoverTrigger asChild={true}>\n {/* TODO: create a trigger component */}\n <Button\n id={id}\n name={name}\n type=\"button\"\n role=\"combobox\"\n onBlur={onTriggerBlur}\n disabled={disabled}\n aria-invalid={errors.length > 0}\n aria-label={\n label ? undefined : multiple ? \"Multi select\" : \"Select\"\n }\n aria-required={required}\n aria-expanded={isPopoverOpen}\n className={twMerge(\n \"flex h-9 w-full items-center justify-between px-3 py-2\",\n \"rounded-md border border-gray-300 bg-gray-50 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-100\",\n \"hover:border-gray-300 hover:bg-gray-100 dark:hover:border-slate-500 dark:hover:bg-slate-600 dark:hover:text-slate-100\",\n \"dark:hover:border-slate-500 dark:hover:bg-slate-600 dark:hover:text-slate-100\",\n \"focus:ring-1 focus:ring-gray-900 focus:ring-offset-0 focus:outline-none dark:focus:ring-slate-300\",\n \"focus-visible:ring-1 focus-visible:ring-gray-900 focus-visible:ring-offset-0 dark:focus-visible:ring-slate-300\",\n disabled && [\n \"pointer-events-auto! cursor-not-allowed bg-gray-50 dark:bg-slate-800\",\n \"hover:border-gray-300 hover:bg-gray-50 dark:hover:border-slate-500 dark:hover:bg-slate-600 dark:hover:text-slate-100\",\n ],\n className,\n )}\n {...props}\n ref={ref}\n >\n <SelectedContent\n selectedValues={selectedValues}\n options={[...favoriteOptions, ...options]}\n multiple={multiple}\n searchable={searchable}\n placeholder={placeholder}\n handleClear={handleClear}\n />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align={contentAlign}\n onEscapeKeyDown={(e) => {\n e.preventDefault();\n handleOpenChange(false);\n }}\n className={contentClassName}\n >\n <Command\n defaultValue={\n !multiple && selectedValues[0]\n ? options.find((opt) => opt.value === selectedValues[0])\n ?.label\n : undefined\n }\n >\n <Content\n favoriteOptions={favoriteOptions}\n searchable={searchable}\n commandListRef={commandListRef}\n multiple={multiple}\n selectedValues={selectedValues}\n selectionIcon={selectionIcon}\n selectionIconPosition={selectionIconPosition}\n options={options}\n toggleAll={toggleAll}\n toggleOption={toggleOption}\n />\n </Command>\n </PopoverContent>\n </Popover>\n {description && <FormDescription>{description}</FormDescription>}\n {warnings.length > 0 && (\n <FormMessageList messages={warnings} type=\"warning\" />\n )}\n {errors.length > 0 && (\n <FormMessageList messages={errors} type=\"error\" />\n )}\n </FormGroup>\n );\n },\n);\n\nexport const SelectField =\n withFieldValidation<SelectFieldProps>(SelectFieldRaw);\n\nSelectField.displayName = \"SelectField\";\n"],"mappings":";;;;;;;;;;;;;;;AA8BA,MAAaiB,iBAAiBhB,sBAAMiB,YAKhC,EAEEC,UAAU,EAAE,EACZC,kBAAkB,EAAE,EACpBC,cACAC,OACAC,UACAC,QAGAC,IAAIC,QACJC,MACAC,OACAC,UACAC,UAGAC,SAAS,EAAE,EACXC,WAAW,EAAE,EAGbC,UACAC,gBAAgB,QAChBC,wBAAwB,QACxBC,YAGAC,aACAC,aACAC,WACAC,kBACAC,eAAe,SAEf,GAAGC,SAELC,QACG;CACH,MAAMC,SAASzC,OAAO;CACtB,MAAMsB,KAAKC,UAAU,GAAGkB,OAAM;CAE9B,MAAM,EACJC,gBACAC,eACAC,gBACAC,cACAC,aACAC,WACAC,qBACEnC,eAAe;EACjBG;EACAc;EACAZ;EACAC;EACAC;EACD,CAAC;CAEF,MAAM6B,gBAAgBlD,aACnBmD,MAA2C;AAC1C,MAAI,CAACP,cAGHtB,UAAS6B,EAAE;IAGf,CAAC7B,QAAQsB,cACX,CAAC;AAED,QACE,qBAAC,WAAD,EAAA,UAAA;EACGlB,SACC,oBAAC,WAAD;GACE,SAASH;GACCI;GACAC;GACV,UAAUC,OAAOuB,SAAS;GAC1B,QAAQ;GACR,UAAUD,MAAM;AACdA,MAAEE,gBAAgB;AACjBF,MAAEG,OAA4BC,SAASC,OAAO;;aAGhD9B;GAEJ,CAAA;EACD,qBAAC,SAAD;GACE,MAAMkB;GACN,eAAea,SAAS;AACtBR,qBAAiBQ,KAAK;AAEtB,QAAI,CAACA,QAAQC,SAASC,eAAepC,OAAOA,GAC1CD,UAAS,EAAEgC,QAAQ,EAAC,EAA0C,CAAC;;aANrE,CAUE,oBAAC,gBAAD;IAAgB,SAAS;cAEvB,oBAAC,QAAD;KACM/B;KACEE;KACN,MAAK;KACL,MAAK;KACL,QAAQyB;KACEtB;KACV,gBAAcC,OAAOuB,SAAS;KAC9B,cACE1B,QAAQkC,KAAAA,IAAY7B,WAAW,iBAAiB;KAElD,iBAAeJ;KACf,iBAAeiB;KACf,WAAW9C,QACT,0DACA,4GACA,yHACA,iFACA,qGACA,kHACA8B,YAAY,CACV,wEACA,uHACD,EACDS,UACD;KACD,GAAIG;KACCC;eAEL,oBAAC,iBAAD;MACkBE;MAChB,SAAS,CAAC,GAAGzB,iBAAiB,GAAGD,QAAQ;MAC/Bc;MACEG;MACCE;MACAW;MAAY,CAAA;KAErB,CAAA;IACM,CAAA,EAChB,oBAAC,gBAAD;IACE,OAAOR;IACP,kBAAkBY,MAAM;AACtBA,OAAEE,gBAAgB;AAClBJ,sBAAiB,MAAM;;IAEzB,WAAWX;cAEX,oBAAC,SAAD;KACE,cACE,CAACP,YAAYY,eAAe,KACxB1B,QAAQ4C,MAAMC,QAAQA,IAAI1C,UAAUuB,eAAe,GAAG,EAClDjB,QACJkC,KAAAA;eAGN,oBAAC,SAAD;MACmB1C;MACLgB;MACIW;MACNd;MACMY;MACDX;MACQC;MACdhB;MACE+B;MACGF;MAAa,CAAA;KAEtB,CAAA;IACK,CAAA,CACT;;EACRX,eAAe,oBAAC,iBAAD,EAAA,UAAkBA,aAA8B,CAAA;EAC/DL,SAASsB,SAAS,KACjB,oBAAC,iBAAD;GAAiB,UAAUtB;GAAU,MAAK;GAC3C,CAAA;EACAD,OAAOuB,SAAS,KACf,oBAAC,iBAAD;GAAiB,UAAUvB;GAAQ,MAAK;GACzC,CAAA;EACS,EAAA,CAAA;EAGjB;AAED,MAAakC,cACXpD,oBAAsCI,eAAe;AAEvDgD,YAAYC,cAAc"}
|
|
1
|
+
{"version":3,"file":"select-field.js","names":["twMerge","React","useCallback","useId","Button","Command","FormDescription","FormGroup","FormLabel","FormMessageList","Popover","PopoverContent","PopoverTrigger","withFieldValidation","Content","SelectedContent","useSelectField","SelectFieldRaw","forwardRef","options","favoriteOptions","defaultValue","value","onChange","onBlur","id","propId","name","label","required","disabled","errors","warnings","multiple","selectionIcon","selectionIconPosition","searchable","description","placeholder","className","contentClassName","contentAlign","props","ref","prefix","selectedValues","isPopoverOpen","commandListRef","toggleOption","handleClear","toggleAll","handleOpenChange","onTriggerBlur","e","length","preventDefault","target","control","focus","open","document","activeElement","undefined","find","opt","SelectField","displayName"],"sources":["../../../../src/ui/components/select-field/select-field.tsx"],"sourcesContent":["import { twMerge } from \"tailwind-merge\";\nimport React, { useCallback, useId } from \"react\";\nimport type { FieldErrorHandling, InputBaseProps } from \"../../types.js\";\nimport { Button } from \"../button/button.js\";\nimport { Command } from \"../command/command.js\";\nimport type { SelectOption, SelectProps } from \"../enum-field/types.js\";\nimport { FormDescription } from \"../form-description/form-description.js\";\nimport { FormGroup } from \"../form-group/form-group.js\";\nimport { FormLabel } from \"../form-label/form-label.js\";\nimport { FormMessageList } from \"../form-message/message-list.js\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../popover/popover.js\";\nimport { withFieldValidation } from \"../with-field-validation/with-field-validation.js\";\nimport { Content } from \"./content.js\";\nimport { SelectedContent } from \"./selected-content.js\";\nimport { useSelectField } from \"./use-select-field.js\";\n\ntype SelectFieldBaseProps = Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n | keyof InputBaseProps<string | string[]>\n | keyof FieldErrorHandling\n | keyof SelectProps\n>;\n\nexport type SelectFieldProps = SelectFieldBaseProps &\n InputBaseProps<string | string[]> &\n FieldErrorHandling & {\n options?: SelectOption[];\n favoriteOptions?: SelectOption[];\n } & Omit<SelectProps, \"options\" | \"favoriteOptions\">;\n\nexport const SelectFieldRaw = React.forwardRef<\n HTMLButtonElement,\n SelectFieldProps\n>(\n (\n {\n // core functionality props\n options = [],\n favoriteOptions = [],\n defaultValue,\n value,\n onChange,\n onBlur,\n\n // form-related props\n id: propId,\n name,\n label,\n required,\n disabled,\n\n // validation props\n errors = [],\n warnings = [],\n\n // behavior props\n multiple,\n selectionIcon = \"auto\",\n selectionIconPosition = \"left\",\n searchable,\n\n // display props\n description,\n placeholder,\n className,\n contentClassName,\n contentAlign = \"start\",\n\n ...props\n },\n ref,\n ) => {\n const prefix = useId();\n const id = propId ?? `${prefix}-select`;\n\n const {\n selectedValues,\n isPopoverOpen,\n commandListRef,\n toggleOption,\n handleClear,\n toggleAll,\n handleOpenChange,\n } = useSelectField({\n options,\n multiple,\n defaultValue,\n value,\n onChange,\n });\n\n const onTriggerBlur = useCallback(\n (e: React.FocusEvent<HTMLButtonElement>) => {\n if (!isPopoverOpen) {\n // trigger the blur event when the trigger loses focus but the popover is not open,\n // because when the popover is open, the trigger loses focus but the select as a component still has the focus\n onBlur?.(e);\n }\n },\n [onBlur, isPopoverOpen],\n );\n\n return (\n <FormGroup>\n {label && (\n <FormLabel\n htmlFor={id}\n required={required}\n disabled={disabled}\n hasError={errors.length > 0}\n inline={false}\n onClick={(e) => {\n e.preventDefault();\n (e.target as HTMLLabelElement).control?.focus();\n }}\n >\n {label}\n </FormLabel>\n )}\n <Popover\n open={isPopoverOpen}\n onOpenChange={(open) => {\n handleOpenChange(open);\n // if the popover is closing and it was not by the trigger button\n if (!open && document.activeElement?.id !== id) {\n onBlur?.({ target: {} } as React.FocusEvent<HTMLButtonElement>);\n }\n }}\n >\n <PopoverTrigger asChild={true}>\n {/* TODO: create a trigger component */}\n <Button\n id={id}\n name={name}\n type=\"button\"\n role=\"combobox\"\n onBlur={onTriggerBlur}\n disabled={disabled}\n aria-invalid={errors.length > 0}\n aria-label={\n label ? undefined : multiple ? \"Multi select\" : \"Select\"\n }\n aria-required={required}\n aria-expanded={isPopoverOpen}\n className={twMerge(\n \"flex h-9 w-full items-center justify-between px-3 py-2\",\n \"rounded-md border border-border bg-background\",\n \"hover:hover-effect\",\n \"hover:hover-effect\",\n \"focus:ring-1 focus:ring-ring focus:ring-offset-0 focus:outline-none\",\n \"focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-0\",\n disabled && [\n \"pointer-events-auto! cursor-not-allowed bg-background\",\n \"hover:hover-effect\",\n ],\n className,\n )}\n {...props}\n ref={ref}\n >\n <SelectedContent\n selectedValues={selectedValues}\n options={[...favoriteOptions, ...options]}\n multiple={multiple}\n searchable={searchable}\n placeholder={placeholder}\n handleClear={handleClear}\n />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align={contentAlign}\n onEscapeKeyDown={(e) => {\n e.preventDefault();\n handleOpenChange(false);\n }}\n className={contentClassName}\n >\n <Command\n defaultValue={\n !multiple && selectedValues[0]\n ? options.find((opt) => opt.value === selectedValues[0])\n ?.label\n : undefined\n }\n >\n <Content\n favoriteOptions={favoriteOptions}\n searchable={searchable}\n commandListRef={commandListRef}\n multiple={multiple}\n selectedValues={selectedValues}\n selectionIcon={selectionIcon}\n selectionIconPosition={selectionIconPosition}\n options={options}\n toggleAll={toggleAll}\n toggleOption={toggleOption}\n />\n </Command>\n </PopoverContent>\n </Popover>\n {description && <FormDescription>{description}</FormDescription>}\n {warnings.length > 0 && (\n <FormMessageList messages={warnings} type=\"warning\" />\n )}\n {errors.length > 0 && (\n <FormMessageList messages={errors} type=\"error\" />\n )}\n </FormGroup>\n );\n },\n);\n\nexport const SelectField =\n withFieldValidation<SelectFieldProps>(SelectFieldRaw);\n\nSelectField.displayName = \"SelectField\";\n"],"mappings":";;;;;;;;;;;;;;;AA8BA,MAAaiB,iBAAiBhB,sBAAMiB,YAKhC,EAEEC,UAAU,EAAE,EACZC,kBAAkB,EAAE,EACpBC,cACAC,OACAC,UACAC,QAGAC,IAAIC,QACJC,MACAC,OACAC,UACAC,UAGAC,SAAS,EAAE,EACXC,WAAW,EAAE,EAGbC,UACAC,gBAAgB,QAChBC,wBAAwB,QACxBC,YAGAC,aACAC,aACAC,WACAC,kBACAC,eAAe,SAEf,GAAGC,SAELC,QACG;CACH,MAAMC,SAASzC,OAAO;CACtB,MAAMsB,KAAKC,UAAU,GAAGkB,OAAM;CAE9B,MAAM,EACJC,gBACAC,eACAC,gBACAC,cACAC,aACAC,WACAC,qBACEnC,eAAe;EACjBG;EACAc;EACAZ;EACAC;EACAC;EACD,CAAC;CAEF,MAAM6B,gBAAgBlD,aACnBmD,MAA2C;AAC1C,MAAI,CAACP,cAGHtB,UAAS6B,EAAE;IAGf,CAAC7B,QAAQsB,cACX,CAAC;AAED,QACE,qBAAC,WAAD,EAAA,UAAA;EACGlB,SACC,oBAAC,WAAD;GACE,SAASH;GACCI;GACAC;GACV,UAAUC,OAAOuB,SAAS;GAC1B,QAAQ;GACR,UAAUD,MAAM;AACdA,MAAEE,gBAAgB;AACjBF,MAAEG,OAA4BC,SAASC,OAAO;;aAGhD9B;GAEJ,CAAA;EACD,qBAAC,SAAD;GACE,MAAMkB;GACN,eAAea,SAAS;AACtBR,qBAAiBQ,KAAK;AAEtB,QAAI,CAACA,QAAQC,SAASC,eAAepC,OAAOA,GAC1CD,UAAS,EAAEgC,QAAQ,EAAC,EAA0C,CAAC;;aANrE,CAUE,oBAAC,gBAAD;IAAgB,SAAS;cAEvB,oBAAC,QAAD;KACM/B;KACEE;KACN,MAAK;KACL,MAAK;KACL,QAAQyB;KACEtB;KACV,gBAAcC,OAAOuB,SAAS;KAC9B,cACE1B,QAAQkC,KAAAA,IAAY7B,WAAW,iBAAiB;KAElD,iBAAeJ;KACf,iBAAeiB;KACf,WAAW9C,QACT,0DACA,iDACA,sBACA,sBACA,uEACA,4EACA8B,YAAY,CACV,yDACA,qBACD,EACDS,UACD;KACD,GAAIG;KACCC;eAEL,oBAAC,iBAAD;MACkBE;MAChB,SAAS,CAAC,GAAGzB,iBAAiB,GAAGD,QAAQ;MAC/Bc;MACEG;MACCE;MACAW;MAAY,CAAA;KAErB,CAAA;IACM,CAAA,EAChB,oBAAC,gBAAD;IACE,OAAOR;IACP,kBAAkBY,MAAM;AACtBA,OAAEE,gBAAgB;AAClBJ,sBAAiB,MAAM;;IAEzB,WAAWX;cAEX,oBAAC,SAAD;KACE,cACE,CAACP,YAAYY,eAAe,KACxB1B,QAAQ4C,MAAMC,QAAQA,IAAI1C,UAAUuB,eAAe,GAAG,EAClDjB,QACJkC,KAAAA;eAGN,oBAAC,SAAD;MACmB1C;MACLgB;MACIW;MACNd;MACMY;MACDX;MACQC;MACdhB;MACE+B;MACGF;MAAa,CAAA;KAEtB,CAAA;IACK,CAAA,CACT;;EACRX,eAAe,oBAAC,iBAAD,EAAA,UAAkBA,aAA8B,CAAA;EAC/DL,SAASsB,SAAS,KACjB,oBAAC,iBAAD;GAAiB,UAAUtB;GAAU,MAAK;GAC3C,CAAA;EACAD,OAAOuB,SAAS,KACf,oBAAC,iBAAD;GAAiB,UAAUvB;GAAQ,MAAK;GACzC,CAAA;EACS,EAAA,CAAA;EAGjB;AAED,MAAakC,cACXpD,oBAAsCI,eAAe;AAEvDgD,YAAYC,cAAc"}
|
|
@@ -14,22 +14,22 @@ const SelectedContent = ({ selectedValues, options = [], multiple, searchable, p
|
|
|
14
14
|
if (selectedValues.length === 0) return /* @__PURE__ */ jsxs("div", {
|
|
15
15
|
className: twMerge("mx-auto flex w-full items-center", placeholder ? "justify-between" : "justify-end"),
|
|
16
16
|
children: [placeholder && /* @__PURE__ */ jsx("span", {
|
|
17
|
-
className: "text-sm/5 font-normal text-
|
|
17
|
+
className: "text-sm/5 font-normal text-foreground",
|
|
18
18
|
children: placeholder
|
|
19
19
|
}), searchable ? /* @__PURE__ */ jsx(Icon, {
|
|
20
20
|
name: "CaretSort",
|
|
21
21
|
size: 16,
|
|
22
|
-
className: "cursor-pointer text-
|
|
22
|
+
className: "cursor-pointer text-foreground"
|
|
23
23
|
}) : /* @__PURE__ */ jsx(Icon, {
|
|
24
24
|
name: "ChevronDown",
|
|
25
25
|
size: 16,
|
|
26
|
-
className: "cursor-pointer text-
|
|
26
|
+
className: "cursor-pointer text-foreground"
|
|
27
27
|
})]
|
|
28
28
|
});
|
|
29
29
|
return /* @__PURE__ */ jsxs("div", {
|
|
30
30
|
className: "flex w-full items-center justify-between gap-2",
|
|
31
31
|
children: [/* @__PURE__ */ jsx("div", {
|
|
32
|
-
className: twMerge("max-w-full truncate text-
|
|
32
|
+
className: twMerge("max-w-full truncate text-foreground", !multiple && "flex items-center gap-2"),
|
|
33
33
|
children: selectedValues.map((value, index) => {
|
|
34
34
|
const option = options.find((o) => o.value === value);
|
|
35
35
|
return !multiple ? /* @__PURE__ */ jsxs(React.Fragment, { children: [renderIcon(option?.icon), /* @__PURE__ */ jsx("span", {
|
|
@@ -52,16 +52,16 @@ const SelectedContent = ({ selectedValues, options = [], multiple, searchable, p
|
|
|
52
52
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
53
53
|
name: "XmarkLight",
|
|
54
54
|
size: 16,
|
|
55
|
-
className: "cursor-pointer text-
|
|
55
|
+
className: "cursor-pointer text-foreground"
|
|
56
56
|
})
|
|
57
57
|
}), searchable ? /* @__PURE__ */ jsx(Icon, {
|
|
58
58
|
name: "CaretSort",
|
|
59
59
|
size: 16,
|
|
60
|
-
className: "cursor-pointer text-
|
|
60
|
+
className: "cursor-pointer text-foreground"
|
|
61
61
|
}) : /* @__PURE__ */ jsx(Icon, {
|
|
62
62
|
name: "ChevronDown",
|
|
63
63
|
size: 16,
|
|
64
|
-
className: "cursor-pointer text-
|
|
64
|
+
className: "cursor-pointer text-foreground"
|
|
65
65
|
})]
|
|
66
66
|
})]
|
|
67
67
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selected-content.js","names":["Icon","twMerge","React","SelectedContent","selectedValues","options","multiple","searchable","placeholder","handleClear","renderIcon","IconComponent","length","map","value","index","option","find","o","icon","label","e","preventDefault","stopPropagation"],"sources":["../../../../src/ui/components/select-field/selected-content.tsx"],"sourcesContent":["import type { IconName } from \"#design-system\";\nimport { Icon } from \"#design-system\";\nimport { twMerge } from \"tailwind-merge\";\nimport React from \"react\";\nimport type { SelectProps } from \"../enum-field/types.js\";\n\ninterface SelectedContentProps {\n selectedValues: string[];\n options: SelectProps[\"options\"];\n multiple?: boolean;\n searchable?: boolean;\n placeholder?: string;\n handleClear: () => void;\n}\n\nexport const SelectedContent: React.FC<SelectedContentProps> = ({\n selectedValues,\n options = [],\n multiple,\n searchable,\n placeholder,\n handleClear,\n}) => {\n const renderIcon = (\n IconComponent:\n | IconName\n | React.ComponentType<{ className?: string }>\n | undefined,\n ) => {\n if (typeof IconComponent === \"string\") {\n return <Icon name={IconComponent} size={16} />;\n }\n return IconComponent && <IconComponent className=\"size-4\" />;\n };\n\n if (selectedValues.length === 0) {\n return (\n <div\n className={twMerge(\n \"mx-auto flex w-full items-center\",\n placeholder ? \"justify-between\" : \"justify-end\",\n )}\n >\n {placeholder && (\n <span className=\"text-sm/5 font-normal text-
|
|
1
|
+
{"version":3,"file":"selected-content.js","names":["Icon","twMerge","React","SelectedContent","selectedValues","options","multiple","searchable","placeholder","handleClear","renderIcon","IconComponent","length","map","value","index","option","find","o","icon","label","e","preventDefault","stopPropagation"],"sources":["../../../../src/ui/components/select-field/selected-content.tsx"],"sourcesContent":["import type { IconName } from \"#design-system\";\nimport { Icon } from \"#design-system\";\nimport { twMerge } from \"tailwind-merge\";\nimport React from \"react\";\nimport type { SelectProps } from \"../enum-field/types.js\";\n\ninterface SelectedContentProps {\n selectedValues: string[];\n options: SelectProps[\"options\"];\n multiple?: boolean;\n searchable?: boolean;\n placeholder?: string;\n handleClear: () => void;\n}\n\nexport const SelectedContent: React.FC<SelectedContentProps> = ({\n selectedValues,\n options = [],\n multiple,\n searchable,\n placeholder,\n handleClear,\n}) => {\n const renderIcon = (\n IconComponent:\n | IconName\n | React.ComponentType<{ className?: string }>\n | undefined,\n ) => {\n if (typeof IconComponent === \"string\") {\n return <Icon name={IconComponent} size={16} />;\n }\n return IconComponent && <IconComponent className=\"size-4\" />;\n };\n\n if (selectedValues.length === 0) {\n return (\n <div\n className={twMerge(\n \"mx-auto flex w-full items-center\",\n placeholder ? \"justify-between\" : \"justify-end\",\n )}\n >\n {placeholder && (\n <span className=\"text-sm/5 font-normal text-foreground\">\n {placeholder}\n </span>\n )}\n {searchable ? (\n <Icon\n name=\"CaretSort\"\n size={16}\n className=\"cursor-pointer text-foreground\"\n />\n ) : (\n <Icon\n name=\"ChevronDown\"\n size={16}\n className=\"cursor-pointer text-foreground\"\n />\n )}\n </div>\n );\n }\n\n return (\n <div className=\"flex w-full items-center justify-between gap-2\">\n <div\n className={twMerge(\n \"max-w-full truncate text-foreground\",\n !multiple && \"flex items-center gap-2\",\n )}\n >\n {selectedValues.map((value, index) => {\n const option = options.find((o) => o.value === value);\n return !multiple ? (\n <React.Fragment key={value}>\n {renderIcon(option?.icon)}\n <span className=\"truncate text-sm/5 font-normal\">\n {option?.label}\n </span>\n </React.Fragment>\n ) : (\n <span\n key={value}\n className={twMerge(\n \"text-sm/5 font-normal\",\n index !== selectedValues.length - 1 && \"mr-1\",\n )}\n >\n {index !== selectedValues.length - 1\n ? `${option?.label},`\n : option?.label}\n </span>\n );\n })}\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n {multiple && selectedValues.length > 0 && (\n <div\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n handleClear();\n }}\n className=\"size-4 p-0\"\n >\n <Icon\n name=\"XmarkLight\"\n size={16}\n className=\"cursor-pointer text-foreground\"\n />\n </div>\n )}\n {searchable ? (\n <Icon\n name=\"CaretSort\"\n size={16}\n className=\"cursor-pointer text-foreground\"\n />\n ) : (\n <Icon\n name=\"ChevronDown\"\n size={16}\n className=\"cursor-pointer text-foreground\"\n />\n )}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;AAeA,MAAaG,mBAAmD,EAC9DC,gBACAC,UAAU,EAAE,EACZC,UACAC,YACAC,aACAC,kBACI;CACJ,MAAMC,cACJC,kBAIG;AACH,MAAI,OAAOA,kBAAkB,SAC3B,QAAO,oBAAC,MAAD;GAAM,MAAMA;GAAe,MAAM;GAAM,CAAA;AAEhD,SAAOA,iBAAiB,oBAAC,eAAD,EAAe,WAAU,UAAW,CAAA;;AAG9D,KAAIP,eAAeQ,WAAW,EAC5B,QACE,qBAAC,OAAD;EACE,WAAWX,QACT,oCACAO,cAAc,oBAAoB,cACnC;YAJH,CAMGA,eACC,oBAAC,QAAD;GAAM,WAAU;aACbA;GAEJ,CAAA,EACAD,aACC,oBAAC,MAAD;GACE,MAAK;GACL,MAAM;GACN,WAAU;GACV,CAAA,GAEF,oBAAC,MAAD;GACE,MAAK;GACL,MAAM;GACN,WAAU;GAEb,CAAA,CACG;;AAIV,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,OAAD;GACE,WAAWN,QACT,uCACA,CAACK,YAAY,0BACd;aAEAF,eAAeS,KAAKC,OAAOC,UAAU;IACpC,MAAMC,SAASX,QAAQY,MAAMC,MAAMA,EAAEJ,UAAUA,MAAM;AACrD,WAAO,CAACR,WACN,qBAAC,MAAM,UAAP,EAAA,UAAA,CACGI,WAAWM,QAAQG,KAAK,EACzB,oBAAC,QAAD;KAAM,WAAU;eACbH,QAAQI;KACL,CAAA,CACS,EAAA,EALIN,MAKJ,GAEjB,oBAAC,QAAD;KAEE,WAAWb,QACT,yBACAc,UAAUX,eAAeQ,SAAS,KAAK,OACxC;eAEAG,UAAUX,eAAeQ,SAAS,IAC/B,GAAGI,QAAQI,MAAK,KAChBJ,QAAQI;KAEf,EAVQN,MAUR;KACD;GACC,CAAA,EACL,qBAAC,OAAD;GAAK,WAAU;aAAf,CACGR,YAAYF,eAAeQ,SAAS,KACnC,oBAAC,OAAD;IACE,UAAUS,MAAM;AACdA,OAAEC,gBAAgB;AAClBD,OAAEE,iBAAiB;AACnBd,kBAAa;;IAEf,WAAU;cAEV,oBAAC,MAAD;KACE,MAAK;KACL,MAAM;KACN,WAAU;KAAgC,CAAA;IAG/C,CAAA,EACAF,aACC,oBAAC,MAAD;IACE,MAAK;IACL,MAAM;IACN,WAAU;IACV,CAAA,GAEF,oBAAC,MAAD;IACE,MAAK;IACL,MAAM;IACN,WAAU;IAEb,CAAA,CACE;KACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandItemList.d.ts","names":[],"sources":["../../../../../src/ui/components/select-field/subcomponents/CommandItemList.tsx"],"mappings":";;;;UAOU,oBAAA;EACR,OAAA,EAAS,WAAA;EACT,cAAA;EACA,QAAA;EACA,aAAA;EACA,qBAAA;EACA,UAAA;EACA,YAAA,GAAe,KAAA;EACf,QAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"CommandItemList.d.ts","names":[],"sources":["../../../../../src/ui/components/select-field/subcomponents/CommandItemList.tsx"],"mappings":";;;;UAOU,oBAAA;EACR,OAAA,EAAS,WAAA;EACT,cAAA;EACA,QAAA;EACA,aAAA;EACA,qBAAA;EACA,UAAA;EACA,YAAA,GAAe,KAAA;EACf,QAAA;AAAA;AAAA,cAyBW,eAAA,EAAiB,KAAA,CAAM,EAAA,CAAG,oBAAA"}
|
|
@@ -8,9 +8,9 @@ const renderIcon = (IconComponent) => {
|
|
|
8
8
|
if (typeof IconComponent === "string") return /* @__PURE__ */ jsx(Icon, {
|
|
9
9
|
name: IconComponent,
|
|
10
10
|
size: 16,
|
|
11
|
-
className: twMerge("text-
|
|
11
|
+
className: twMerge("text-foreground")
|
|
12
12
|
});
|
|
13
|
-
return IconComponent && /* @__PURE__ */ jsx(IconComponent, { className: twMerge("size-4", "text-
|
|
13
|
+
return IconComponent && /* @__PURE__ */ jsx(IconComponent, { className: twMerge("size-4", "text-foreground") });
|
|
14
14
|
};
|
|
15
15
|
const CommandItemList = ({ options = [], selectedValues, multiple, selectionIcon, selectionIconPosition, hasAnyIcon, toggleOption, tabIndex }) => {
|
|
16
16
|
return /* @__PURE__ */ jsx(Fragment$1, { children: options.map((opt) => {
|
|
@@ -20,31 +20,31 @@ const CommandItemList = ({ options = [], selectedValues, multiple, selectionIcon
|
|
|
20
20
|
value: opt.label,
|
|
21
21
|
onSelect: () => !opt.disabled && toggleOption(opt.value),
|
|
22
22
|
disabled: opt.disabled,
|
|
23
|
-
className: twMerge("cursor-pointer", "data-[selected=true]:bg-
|
|
23
|
+
className: twMerge("cursor-pointer", "data-[selected=true]:bg-accent", opt.disabled && "pointer-events-auto! cursor-not-allowed hover:hover-effect"),
|
|
24
24
|
role: "option",
|
|
25
25
|
"aria-selected": isSelected,
|
|
26
26
|
children: [
|
|
27
27
|
selectionIcon === "auto" && (multiple ? /* @__PURE__ */ jsx("div", {
|
|
28
|
-
className: twMerge("flex size-4 items-center justify-center rounded-md border", "border-
|
|
28
|
+
className: twMerge("flex size-4 items-center justify-center rounded-md border", "border-foreground", isSelected && "bg-primary text-primary-foreground"),
|
|
29
29
|
children: isSelected && /* @__PURE__ */ jsx(Icon, {
|
|
30
30
|
name: "Checkmark",
|
|
31
31
|
size: 16
|
|
32
32
|
})
|
|
33
33
|
}) : /* @__PURE__ */ jsx("div", {
|
|
34
|
-
className: twMerge("relative size-4 rounded-full border", isSelected ? "border-
|
|
35
|
-
children: isSelected && /* @__PURE__ */ jsx("div", { className: "absolute top-1/2 left-1/2 size-2.5 -translate-1/2 rounded-full bg-
|
|
34
|
+
className: twMerge("relative size-4 rounded-full border", isSelected ? "border-foreground" : "border-foreground", "bg-transparent"),
|
|
35
|
+
children: isSelected && /* @__PURE__ */ jsx("div", { className: "absolute top-1/2 left-1/2 size-2.5 -translate-1/2 rounded-full bg-primary" })
|
|
36
36
|
})),
|
|
37
37
|
selectionIcon === "checkmark" && !(selectionIconPosition === "right" && hasAnyIcon) && /* @__PURE__ */ jsx("div", {
|
|
38
38
|
className: "size-4",
|
|
39
39
|
children: selectionIconPosition === "left" && isSelected && /* @__PURE__ */ jsx(Icon, {
|
|
40
40
|
name: "Checkmark",
|
|
41
41
|
size: 16,
|
|
42
|
-
className: "text-
|
|
42
|
+
className: "text-foreground"
|
|
43
43
|
})
|
|
44
44
|
}),
|
|
45
45
|
renderIcon(opt.icon),
|
|
46
46
|
/* @__PURE__ */ jsx("span", {
|
|
47
|
-
className: twMerge("flex-1 truncate text-sm/4 font-normal", "text-
|
|
47
|
+
className: twMerge("flex-1 truncate text-sm/4 font-normal", "text-foreground", opt.disabled && "text-foreground"),
|
|
48
48
|
children: opt.label
|
|
49
49
|
}),
|
|
50
50
|
selectionIcon === "checkmark" && selectionIconPosition === "right" && /* @__PURE__ */ jsx("div", {
|
|
@@ -52,7 +52,7 @@ const CommandItemList = ({ options = [], selectedValues, multiple, selectionIcon
|
|
|
52
52
|
children: isSelected && /* @__PURE__ */ jsx(Icon, {
|
|
53
53
|
name: "Checkmark",
|
|
54
54
|
size: 16,
|
|
55
|
-
className: "text-
|
|
55
|
+
className: "text-foreground"
|
|
56
56
|
})
|
|
57
57
|
})
|
|
58
58
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandItemList.js","names":["Icon","
|
|
1
|
+
{"version":3,"file":"CommandItemList.js","names":["Icon","React","twMerge","CommandItem","renderIcon","IconComponent","CommandItemList","options","selectedValues","multiple","selectionIcon","selectionIconPosition","hasAnyIcon","toggleOption","tabIndex","map","opt","isSelected","includes","value","label","disabled","icon"],"sources":["../../../../../src/ui/components/select-field/subcomponents/CommandItemList.tsx"],"sourcesContent":["import type { IconName } from \"#design-system\";\nimport { Icon } from \"#design-system\";\nimport React from \"react\";\nimport { twMerge } from \"tailwind-merge\";\nimport { CommandItem } from \"../../command/command.js\";\nimport type { SelectProps } from \"../../enum-field/types.js\";\n\ninterface FavoriteOptionsProps {\n options: SelectProps[\"options\"];\n selectedValues: string[];\n multiple?: boolean;\n selectionIcon: \"auto\" | \"checkmark\";\n selectionIconPosition: \"left\" | \"right\";\n hasAnyIcon: boolean;\n toggleOption: (value: string) => void;\n tabIndex?: number;\n}\n\nconst renderIcon = (\n IconComponent:\n | IconName\n | React.ComponentType<{ className?: string }>\n | undefined,\n) => {\n if (typeof IconComponent === \"string\") {\n return (\n <Icon\n name={IconComponent}\n size={16}\n className={twMerge(\"text-foreground\")}\n />\n );\n }\n return (\n IconComponent && (\n <IconComponent className={twMerge(\"size-4\", \"text-foreground\")} />\n )\n );\n};\n\nexport const CommandItemList: React.FC<FavoriteOptionsProps> = ({\n options = [],\n selectedValues,\n multiple,\n selectionIcon,\n selectionIconPosition,\n hasAnyIcon,\n toggleOption,\n tabIndex,\n}) => {\n return (\n <>\n {options.map((opt) => {\n const isSelected = selectedValues.includes(opt.value);\n return (\n <CommandItem\n tabIndex={tabIndex}\n key={`favorite-${opt.value}`}\n value={opt.label}\n onSelect={() => !opt.disabled && toggleOption(opt.value)}\n disabled={opt.disabled}\n className={twMerge(\n \"cursor-pointer\",\n \"data-[selected=true]:bg-accent\",\n opt.disabled &&\n \"pointer-events-auto! cursor-not-allowed hover:hover-effect\",\n )}\n role=\"option\"\n aria-selected={isSelected}\n >\n {selectionIcon === \"auto\" &&\n (multiple ? (\n <div\n className={twMerge(\n \"flex size-4 items-center justify-center rounded-md border\",\n \"border-foreground\",\n isSelected && \"bg-primary text-primary-foreground\",\n )}\n >\n {isSelected && <Icon name=\"Checkmark\" size={16} />}\n </div>\n ) : (\n <div\n className={twMerge(\n \"relative size-4 rounded-full border\",\n isSelected ? \"border-foreground\" : \"border-foreground\",\n \"bg-transparent\",\n )}\n >\n {isSelected && (\n <div className=\"absolute top-1/2 left-1/2 size-2.5 -translate-1/2 rounded-full bg-primary\" />\n )}\n </div>\n ))}\n {selectionIcon === \"checkmark\" &&\n !(selectionIconPosition === \"right\" && hasAnyIcon) && (\n <div className=\"size-4\">\n {selectionIconPosition === \"left\" && isSelected && (\n <Icon\n name=\"Checkmark\"\n size={16}\n className=\"text-foreground\"\n />\n )}\n </div>\n )}\n {renderIcon(opt.icon)}\n <span\n className={twMerge(\n \"flex-1 truncate text-sm/4 font-normal\",\n \"text-foreground\",\n opt.disabled && \"text-foreground\",\n )}\n >\n {opt.label}\n </span>\n {selectionIcon === \"checkmark\" &&\n selectionIconPosition === \"right\" && (\n <div className=\"size-4\">\n {isSelected && (\n <Icon\n name=\"Checkmark\"\n size={16}\n className=\"text-foreground\"\n />\n )}\n </div>\n )}\n </CommandItem>\n );\n })}\n </>\n );\n};\n"],"mappings":";;;;;;AAkBA,MAAMI,cACJC,kBAIG;AACH,KAAI,OAAOA,kBAAkB,SAC3B,QACE,oBAAC,MAAD;EACE,MAAMA;EACN,MAAM;EACN,WAAWH,QAAQ,kBAAkB;EACrC,CAAA;AAGN,QACEG,iBACE,oBAAC,eAAD,EAAe,WAAWH,QAAQ,UAAU,kBAAkB,EAC/D,CAAA;;AAIL,MAAaI,mBAAmD,EAC9DC,UAAU,EAAE,EACZC,gBACAC,UACAC,eACAC,uBACAC,YACAC,cACAC,eACI;AACJ,QACE,oBAAA,YAAA,EAAA,UACGP,QAAQQ,KAAKC,QAAQ;EACpB,MAAMC,aAAaT,eAAeU,SAASF,IAAIG,MAAM;AACrD,SACE,qBAAC,aAAD;GACYL;GAEV,OAAOE,IAAII;GACX,gBAAgB,CAACJ,IAAIK,YAAYR,aAAaG,IAAIG,MAAM;GACxD,UAAUH,IAAIK;GACd,WAAWnB,QACT,kBACA,kCACAc,IAAIK,YACF,6DACH;GACD,MAAK;GACL,iBAAeJ;aAbjB;IAeGP,kBAAkB,WAChBD,WACC,oBAAC,OAAD;KACE,WAAWP,QACT,6DACA,qBACAe,cAAc,qCACf;eAEAA,cAAc,oBAAC,MAAD;MAAM,MAAK;MAAY,MAAM;MAAM,CAAA;KAC9C,CAAA,GAEN,oBAAC,OAAD;KACE,WAAWf,QACT,uCACAe,aAAa,sBAAsB,qBACnC,iBACD;eAEAA,cACC,oBAAC,OAAD,EAAK,WAAU,6EAChB,CAAA;KAEJ,CAAA;IACFP,kBAAkB,eACjB,EAAEC,0BAA0B,WAAWC,eACrC,oBAAC,OAAD;KAAK,WAAU;eACZD,0BAA0B,UAAUM,cACnC,oBAAC,MAAD;MACE,MAAK;MACL,MAAM;MACN,WAAU;MAEb,CAAA;KAEJ,CAAA;IACFb,WAAWY,IAAIM,KAAK;IACrB,oBAAC,QAAD;KACE,WAAWpB,QACT,yCACA,mBACAc,IAAIK,YAAY,kBACjB;eAEAL,IAAII;KACD,CAAA;IACLV,kBAAkB,eACjBC,0BAA0B,WACxB,oBAAC,OAAD;KAAK,WAAU;eACZM,cACC,oBAAC,MAAD;MACE,MAAK;MACL,MAAM;MACN,WAAU;MAEb,CAAA;KAEJ,CAAA;IACS;KAvEP,YAAYD,IAAIG,QAuET;GAEhB,EACD,CAAA"}
|
|
@@ -67,7 +67,7 @@ const Sidebar = ({ activeNodeId, onActiveNodeChange, nodes, sidebarIcon, sidebar
|
|
|
67
67
|
return /* @__PURE__ */ jsxs("aside", {
|
|
68
68
|
ref: sidebarRef,
|
|
69
69
|
style: { width: "min(100%, var(--sidebar-width))" },
|
|
70
|
-
className: twMerge("group peer relative flex h-svh max-h-screen flex-col bg-
|
|
70
|
+
className: twMerge("group peer relative flex h-svh max-h-screen flex-col bg-sidebar text-sidebar-foreground shadow-lg transition-[width] duration-75 ease-linear", isResizing && "transition-none", className),
|
|
71
71
|
children: [isSidebarOpen && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
72
72
|
/* @__PURE__ */ jsx(SidebarHeader, {
|
|
73
73
|
sidebarTitle,
|
|
@@ -82,22 +82,22 @@ const Sidebar = ({ activeNodeId, onActiveNodeChange, nodes, sidebarIcon, sidebar
|
|
|
82
82
|
}),
|
|
83
83
|
showSearchBar && /* @__PURE__ */ jsx(SidebarSearch, { showStatusFilter }),
|
|
84
84
|
extraFooterContent && /* @__PURE__ */ jsx("div", {
|
|
85
|
-
className: "w-full border-t border-
|
|
85
|
+
className: "w-full border-t border-sidebar-border p-2",
|
|
86
86
|
children: extraFooterContent
|
|
87
87
|
})
|
|
88
88
|
] }), resizable && /* @__PURE__ */ jsx("div", {
|
|
89
89
|
className: "group/sidebar-resizer absolute top-0 right-0 h-full w-[10px] translate-x-1/2 cursor-ew-resize select-none",
|
|
90
90
|
onMouseDown: startResizing,
|
|
91
91
|
children: /* @__PURE__ */ jsx("div", {
|
|
92
|
-
className: twMerge("relative h-full w-px translate-x-[5px] transition-colors group-hover/sidebar-resizer:
|
|
92
|
+
className: twMerge("relative h-full w-px translate-x-[5px] transition-colors group-hover/sidebar-resizer:hover-effect", isResizing && "cursor-default bg-info", !isSidebarOpen && "bg-secondary"),
|
|
93
93
|
children: /* @__PURE__ */ jsx("button", {
|
|
94
94
|
type: "button",
|
|
95
|
-
className: twMerge("absolute top-14 right-0 size-4 translate-x-1/2 rounded-full bg-
|
|
95
|
+
className: twMerge("absolute top-14 right-0 size-4 translate-x-1/2 rounded-full bg-muted-foreground", "opacity-0 transition-opacity group-hover/sidebar-resizer:opacity-100"),
|
|
96
96
|
onClick: handleToggleSidebar,
|
|
97
97
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
98
98
|
name: "Caret",
|
|
99
99
|
size: 16,
|
|
100
|
-
className: twMerge("min-w-4 text-
|
|
100
|
+
className: twMerge("min-w-4 text-foreground", isSidebarOpen && "-rotate-180")
|
|
101
101
|
})
|
|
102
102
|
})
|
|
103
103
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar.js","names":["useCallback","useEffect","twMerge","Icon","SidebarContentArea","SidebarHeader","SidebarPinningArea","useSidebar","SidebarSearch","useSidebarResize","triggerEvent","Sidebar","activeNodeId","onActiveNodeChange","nodes","sidebarIcon","sidebarTitle","defaultLevel","enableMacros","allowPinning","resizable","showSearchBar","showStatusFilter","extraFooterContent","initialWidth","maxWidth","className","handleOnTitleClick","isLoading","nodeSortType","nodeSortOrder","sidebarRef","startResizing","isResizing","isSidebarOpen","handleToggleSidebar","defaultWidth","minWidth","pinnedNodePath","setNodes","openLevel","togglePin","syncActiveNodeId","setActiveNodeChangeCallback","providerNodes","currentSortType","currentSortOrder","length","handleActiveNodeChange","node","current","hasPinnedNodes","lastPinnedNodeId","id","width"],"sources":["../../../../src/ui/components/sidebar/sidebar.tsx"],"sourcesContent":["import { useCallback, useEffect } from \"react\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Icon } from \"../../../powerhouse/index.js\";\nimport { SidebarContentArea } from \"./subcomponents/sidebar-content-area.js\";\nimport { SidebarHeader } from \"./subcomponents/sidebar-header.js\";\nimport { SidebarPinningArea } from \"./subcomponents/sidebar-pinning-area.js\";\nimport { useSidebar } from \"./subcomponents/sidebar-provider/index.js\";\nimport { SidebarSearch } from \"./subcomponents/sidebar-search/index.js\";\nimport type { NodeSortOrder, NodeSortType, SidebarNode } from \"./types.js\";\nimport { useSidebarResize } from \"./use-sidebar-resize.js\";\nimport { triggerEvent } from \"./utils.js\";\n\nexport interface SidebarProps {\n /**\n * The ID of the currently active node\n */\n activeNodeId?: string;\n /**\n * Callback function to update the active node\n */\n onActiveNodeChange?: (newNode: SidebarNode) => void;\n /**\n * The nodes to be displayed in the sidebar\n */\n nodes?: SidebarNode[];\n /**\n * Which level should be shown by default (i.e., level 1).\n * @default 1 It is 1-indexed.\n */\n defaultLevel?: number;\n /**\n * Enables the vertical resizing of the sidebar\n * @default true\n */\n resizable?: boolean;\n /**\n * Whether pinning functionality is enabled for nodes\n * @default true\n */\n allowPinning?: boolean;\n /**\n * Configurable levels (e.g., 4). If its zero, then no buttons are shown (default).\n * @default 0\n */\n enableMacros?: number;\n /**\n * Whether to display the search bar in the sidebar\n * @default true\n */\n showSearchBar?: boolean;\n /**\n * Whether to display the status filter button in the sidebar. This option is ignored if `showSearchBar` is `false`.\n * @default false\n */\n showStatusFilter?: boolean;\n /**\n * The title that appears at the top of the sidebar, providing context or a heading for the hierarchy displayed within it.\n */\n sidebarTitle?: string;\n /**\n * An optional icon that can be displayed next to the sidebar title or at the top of the sidebar.\n */\n sidebarIcon?: React.ReactNode;\n /**\n * An optional footer content that can be displayed at the bottom of the sidebar.\n */\n extraFooterContent?: React.ReactNode;\n /**\n * The initial width of the sidebar.\n * @default 300\n */\n initialWidth?: number;\n /**\n * The maximum width of the sidebar.\n */\n maxWidth?: number;\n /**\n * Optional className for the sidebar container\n */\n className?: string;\n\n /**\n * Callback function triggered when the sidebar title is clicked.\n */\n handleOnTitleClick?: () => void;\n\n /**\n * Whether the sidebar is in a loading state, displaying skeleton items\n * @default false\n */\n isLoading?: boolean;\n /**\n * The type of sorting to apply to all nodes recursively.\n * Can be \"alphabetical\", \"natural\", or a custom comparison function.\n * When undefined, nodes maintain their original order.\n * Affects all levels of the tree hierarchy.\n */\n nodeSortType?: NodeSortType;\n /**\n * The order direction for sorting nodes (\"asc\" or \"desc\").\n * Only applicable when nodeSortType is defined.\n * @default \"asc\"\n */\n nodeSortOrder?: NodeSortOrder;\n}\n\n/**\n * Sidebar component that provides a collapsible and resizable navigation panel\n * with support for hierarchical data, search, pinning, and custom styling.\n */\nexport const Sidebar: React.FC<SidebarProps> = ({\n activeNodeId,\n onActiveNodeChange,\n nodes,\n sidebarIcon,\n sidebarTitle,\n defaultLevel = 1,\n enableMacros = 0,\n allowPinning = true,\n resizable = true,\n showSearchBar = true,\n showStatusFilter = false,\n extraFooterContent,\n initialWidth = 300,\n maxWidth,\n className,\n handleOnTitleClick,\n isLoading = false,\n nodeSortType,\n nodeSortOrder = \"asc\",\n}) => {\n const {\n sidebarRef,\n startResizing,\n isResizing,\n isSidebarOpen,\n handleToggleSidebar,\n } = useSidebarResize({\n defaultWidth: initialWidth,\n minWidth: 220,\n maxWidth,\n });\n\n const {\n pinnedNodePath,\n setNodes,\n openLevel,\n togglePin,\n syncActiveNodeId,\n setActiveNodeChangeCallback,\n nodes: providerNodes,\n nodeSortType: currentSortType,\n nodeSortOrder: currentSortOrder,\n } = useSidebar();\n\n useEffect(() => {\n if (nodes) {\n setNodes(nodes, nodeSortType, nodeSortOrder);\n }\n }, [nodes, setNodes, nodeSortType, nodeSortOrder]);\n\n useEffect(() => {\n if (\n !nodes &&\n providerNodes.length > 0 &&\n (nodeSortType !== currentSortType || nodeSortOrder !== currentSortOrder)\n ) {\n setNodes(providerNodes, nodeSortType, nodeSortOrder);\n }\n }, [\n nodes,\n providerNodes,\n setNodes,\n nodeSortType,\n nodeSortOrder,\n currentSortType,\n currentSortOrder,\n ]);\n\n // Initialize default expanded level on mount.\n // openLevel intentionally omitted — it changes every time currentRoots\n // changes, which would cause an infinite loop.\n useEffect(() => {\n if (defaultLevel > 1) {\n openLevel(defaultLevel);\n }\n }, [defaultLevel]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleActiveNodeChange = useCallback(\n (node: SidebarNode) => {\n onActiveNodeChange?.(node);\n triggerEvent(\"sidebar:change\", { node }, sidebarRef.current);\n },\n [onActiveNodeChange, sidebarRef],\n );\n useEffect(() => {\n syncActiveNodeId(activeNodeId);\n }, [activeNodeId, syncActiveNodeId]);\n\n useEffect(() => {\n setActiveNodeChangeCallback(handleActiveNodeChange);\n }, [handleActiveNodeChange, setActiveNodeChangeCallback]);\n\n useEffect(() => {\n const hasPinnedNodes = pinnedNodePath.length > 0;\n if (!allowPinning && hasPinnedNodes) {\n const lastPinnedNodeId = pinnedNodePath[pinnedNodePath.length - 1].id;\n togglePin(lastPinnedNodeId);\n }\n }, [allowPinning, pinnedNodePath, togglePin]);\n\n return (\n <aside\n ref={sidebarRef}\n style={{ width: \"min(100%, var(--sidebar-width))\" }}\n className={twMerge(\n \"group peer relative flex h-svh max-h-screen flex-col bg-gray-50 shadow-lg transition-[width] duration-75 ease-linear dark:bg-slate-600\",\n isResizing && \"transition-none\",\n className,\n )}\n >\n {isSidebarOpen && (\n <>\n <SidebarHeader\n sidebarTitle={sidebarTitle}\n sidebarIcon={sidebarIcon}\n enableMacros={enableMacros}\n handleOnTitleClick={handleOnTitleClick}\n />\n\n {allowPinning && pinnedNodePath.length > 0 && <SidebarPinningArea />}\n <SidebarContentArea\n allowPinning={allowPinning}\n isLoading={isLoading}\n />\n {showSearchBar && (\n <SidebarSearch showStatusFilter={showStatusFilter} />\n )}\n {extraFooterContent && (\n <div className=\"w-full border-t border-gray-300 p-2 dark:border-slate-800\">\n {extraFooterContent}\n </div>\n )}\n </>\n )}\n\n {resizable && (\n <div\n className=\"group/sidebar-resizer absolute top-0 right-0 h-full w-[10px] translate-x-1/2 cursor-ew-resize select-none\"\n onMouseDown={startResizing}\n >\n <div\n className={twMerge(\n \"relative h-full w-px translate-x-[5px] transition-colors group-hover/sidebar-resizer:bg-gray-500 dark:group-hover/sidebar-resizer:bg-slate-600\",\n isResizing && \"cursor-default bg-blue-500\",\n !isSidebarOpen && \"bg-gray-300 dark:bg-slate-600\",\n )}\n >\n <button\n type=\"button\"\n className={twMerge(\n \"absolute top-14 right-0 size-4 translate-x-1/2 rounded-full bg-gray-500 dark:bg-slate-900\",\n \"opacity-0 transition-opacity group-hover/sidebar-resizer:opacity-100\",\n )}\n onClick={handleToggleSidebar}\n >\n <Icon\n name=\"Caret\"\n size={16}\n className={twMerge(\n \"min-w-4 text-gray-50 dark:text-slate-500\",\n isSidebarOpen && \"-rotate-180\",\n )}\n />\n </button>\n </div>\n </div>\n )}\n </aside>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;AA8GA,MAAaW,WAAmC,EAC9CC,cACAC,oBACAC,OACAC,aACAC,cACAC,eAAe,GACfC,eAAe,GACfC,eAAe,MACfC,YAAY,MACZC,gBAAgB,MAChBC,mBAAmB,OACnBC,oBACAC,eAAe,KACfC,UACAC,WACAC,oBACAC,YAAY,OACZC,cACAC,gBAAgB,YACZ;CACJ,MAAM,EACJC,YACAC,eACAC,YACAC,eACAC,wBACE1B,iBAAiB;EACnB2B,cAAcZ;EACda,UAAU;EACVZ;EACD,CAAC;CAEF,MAAM,EACJa,gBACAC,UACAC,WACAC,WACAC,kBACAC,6BACA7B,OAAO8B,eACPf,cAAcgB,iBACdf,eAAegB,qBACbvC,YAAY;AAEhBN,iBAAgB;AACd,MAAIa,MACFyB,UAASzB,OAAOe,cAAcC,cAAc;IAE7C;EAAChB;EAAOyB;EAAUV;EAAcC;EAAc,CAAC;AAElD7B,iBAAgB;AACd,MACE,CAACa,SACD8B,cAAcG,SAAS,MACtBlB,iBAAiBgB,mBAAmBf,kBAAkBgB,kBAEvDP,UAASK,eAAef,cAAcC,cAAc;IAErD;EACDhB;EACA8B;EACAL;EACAV;EACAC;EACAe;EACAC;EACD,CAAC;AAKF7C,iBAAgB;AACd,MAAIgB,eAAe,EACjBuB,WAAUvB,aAAa;IAExB,CAACA,aAAa,CAAC;CAElB,MAAM+B,yBAAyBhD,aAC5BiD,SAAsB;AACrBpC,uBAAqBoC,KAAK;AAC1BvC,eAAa,kBAAkB,EAAEuC,MAAM,EAAElB,WAAWmB,QAAQ;IAE9D,CAACrC,oBAAoBkB,WACvB,CAAC;AACD9B,iBAAgB;AACdyC,mBAAiB9B,aAAa;IAC7B,CAACA,cAAc8B,iBAAiB,CAAC;AAEpCzC,iBAAgB;AACd0C,8BAA4BK,uBAAuB;IAClD,CAACA,wBAAwBL,4BAA4B,CAAC;AAEzD1C,iBAAgB;EACd,MAAMkD,iBAAiBb,eAAeS,SAAS;AAC/C,MAAI,CAAC5B,gBAAgBgC,gBAAgB;GACnC,MAAMC,mBAAmBd,eAAeA,eAAeS,SAAS,GAAGM;AACnEZ,aAAUW,iBAAiB;;IAE5B;EAACjC;EAAcmB;EAAgBG;EAAU,CAAC;AAE7C,QACE,qBAAC,SAAD;EACE,KAAKV;EACL,OAAO,EAAEuB,OAAO,mCAAmC;EACnD,WAAWpD,QACT,0IACA+B,cAAc,mBACdP,UACD;YAPH,CASGQ,iBACC,qBAAA,YAAA,EAAA,UAAA;GACE,oBAAC,eAAD;IACgBlB;IACDD;IACCG;IACMS;IAAmB,CAAA;GAGxCR,gBAAgBmB,eAAeS,SAAS,KAAK,oBAAC,oBAAD,EAAsB,CAAA;GACpE,oBAAC,oBAAD;IACgB5B;IACHS;IAAU,CAAA;GAEtBP,iBACC,oBAAC,eAAD,EAAiCC,kBAClC,CAAA;GACAC,sBACC,oBAAC,OAAD;IAAK,WAAU;cACZA;IAEJ,CAAA;GAEJ,EAAA,CAAA,EAEAH,aACC,oBAAC,OAAD;GACE,WAAU;GACV,aAAaY;aAEb,oBAAC,OAAD;IACE,WAAW9B,QACT,kJACA+B,cAAc,8BACd,CAACC,iBAAiB,gCACnB;cAED,oBAAC,UAAD;KACE,MAAK;KACL,WAAWhC,QACT,6FACA,uEACD;KACD,SAASiC;eAET,oBAAC,MAAD;MACE,MAAK;MACL,MAAM;MACN,WAAWjC,QACT,4CACAgC,iBAAiB,cAClB;MAAC,CAAA;KAEE,CAAA;IACL,CAAA;GAER,CAAA,CACK"}
|
|
1
|
+
{"version":3,"file":"sidebar.js","names":["useCallback","useEffect","twMerge","Icon","SidebarContentArea","SidebarHeader","SidebarPinningArea","useSidebar","SidebarSearch","useSidebarResize","triggerEvent","Sidebar","activeNodeId","onActiveNodeChange","nodes","sidebarIcon","sidebarTitle","defaultLevel","enableMacros","allowPinning","resizable","showSearchBar","showStatusFilter","extraFooterContent","initialWidth","maxWidth","className","handleOnTitleClick","isLoading","nodeSortType","nodeSortOrder","sidebarRef","startResizing","isResizing","isSidebarOpen","handleToggleSidebar","defaultWidth","minWidth","pinnedNodePath","setNodes","openLevel","togglePin","syncActiveNodeId","setActiveNodeChangeCallback","providerNodes","currentSortType","currentSortOrder","length","handleActiveNodeChange","node","current","hasPinnedNodes","lastPinnedNodeId","id","width"],"sources":["../../../../src/ui/components/sidebar/sidebar.tsx"],"sourcesContent":["import { useCallback, useEffect } from \"react\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Icon } from \"../../../powerhouse/index.js\";\nimport { SidebarContentArea } from \"./subcomponents/sidebar-content-area.js\";\nimport { SidebarHeader } from \"./subcomponents/sidebar-header.js\";\nimport { SidebarPinningArea } from \"./subcomponents/sidebar-pinning-area.js\";\nimport { useSidebar } from \"./subcomponents/sidebar-provider/index.js\";\nimport { SidebarSearch } from \"./subcomponents/sidebar-search/index.js\";\nimport type { NodeSortOrder, NodeSortType, SidebarNode } from \"./types.js\";\nimport { useSidebarResize } from \"./use-sidebar-resize.js\";\nimport { triggerEvent } from \"./utils.js\";\n\nexport interface SidebarProps {\n /**\n * The ID of the currently active node\n */\n activeNodeId?: string;\n /**\n * Callback function to update the active node\n */\n onActiveNodeChange?: (newNode: SidebarNode) => void;\n /**\n * The nodes to be displayed in the sidebar\n */\n nodes?: SidebarNode[];\n /**\n * Which level should be shown by default (i.e., level 1).\n * @default 1 It is 1-indexed.\n */\n defaultLevel?: number;\n /**\n * Enables the vertical resizing of the sidebar\n * @default true\n */\n resizable?: boolean;\n /**\n * Whether pinning functionality is enabled for nodes\n * @default true\n */\n allowPinning?: boolean;\n /**\n * Configurable levels (e.g., 4). If its zero, then no buttons are shown (default).\n * @default 0\n */\n enableMacros?: number;\n /**\n * Whether to display the search bar in the sidebar\n * @default true\n */\n showSearchBar?: boolean;\n /**\n * Whether to display the status filter button in the sidebar. This option is ignored if `showSearchBar` is `false`.\n * @default false\n */\n showStatusFilter?: boolean;\n /**\n * The title that appears at the top of the sidebar, providing context or a heading for the hierarchy displayed within it.\n */\n sidebarTitle?: string;\n /**\n * An optional icon that can be displayed next to the sidebar title or at the top of the sidebar.\n */\n sidebarIcon?: React.ReactNode;\n /**\n * An optional footer content that can be displayed at the bottom of the sidebar.\n */\n extraFooterContent?: React.ReactNode;\n /**\n * The initial width of the sidebar.\n * @default 300\n */\n initialWidth?: number;\n /**\n * The maximum width of the sidebar.\n */\n maxWidth?: number;\n /**\n * Optional className for the sidebar container\n */\n className?: string;\n\n /**\n * Callback function triggered when the sidebar title is clicked.\n */\n handleOnTitleClick?: () => void;\n\n /**\n * Whether the sidebar is in a loading state, displaying skeleton items\n * @default false\n */\n isLoading?: boolean;\n /**\n * The type of sorting to apply to all nodes recursively.\n * Can be \"alphabetical\", \"natural\", or a custom comparison function.\n * When undefined, nodes maintain their original order.\n * Affects all levels of the tree hierarchy.\n */\n nodeSortType?: NodeSortType;\n /**\n * The order direction for sorting nodes (\"asc\" or \"desc\").\n * Only applicable when nodeSortType is defined.\n * @default \"asc\"\n */\n nodeSortOrder?: NodeSortOrder;\n}\n\n/**\n * Sidebar component that provides a collapsible and resizable navigation panel\n * with support for hierarchical data, search, pinning, and custom styling.\n */\nexport const Sidebar: React.FC<SidebarProps> = ({\n activeNodeId,\n onActiveNodeChange,\n nodes,\n sidebarIcon,\n sidebarTitle,\n defaultLevel = 1,\n enableMacros = 0,\n allowPinning = true,\n resizable = true,\n showSearchBar = true,\n showStatusFilter = false,\n extraFooterContent,\n initialWidth = 300,\n maxWidth,\n className,\n handleOnTitleClick,\n isLoading = false,\n nodeSortType,\n nodeSortOrder = \"asc\",\n}) => {\n const {\n sidebarRef,\n startResizing,\n isResizing,\n isSidebarOpen,\n handleToggleSidebar,\n } = useSidebarResize({\n defaultWidth: initialWidth,\n minWidth: 220,\n maxWidth,\n });\n\n const {\n pinnedNodePath,\n setNodes,\n openLevel,\n togglePin,\n syncActiveNodeId,\n setActiveNodeChangeCallback,\n nodes: providerNodes,\n nodeSortType: currentSortType,\n nodeSortOrder: currentSortOrder,\n } = useSidebar();\n\n useEffect(() => {\n if (nodes) {\n setNodes(nodes, nodeSortType, nodeSortOrder);\n }\n }, [nodes, setNodes, nodeSortType, nodeSortOrder]);\n\n useEffect(() => {\n if (\n !nodes &&\n providerNodes.length > 0 &&\n (nodeSortType !== currentSortType || nodeSortOrder !== currentSortOrder)\n ) {\n setNodes(providerNodes, nodeSortType, nodeSortOrder);\n }\n }, [\n nodes,\n providerNodes,\n setNodes,\n nodeSortType,\n nodeSortOrder,\n currentSortType,\n currentSortOrder,\n ]);\n\n // Initialize default expanded level on mount.\n // openLevel intentionally omitted — it changes every time currentRoots\n // changes, which would cause an infinite loop.\n useEffect(() => {\n if (defaultLevel > 1) {\n openLevel(defaultLevel);\n }\n }, [defaultLevel]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleActiveNodeChange = useCallback(\n (node: SidebarNode) => {\n onActiveNodeChange?.(node);\n triggerEvent(\"sidebar:change\", { node }, sidebarRef.current);\n },\n [onActiveNodeChange, sidebarRef],\n );\n useEffect(() => {\n syncActiveNodeId(activeNodeId);\n }, [activeNodeId, syncActiveNodeId]);\n\n useEffect(() => {\n setActiveNodeChangeCallback(handleActiveNodeChange);\n }, [handleActiveNodeChange, setActiveNodeChangeCallback]);\n\n useEffect(() => {\n const hasPinnedNodes = pinnedNodePath.length > 0;\n if (!allowPinning && hasPinnedNodes) {\n const lastPinnedNodeId = pinnedNodePath[pinnedNodePath.length - 1].id;\n togglePin(lastPinnedNodeId);\n }\n }, [allowPinning, pinnedNodePath, togglePin]);\n\n return (\n <aside\n ref={sidebarRef}\n style={{ width: \"min(100%, var(--sidebar-width))\" }}\n className={twMerge(\n \"group peer relative flex h-svh max-h-screen flex-col bg-sidebar text-sidebar-foreground shadow-lg transition-[width] duration-75 ease-linear\",\n isResizing && \"transition-none\",\n className,\n )}\n >\n {isSidebarOpen && (\n <>\n <SidebarHeader\n sidebarTitle={sidebarTitle}\n sidebarIcon={sidebarIcon}\n enableMacros={enableMacros}\n handleOnTitleClick={handleOnTitleClick}\n />\n\n {allowPinning && pinnedNodePath.length > 0 && <SidebarPinningArea />}\n <SidebarContentArea\n allowPinning={allowPinning}\n isLoading={isLoading}\n />\n {showSearchBar && (\n <SidebarSearch showStatusFilter={showStatusFilter} />\n )}\n {extraFooterContent && (\n <div className=\"w-full border-t border-sidebar-border p-2\">\n {extraFooterContent}\n </div>\n )}\n </>\n )}\n\n {resizable && (\n <div\n className=\"group/sidebar-resizer absolute top-0 right-0 h-full w-[10px] translate-x-1/2 cursor-ew-resize select-none\"\n onMouseDown={startResizing}\n >\n <div\n className={twMerge(\n \"relative h-full w-px translate-x-[5px] transition-colors group-hover/sidebar-resizer:hover-effect\",\n isResizing && \"cursor-default bg-info\",\n !isSidebarOpen && \"bg-secondary\",\n )}\n >\n <button\n type=\"button\"\n className={twMerge(\n \"absolute top-14 right-0 size-4 translate-x-1/2 rounded-full bg-muted-foreground\",\n \"opacity-0 transition-opacity group-hover/sidebar-resizer:opacity-100\",\n )}\n onClick={handleToggleSidebar}\n >\n <Icon\n name=\"Caret\"\n size={16}\n className={twMerge(\n \"min-w-4 text-foreground\",\n isSidebarOpen && \"-rotate-180\",\n )}\n />\n </button>\n </div>\n </div>\n )}\n </aside>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;AA8GA,MAAaW,WAAmC,EAC9CC,cACAC,oBACAC,OACAC,aACAC,cACAC,eAAe,GACfC,eAAe,GACfC,eAAe,MACfC,YAAY,MACZC,gBAAgB,MAChBC,mBAAmB,OACnBC,oBACAC,eAAe,KACfC,UACAC,WACAC,oBACAC,YAAY,OACZC,cACAC,gBAAgB,YACZ;CACJ,MAAM,EACJC,YACAC,eACAC,YACAC,eACAC,wBACE1B,iBAAiB;EACnB2B,cAAcZ;EACda,UAAU;EACVZ;EACD,CAAC;CAEF,MAAM,EACJa,gBACAC,UACAC,WACAC,WACAC,kBACAC,6BACA7B,OAAO8B,eACPf,cAAcgB,iBACdf,eAAegB,qBACbvC,YAAY;AAEhBN,iBAAgB;AACd,MAAIa,MACFyB,UAASzB,OAAOe,cAAcC,cAAc;IAE7C;EAAChB;EAAOyB;EAAUV;EAAcC;EAAc,CAAC;AAElD7B,iBAAgB;AACd,MACE,CAACa,SACD8B,cAAcG,SAAS,MACtBlB,iBAAiBgB,mBAAmBf,kBAAkBgB,kBAEvDP,UAASK,eAAef,cAAcC,cAAc;IAErD;EACDhB;EACA8B;EACAL;EACAV;EACAC;EACAe;EACAC;EACD,CAAC;AAKF7C,iBAAgB;AACd,MAAIgB,eAAe,EACjBuB,WAAUvB,aAAa;IAExB,CAACA,aAAa,CAAC;CAElB,MAAM+B,yBAAyBhD,aAC5BiD,SAAsB;AACrBpC,uBAAqBoC,KAAK;AAC1BvC,eAAa,kBAAkB,EAAEuC,MAAM,EAAElB,WAAWmB,QAAQ;IAE9D,CAACrC,oBAAoBkB,WACvB,CAAC;AACD9B,iBAAgB;AACdyC,mBAAiB9B,aAAa;IAC7B,CAACA,cAAc8B,iBAAiB,CAAC;AAEpCzC,iBAAgB;AACd0C,8BAA4BK,uBAAuB;IAClD,CAACA,wBAAwBL,4BAA4B,CAAC;AAEzD1C,iBAAgB;EACd,MAAMkD,iBAAiBb,eAAeS,SAAS;AAC/C,MAAI,CAAC5B,gBAAgBgC,gBAAgB;GACnC,MAAMC,mBAAmBd,eAAeA,eAAeS,SAAS,GAAGM;AACnEZ,aAAUW,iBAAiB;;IAE5B;EAACjC;EAAcmB;EAAgBG;EAAU,CAAC;AAE7C,QACE,qBAAC,SAAD;EACE,KAAKV;EACL,OAAO,EAAEuB,OAAO,mCAAmC;EACnD,WAAWpD,QACT,gJACA+B,cAAc,mBACdP,UACD;YAPH,CASGQ,iBACC,qBAAA,YAAA,EAAA,UAAA;GACE,oBAAC,eAAD;IACgBlB;IACDD;IACCG;IACMS;IAAmB,CAAA;GAGxCR,gBAAgBmB,eAAeS,SAAS,KAAK,oBAAC,oBAAD,EAAsB,CAAA;GACpE,oBAAC,oBAAD;IACgB5B;IACHS;IAAU,CAAA;GAEtBP,iBACC,oBAAC,eAAD,EAAiCC,kBAClC,CAAA;GACAC,sBACC,oBAAC,OAAD;IAAK,WAAU;cACZA;IAEJ,CAAA;GAEJ,EAAA,CAAA,EAEAH,aACC,oBAAC,OAAD;GACE,WAAU;GACV,aAAaY;aAEb,oBAAC,OAAD;IACE,WAAW9B,QACT,qGACA+B,cAAc,0BACd,CAACC,iBAAiB,eACnB;cAED,oBAAC,UAAD;KACE,MAAK;KACL,WAAWhC,QACT,mFACA,uEACD;KACD,SAASiC;eAET,oBAAC,MAAD;MACE,MAAK;MACL,MAAM;MACN,WAAWjC,QACT,2BACAgC,iBAAiB,cAClB;MAAC,CAAA;KAEE,CAAA;IACL,CAAA;GAER,CAAA,CACK"}
|
|
@@ -27,7 +27,7 @@ const SidebarContentArea = ({ allowPinning, isLoading }) => {
|
|
|
27
27
|
return /* @__PURE__ */ jsx("div", {
|
|
28
28
|
className: twMerge("flex flex-1 flex-col gap-1 overflow-y-auto", hasPinnedItems && "pt-0.5"),
|
|
29
29
|
children: isLoading ? /* @__PURE__ */ jsx(SidebarSkeleton, {}) : flattenedNodes.length === 0 ? /* @__PURE__ */ jsxs("div", {
|
|
30
|
-
className: "flex max-w-full items-center gap-2 p-2 text-sm/5 text-
|
|
30
|
+
className: "flex max-w-full items-center gap-2 p-2 text-sm/5 text-muted-foreground",
|
|
31
31
|
children: [/* @__PURE__ */ jsx(Icon, {
|
|
32
32
|
name: "TreeViewSlash",
|
|
33
33
|
size: 16,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar-content-area.js","names":["AutoSizer","List","twMerge","SidebarItem","useSidebar","SidebarSkeleton","Icon","SidebarContentArea","allowPinning","isLoading","flattenedNodes","toggleNode","togglePin","searchTerm","searchResults","activeSearchIndex","pinnedNodePath","activeNodeId","virtualListRef","onActiveNodeChange","hasPinnedItems","length","renderNode","index","key","style","node","id","width","height"],"sources":["../../../../../src/ui/components/sidebar/subcomponents/sidebar-content-area.tsx"],"sourcesContent":["import { AutoSizer, List } from \"react-virtualized\";\nimport { twMerge } from \"tailwind-merge\";\nimport { SidebarItem } from \"./sidebar-item.js\";\nimport { useSidebar } from \"./sidebar-provider/index.js\";\nimport { SidebarSkeleton } from \"./sidebar-skeleton.js\";\nimport { Icon } from \"../../../../powerhouse/index.js\";\n\ninterface SidebarContentAreaProps {\n allowPinning?: boolean;\n isLoading?: boolean;\n}\n\nexport const SidebarContentArea = ({\n allowPinning,\n isLoading,\n}: SidebarContentAreaProps) => {\n const {\n flattenedNodes,\n toggleNode,\n togglePin,\n searchTerm,\n searchResults,\n activeSearchIndex,\n pinnedNodePath,\n activeNodeId,\n virtualListRef,\n onActiveNodeChange,\n } = useSidebar();\n const hasPinnedItems = allowPinning && pinnedNodePath.length > 0;\n\n const renderNode = ({\n index,\n key,\n style,\n }: {\n index: number;\n key: string;\n style: React.CSSProperties;\n }) => {\n const node = flattenedNodes[index];\n\n return (\n <SidebarItem\n key={key}\n node={node}\n toggleNode={toggleNode}\n togglePin={togglePin}\n searchTerm={searchTerm}\n searchResults={searchResults}\n activeSearchIndex={activeSearchIndex}\n allowPinning={allowPinning ?? false}\n isActive={activeNodeId === node.id}\n onChange={onActiveNodeChange}\n style={style}\n />\n );\n };\n\n return (\n <div\n className={twMerge(\n \"flex flex-1 flex-col gap-1 overflow-y-auto\",\n hasPinnedItems && \"pt-0.5\",\n )}\n >\n {isLoading ? (\n <SidebarSkeleton />\n ) : flattenedNodes.length === 0 ? (\n <div className=\"flex max-w-full items-center gap-2 p-2 text-sm/5 text-
|
|
1
|
+
{"version":3,"file":"sidebar-content-area.js","names":["AutoSizer","List","twMerge","SidebarItem","useSidebar","SidebarSkeleton","Icon","SidebarContentArea","allowPinning","isLoading","flattenedNodes","toggleNode","togglePin","searchTerm","searchResults","activeSearchIndex","pinnedNodePath","activeNodeId","virtualListRef","onActiveNodeChange","hasPinnedItems","length","renderNode","index","key","style","node","id","width","height"],"sources":["../../../../../src/ui/components/sidebar/subcomponents/sidebar-content-area.tsx"],"sourcesContent":["import { AutoSizer, List } from \"react-virtualized\";\nimport { twMerge } from \"tailwind-merge\";\nimport { SidebarItem } from \"./sidebar-item.js\";\nimport { useSidebar } from \"./sidebar-provider/index.js\";\nimport { SidebarSkeleton } from \"./sidebar-skeleton.js\";\nimport { Icon } from \"../../../../powerhouse/index.js\";\n\ninterface SidebarContentAreaProps {\n allowPinning?: boolean;\n isLoading?: boolean;\n}\n\nexport const SidebarContentArea = ({\n allowPinning,\n isLoading,\n}: SidebarContentAreaProps) => {\n const {\n flattenedNodes,\n toggleNode,\n togglePin,\n searchTerm,\n searchResults,\n activeSearchIndex,\n pinnedNodePath,\n activeNodeId,\n virtualListRef,\n onActiveNodeChange,\n } = useSidebar();\n const hasPinnedItems = allowPinning && pinnedNodePath.length > 0;\n\n const renderNode = ({\n index,\n key,\n style,\n }: {\n index: number;\n key: string;\n style: React.CSSProperties;\n }) => {\n const node = flattenedNodes[index];\n\n return (\n <SidebarItem\n key={key}\n node={node}\n toggleNode={toggleNode}\n togglePin={togglePin}\n searchTerm={searchTerm}\n searchResults={searchResults}\n activeSearchIndex={activeSearchIndex}\n allowPinning={allowPinning ?? false}\n isActive={activeNodeId === node.id}\n onChange={onActiveNodeChange}\n style={style}\n />\n );\n };\n\n return (\n <div\n className={twMerge(\n \"flex flex-1 flex-col gap-1 overflow-y-auto\",\n hasPinnedItems && \"pt-0.5\",\n )}\n >\n {isLoading ? (\n <SidebarSkeleton />\n ) : flattenedNodes.length === 0 ? (\n <div className=\"flex max-w-full items-center gap-2 p-2 text-sm/5 text-muted-foreground\">\n <Icon name=\"TreeViewSlash\" size={16} className=\"min-w-4\" />\n <span className=\"truncate\">This node is empty</span>\n </div>\n ) : (\n <div className=\"flex-1 overflow-hidden\">\n <AutoSizer>\n {({ width, height }) => (\n <List\n ref={virtualListRef}\n className=\"p-2\"\n width={width}\n height={height}\n rowCount={flattenedNodes.length}\n rowHeight={32 + 8}\n rowRenderer={renderNode}\n />\n )}\n </AutoSizer>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;AAYA,MAAaO,sBAAsB,EACjCC,cACAC,gBAC6B;CAC7B,MAAM,EACJC,gBACAC,YACAC,WACAC,YACAC,eACAC,mBACAC,gBACAC,cACAC,gBACAC,uBACEf,YAAY;CAChB,MAAMgB,iBAAiBZ,gBAAgBQ,eAAeK,SAAS;CAE/D,MAAMC,cAAc,EAClBC,OACAC,KACAC,YAKI;EACJ,MAAMC,OAAOhB,eAAea;AAE5B,SACE,oBAAC,aAAD;GAEQG;GACMf;GACDC;GACCC;GACGC;GACIC;GACnB,cAAcP,gBAAgB;GAC9B,UAAUS,iBAAiBS,KAAKC;GAChC,UAAUR;GACHM;GACP,EAXKD,IAWL;;AAIN,QACE,oBAAC,OAAD;EACE,WAAWtB,QACT,8CACAkB,kBAAkB,SACnB;YAEAX,YACC,oBAAC,iBAAD,EAAmB,CAAA,GACjBC,eAAeW,WAAW,IAC5B,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,MAAD;IAAM,MAAK;IAAgB,MAAM;IAAI,WAAU;IAAS,CAAA,EACxD,oBAAC,QAAD;IAAM,WAAU;cAAW;IAAwB,CAAA,CAC/C;OAEN,oBAAC,OAAD;GAAK,WAAU;aACb,oBAAC,WAAD,EAAA,WACI,EAAEO,OAAOC,aACT,oBAAC,MAAD;IACE,KAAKX;IACL,WAAU;IACHU;IACCC;IACR,UAAUnB,eAAeW;IACzB,WAAW;IACX,aAAaC;IAEhB,CAAA,EACQ,CAAA;GAEd,CAAA;EACG,CAAA"}
|
|
@@ -6,7 +6,7 @@ const SidebarHeader = ({ sidebarTitle, sidebarIcon, enableMacros = 0, handleOnTi
|
|
|
6
6
|
const { maxDepth, openLevel } = useSidebar();
|
|
7
7
|
if (!sidebarTitle && !sidebarIcon && !enableMacros) return null;
|
|
8
8
|
let titleElement;
|
|
9
|
-
const baseTitleClasses = "truncate text-sm font-semibold text-
|
|
9
|
+
const baseTitleClasses = "truncate text-sm font-semibold text-foreground";
|
|
10
10
|
if (handleOnTitleClick) titleElement = /* @__PURE__ */ jsx("button", {
|
|
11
11
|
type: "button",
|
|
12
12
|
className: twMerge(baseTitleClasses, "cursor-pointer"),
|
|
@@ -18,7 +18,7 @@ const SidebarHeader = ({ sidebarTitle, sidebarIcon, enableMacros = 0, handleOnTi
|
|
|
18
18
|
children: sidebarTitle
|
|
19
19
|
});
|
|
20
20
|
return /* @__PURE__ */ jsxs("header", {
|
|
21
|
-
className: "flex items-center justify-between gap-2 border-b border-
|
|
21
|
+
className: "flex items-center justify-between gap-2 border-b border-sidebar-border bg-sidebar p-4",
|
|
22
22
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
23
23
|
className: "flex items-center gap-2 truncate",
|
|
24
24
|
children: [sidebarIcon, titleElement]
|
|
@@ -29,7 +29,7 @@ const SidebarHeader = ({ sidebarTitle, sidebarIcon, enableMacros = 0, handleOnTi
|
|
|
29
29
|
return /* @__PURE__ */ jsx("div", {
|
|
30
30
|
role: "button",
|
|
31
31
|
tabIndex: 0,
|
|
32
|
-
className: twMerge("w-[26px] rounded-lg bg-
|
|
32
|
+
className: twMerge("w-[26px] rounded-lg bg-sidebar p-1 text-center text-xs text-sidebar-foreground", !isDisabled && "hover:hover-effect", isDisabled && "cursor-not-allowed bg-muted text-muted-foreground"),
|
|
33
33
|
onClick: () => {
|
|
34
34
|
if (!isDisabled) openLevel(index + 1);
|
|
35
35
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar-header.js","names":["twMerge","useSidebar","SidebarHeader","sidebarTitle","sidebarIcon","enableMacros","handleOnTitleClick","maxDepth","openLevel","titleElement","baseTitleClasses","Array","from","length","Math","min","map","_","index","isDisabled"],"sources":["../../../../../src/ui/components/sidebar/subcomponents/sidebar-header.tsx"],"sourcesContent":["import { twMerge } from \"tailwind-merge\";\nimport { useSidebar } from \"./sidebar-provider/index.js\";\n\ninterface SidebarHeaderProps {\n sidebarTitle?: string;\n sidebarIcon?: React.ReactNode;\n enableMacros?: number;\n handleOnTitleClick?: () => void;\n}\n\nexport const SidebarHeader: React.FC<SidebarHeaderProps> = ({\n sidebarTitle,\n sidebarIcon,\n enableMacros = 0,\n handleOnTitleClick,\n}) => {\n const { maxDepth, openLevel } = useSidebar();\n if (!sidebarTitle && !sidebarIcon && !enableMacros) {\n return null;\n }\n let titleElement;\n const baseTitleClasses
|
|
1
|
+
{"version":3,"file":"sidebar-header.js","names":["twMerge","useSidebar","SidebarHeader","sidebarTitle","sidebarIcon","enableMacros","handleOnTitleClick","maxDepth","openLevel","titleElement","baseTitleClasses","Array","from","length","Math","min","map","_","index","isDisabled"],"sources":["../../../../../src/ui/components/sidebar/subcomponents/sidebar-header.tsx"],"sourcesContent":["import { twMerge } from \"tailwind-merge\";\nimport { useSidebar } from \"./sidebar-provider/index.js\";\n\ninterface SidebarHeaderProps {\n sidebarTitle?: string;\n sidebarIcon?: React.ReactNode;\n enableMacros?: number;\n handleOnTitleClick?: () => void;\n}\n\nexport const SidebarHeader: React.FC<SidebarHeaderProps> = ({\n sidebarTitle,\n sidebarIcon,\n enableMacros = 0,\n handleOnTitleClick,\n}) => {\n const { maxDepth, openLevel } = useSidebar();\n if (!sidebarTitle && !sidebarIcon && !enableMacros) {\n return null;\n }\n let titleElement;\n const baseTitleClasses = \"truncate text-sm font-semibold text-foreground\";\n if (handleOnTitleClick) {\n titleElement = (\n <button\n type=\"button\"\n className={twMerge(baseTitleClasses, \"cursor-pointer\")}\n onClick={handleOnTitleClick}\n >\n {sidebarTitle}\n </button>\n );\n } else {\n titleElement = <div className={baseTitleClasses}>{sidebarTitle}</div>;\n }\n\n return (\n <header className=\"flex items-center justify-between gap-2 border-b border-sidebar-border bg-sidebar p-4\">\n <div className=\"flex items-center gap-2 truncate\">\n {sidebarIcon}\n {titleElement}\n </div>\n\n {enableMacros > 0 && (\n <div className=\"flex items-center gap-2 select-none\">\n {Array.from({ length: Math.min(enableMacros, 4) }).map((_, index) => {\n const isDisabled = index + 1 > maxDepth;\n\n return (\n <div\n key={`macro-${index}`}\n role=\"button\"\n tabIndex={0}\n className={twMerge(\n \"w-[26px] rounded-lg bg-sidebar p-1 text-center text-xs text-sidebar-foreground\",\n !isDisabled && \"hover:hover-effect\",\n isDisabled &&\n \"cursor-not-allowed bg-muted text-muted-foreground\",\n )}\n onClick={() => {\n if (!isDisabled) {\n openLevel(index + 1);\n }\n }}\n >\n {index + 1}\n </div>\n );\n })}\n </div>\n )}\n </header>\n );\n};\n"],"mappings":";;;;AAUA,MAAaE,iBAA+C,EAC1DC,cACAC,aACAC,eAAe,GACfC,yBACI;CACJ,MAAM,EAAEC,UAAUC,cAAcP,YAAY;AAC5C,KAAI,CAACE,gBAAgB,CAACC,eAAe,CAACC,aACpC,QAAO;CAET,IAAII;CACJ,MAAMC,mBAAmB;AACzB,KAAIJ,mBACFG,gBACE,oBAAC,UAAD;EACE,MAAK;EACL,WAAWT,QAAQU,kBAAkB,iBAAiB;EACtD,SAASJ;YAERH;EAEJ,CAAA;KAEDM,gBAAe,oBAAC,OAAD;EAAK,WAAWC;YAAmBP;EAAmB,CAAA;AAGvE,QACE,qBAAC,UAAD;EAAQ,WAAU;YAAlB,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACGC,aACAK,aACE;MAEJJ,eAAe,KACd,oBAAC,OAAD;GAAK,WAAU;aACZM,MAAMC,KAAK,EAAEC,QAAQC,KAAKC,IAAIV,cAAc,EAAC,EAAG,CAAC,CAACW,KAAKC,GAAGC,UAAU;IACnE,MAAMC,aAAaD,QAAQ,IAAIX;AAE/B,WACE,oBAAC,OAAD;KAEE,MAAK;KACL,UAAU;KACV,WAAWP,QACT,kFACA,CAACmB,cAAc,sBACfA,cACE,oDACH;KACD,eAAe;AACb,UAAI,CAACA,WACHX,WAAUU,QAAQ,EAAE;;eAIvBA,QAAQ;KACL,EAhBC,SAASA,QAgBV;KAER;GAEL,CAAA,CACM"}
|
|
@@ -40,7 +40,7 @@ const SidebarItem = ({ node, toggleNode, togglePin, searchTerm, searchResults, a
|
|
|
40
40
|
tabIndex: 0,
|
|
41
41
|
"data-testid": "sidebar-item",
|
|
42
42
|
id: `sidebar-item-${node.id}`,
|
|
43
|
-
className: twMerge("group/sidebar-item relative flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-
|
|
43
|
+
className: twMerge("group/sidebar-item relative flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-foreground select-none hover:hover-effect", hasStatus && "pr-6", allowPinning && (hasStatus ? "hover:pr-12" : "hover:pr-6"), isPinned && (hasStatus ? "pr-12" : "pr-6"), isSearchActive && "bg-warning/10", pinnedMode && "after:absolute after:-top-2.5 after:left-[15px] after:h-4 after:w-px after:bg-secondary first:group-first/sidebar-item-wrapper:after:hidden hover:hover-effect", isActive && "bg-sidebar-accent font-medium text-sidebar-accent-foreground hover:hover-effect", node.className),
|
|
44
44
|
onClick: () => {
|
|
45
45
|
toggleNode?.(node.id);
|
|
46
46
|
onChange?.(node);
|
|
@@ -49,7 +49,7 @@ const SidebarItem = ({ node, toggleNode, togglePin, searchTerm, searchResults, a
|
|
|
49
49
|
className: "flex max-w-full items-center gap-2",
|
|
50
50
|
children: [
|
|
51
51
|
!pinnedMode && /* @__PURE__ */ jsx("div", {
|
|
52
|
-
className: "-m-2 -mr-1 h-full rounded-md py-2 pr-1 pl-2 hover:
|
|
52
|
+
className: "-m-2 -mr-1 h-full rounded-md py-2 pr-1 pl-2 hover:hover-effect",
|
|
53
53
|
onClick: (e) => {
|
|
54
54
|
e.stopPropagation();
|
|
55
55
|
toggleNode?.(node.id);
|
|
@@ -57,7 +57,7 @@ const SidebarItem = ({ node, toggleNode, togglePin, searchTerm, searchResults, a
|
|
|
57
57
|
children: /* @__PURE__ */ jsx(CaretDown, {
|
|
58
58
|
width: "16",
|
|
59
59
|
height: "16",
|
|
60
|
-
className: twMerge("min-w-4", node.isExpanded && node.children && node.children.length > 0 ? "" : "-rotate-90", node.children === void 0 || node.children.length === 0 ? "text-
|
|
60
|
+
className: twMerge("min-w-4", node.isExpanded && node.children && node.children.length > 0 ? "" : "-rotate-90", node.children === void 0 || node.children.length === 0 ? "text-muted-foreground" : "text-foreground")
|
|
61
61
|
})
|
|
62
62
|
}),
|
|
63
63
|
IconComponent ? typeof IconComponent === "string" ? /* @__PURE__ */ jsx(Icon, {
|
|
@@ -74,7 +74,7 @@ const SidebarItem = ({ node, toggleNode, togglePin, searchTerm, searchResults, a
|
|
|
74
74
|
className: ""
|
|
75
75
|
}),
|
|
76
76
|
allowPinning && /* @__PURE__ */ jsx("div", {
|
|
77
|
-
className: twMerge("absolute top-1/2 flex -translate-y-1/2 items-center justify-center", hasStatus ? "right-8" : "right-2", isPinned ? "text-
|
|
77
|
+
className: twMerge("absolute top-1/2 flex -translate-y-1/2 items-center justify-center", hasStatus ? "right-8" : "right-2", isPinned ? "text-foreground hover:hover-effect" : "invisible text-muted-foreground group-hover/sidebar-item:visible hover:hover-effect"),
|
|
78
78
|
onClick: (e) => {
|
|
79
79
|
e.stopPropagation();
|
|
80
80
|
togglePin(node.id);
|
|
@@ -88,7 +88,7 @@ const SidebarItem = ({ node, toggleNode, togglePin, searchTerm, searchResults, a
|
|
|
88
88
|
})
|
|
89
89
|
}),
|
|
90
90
|
hasStatus && /* @__PURE__ */ jsx("div", {
|
|
91
|
-
className: twMerge("absolute top-1/2 right-2 flex -translate-y-1/2 items-center justify-center", "text-
|
|
91
|
+
className: twMerge("absolute top-1/2 right-2 flex -translate-y-1/2 items-center justify-center", "text-foreground group-hover/sidebar-item:visible hover:hover-effect"),
|
|
92
92
|
onClick: (e) => {
|
|
93
93
|
e.stopPropagation();
|
|
94
94
|
togglePin(node.id);
|
|
@@ -109,7 +109,7 @@ const RenderTitle = /* @__PURE__ */ forwardRef(({ title, searchTerm, isSearchAct
|
|
|
109
109
|
ref,
|
|
110
110
|
className: twMerge("truncate text-sm/5", className),
|
|
111
111
|
children: searchTerm && title.toLowerCase().includes(searchTerm.toLowerCase()) && !pinnedMode ? (() => {
|
|
112
|
-
const highlightClass = isSearchActive ? "bg-
|
|
112
|
+
const highlightClass = isSearchActive ? "bg-warning/10" : "bg-secondary";
|
|
113
113
|
const parts = [];
|
|
114
114
|
let remaining = title;
|
|
115
115
|
const lowerTerm = searchTerm.toLowerCase();
|
|
@@ -147,7 +147,7 @@ const PinnedModeCircleIcon = ({ isPinned }) => /* @__PURE__ */ jsxs("svg", {
|
|
|
147
147
|
}), /* @__PURE__ */ jsx("path", {
|
|
148
148
|
d: "M12 8C12 10.2091 10.2091 12 8 12C5.79086 12 4 10.2091 4 8C4 5.79086 5.79086 4 8 4C10.2091 4 12 5.79086 12 8Z",
|
|
149
149
|
fill: "currentColor",
|
|
150
|
-
className: isPinned ? "text-
|
|
150
|
+
className: isPinned ? "text-muted-foreground" : "text-foreground"
|
|
151
151
|
})]
|
|
152
152
|
});
|
|
153
153
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar-item.js","names":["cloneElement","forwardRef","useMemo","useRef","twMerge","CaretDown","Pin","PinFilled","Tooltip","TooltipProvider","NodeStatus","useEllipsis","StatusIcon","Icon","TOOLTIP_DELAY","TOOLTIP_DELAY_LONG","SidebarItem","node","toggleNode","togglePin","searchTerm","searchResults","activeSearchIndex","allowPinning","pinnedMode","isPinned","isActive","style","onChange","paddingLeft","depth","isSearchActive","length","id","IconComponent","isExpanded","expandedIcon","icon","isDescendenceModified","check","n","status","UNCHANGED","children","some","child","hasStatus","computedStyle","ellipsisRef","hasEllipsis","title","className","e","stopPropagation","undefined","RenderTitle","ref","toLowerCase","includes","highlightClass","parts","remaining","lowerTerm","i","idx","indexOf","push","slice","displayName","PinnedModeCircleIcon"],"sources":["../../../../../src/ui/components/sidebar/subcomponents/sidebar-item.tsx"],"sourcesContent":["import { cloneElement, forwardRef, useMemo, useRef } from \"react\";\nimport { twMerge } from \"tailwind-merge\";\nimport CaretDown from \"../../../../powerhouse/components/icon-components/CaretDown.js\";\nimport Pin from \"../../../../powerhouse/components/icon-components/Pin.js\";\nimport PinFilled from \"../../../../powerhouse/components/icon-components/PinFilled.js\";\nimport { Tooltip, TooltipProvider } from \"../../tooltip/tooltip.js\";\nimport { type FlattenedNode, NodeStatus, type SidebarNode } from \"../types.js\";\nimport { useEllipsis } from \"../use-ellipsis.js\";\nimport { StatusIcon } from \"./status-icon.js\";\nimport { Icon } from \"../../../../powerhouse/index.js\";\n\ninterface SidebarItemProps {\n node: FlattenedNode;\n toggleNode?: (nodeId: string) => void;\n togglePin: (nodeId: string) => void;\n searchTerm: string;\n searchResults: SidebarNode[];\n activeSearchIndex: number;\n allowPinning: boolean;\n pinnedMode?: boolean;\n isPinned?: boolean;\n isActive?: boolean;\n style?: React.CSSProperties;\n onChange?: (node: SidebarNode) => void;\n}\n\nconst TOOLTIP_DELAY = 700;\nconst TOOLTIP_DELAY_LONG = 172800000; // 2 days to simulate no tooltip\n\nexport const SidebarItem = ({\n node,\n toggleNode,\n togglePin,\n searchTerm,\n searchResults,\n activeSearchIndex,\n allowPinning,\n pinnedMode = false,\n isPinned = false,\n isActive = false,\n style,\n onChange,\n}: SidebarItemProps) => {\n const paddingLeft = node.depth * 24;\n const isSearchActive =\n searchResults.length > 0 && searchResults[activeSearchIndex].id === node.id;\n const IconComponent = node.isExpanded\n ? (node.expandedIcon ?? node.icon)\n : node.icon;\n const isDescendenceModified = useMemo(() => {\n const check = (n: SidebarNode): boolean => {\n // Check current node's status first\n if (n.status !== NodeStatus.UNCHANGED && n.status) {\n return true;\n }\n\n // Then recursively check all children\n if (n.children && n.children.length > 0) {\n return n.children.some((child) => check(child));\n }\n\n return false;\n };\n\n return check(node);\n }, [node]);\n const hasStatus =\n (node.status && node.status !== NodeStatus.UNCHANGED) ||\n isDescendenceModified;\n\n const computedStyle = { ...style, paddingLeft };\n\n // Check if the title has ellipsis to determine if the tooltip should be delayed\n const ellipsisRef = useRef<HTMLDivElement | null>(null);\n const hasEllipsis = useEllipsis(ellipsisRef);\n\n return (\n <TooltipProvider>\n <Tooltip\n content={node.title}\n triggerAsChild\n side=\"bottom\"\n delayDuration={hasEllipsis ? TOOLTIP_DELAY : TOOLTIP_DELAY_LONG}\n >\n <div\n style={computedStyle}\n className={twMerge(\n \"group/sidebar-item-wrapper flex w-full items-center\",\n !pinnedMode && \"pb-2\",\n )}\n >\n <div\n tabIndex={0}\n data-testid=\"sidebar-item\"\n id={`sidebar-item-${node.id}`}\n className={twMerge(\n \"group/sidebar-item relative flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-gray-700 select-none hover:bg-gray-100 dark:text-slate-400 dark:hover:bg-slate-900\",\n hasStatus && \"pr-6\",\n allowPinning && (hasStatus ? \"hover:pr-12\" : \"hover:pr-6\"),\n isPinned && (hasStatus ? \"pr-12\" : \"pr-6\"),\n isSearchActive && \"bg-yellow-100 dark:bg-[#604B0033]\",\n // line between pinned items\n pinnedMode &&\n \"after:absolute after:-top-2.5 after:left-[15px] after:h-4 after:w-px after:bg-gray-300 first:group-first/sidebar-item-wrapper:after:hidden hover:bg-gray-50 dark:hover:bg-slate-600\",\n isActive &&\n \"bg-gray-200 font-medium text-gray-900 hover:bg-gray-200 dark:bg-slate-900 dark:text-slate-50 dark:hover:bg-slate-900\",\n node.className,\n )}\n onClick={() => {\n toggleNode?.(node.id);\n onChange?.(node);\n }}\n >\n <div className=\"flex max-w-full items-center gap-2\">\n {!pinnedMode && (\n <div\n className=\"-m-2 -mr-1 h-full rounded-md py-2 pr-1 pl-2 hover:bg-gray-200\"\n onClick={(e) => {\n e.stopPropagation();\n toggleNode?.(node.id);\n }}\n >\n <CaretDown\n width=\"16\"\n height=\"16\"\n className={twMerge(\n \"min-w-4\",\n node.isExpanded &&\n node.children &&\n node.children.length > 0\n ? \"\"\n : \"-rotate-90\",\n node.children === undefined || node.children.length === 0\n ? \"text-gray-300 dark:text-slate-500\"\n : \"text-gray-700 dark:text-slate-400\",\n )}\n />\n </div>\n )}\n\n {IconComponent ? (\n typeof IconComponent === \"string\" ? (\n <Icon name={IconComponent} size={16} className=\"min-w-4\" />\n ) : (\n // @ts-expect-error -- this is a workaround\n cloneElement(IconComponent, { className: \"min-w-4 w-4\" })\n )\n ) : pinnedMode ? (\n <PinnedModeCircleIcon isPinned={isPinned} />\n ) : null}\n\n <RenderTitle\n ref={ellipsisRef}\n title={node.title}\n searchTerm={searchTerm}\n isSearchActive={isSearchActive}\n pinnedMode={pinnedMode}\n className=\"\"\n />\n\n {allowPinning && (\n <div\n className={twMerge(\n \"absolute top-1/2 flex -translate-y-1/2 items-center justify-center\",\n hasStatus ? \"right-8\" : \"right-2\",\n isPinned\n ? \"text-gray-700 hover:text-blue-900 dark:text-slate-50 dark:hover:text-blue-900\"\n : \"invisible text-gray-300 group-hover/sidebar-item:visible hover:text-gray-700 dark:text-slate-200 dark:hover:text-slate-100\",\n )}\n onClick={(e) => {\n e.stopPropagation();\n togglePin(node.id);\n }}\n >\n {isPinned ? (\n <PinFilled width=\"16\" height=\"16\" />\n ) : (\n <Pin width=\"16\" height=\"16\" />\n )}\n </div>\n )}\n {hasStatus && (\n <div\n className={twMerge(\n \"absolute top-1/2 right-2 flex -translate-y-1/2 items-center justify-center\",\n \"text-gray-300 group-hover/sidebar-item:visible hover:text-gray-700 dark:text-slate-600 dark:hover:text-slate-500\",\n )}\n onClick={(e) => {\n e.stopPropagation();\n togglePin(node.id);\n }}\n >\n <StatusIcon\n status={node.status ?? NodeStatus.UNCHANGED}\n isDescendenceModified={isDescendenceModified}\n />\n </div>\n )}\n </div>\n </div>\n </div>\n </Tooltip>\n </TooltipProvider>\n );\n};\n\nconst RenderTitle = forwardRef<\n HTMLDivElement,\n {\n title: string;\n searchTerm: string;\n isSearchActive: boolean;\n pinnedMode: boolean;\n className?: string;\n }\n>(({ title, searchTerm, isSearchActive, pinnedMode, className }, ref) => {\n return (\n <div ref={ref} className={twMerge(\"truncate text-sm/5\", className)}>\n {searchTerm &&\n title.toLowerCase().includes(searchTerm.toLowerCase()) &&\n !pinnedMode\n ? (() => {\n const highlightClass = isSearchActive\n ? \"bg-yellow-300 dark:bg-[#604B00]\"\n : \"bg-gray-300 dark:bg-slate-800\";\n const parts: React.ReactNode[] = [];\n let remaining = title;\n const lowerTerm = searchTerm.toLowerCase();\n let i = 0;\n while (remaining.length > 0) {\n const idx = remaining.toLowerCase().indexOf(lowerTerm);\n if (idx === -1) {\n parts.push(remaining);\n break;\n }\n if (idx > 0) parts.push(remaining.slice(0, idx));\n parts.push(\n <span key={i++} className={highlightClass}>\n {remaining.slice(idx, idx + searchTerm.length)}\n </span>,\n );\n remaining = remaining.slice(idx + searchTerm.length);\n }\n return <>{parts}</>;\n })()\n : title}\n </div>\n );\n});\n\nRenderTitle.displayName = \"RenderTitle\";\n\nconst PinnedModeCircleIcon = ({ isPinned }: { isPinned: boolean }) => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"min-w-4\"\n >\n <rect width=\"16\" height=\"16\" rx=\"6.4\" fill=\"transparent\" />\n <path\n d=\"M12 8C12 10.2091 10.2091 12 8 12C5.79086 12 4 10.2091 4 8C4 5.79086 5.79086 4 8 4C10.2091 4 12 5.79086 12 8Z\"\n fill=\"currentColor\"\n className={\n isPinned\n ? \"text-gray-500 dark:text-slate-500\"\n : \"text-gray-300 dark:text-slate-300\"\n }\n />\n </svg>\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sidebar-item.js","names":["cloneElement","forwardRef","useMemo","useRef","twMerge","CaretDown","Pin","PinFilled","Icon","Tooltip","TooltipProvider","NodeStatus","useEllipsis","StatusIcon","TOOLTIP_DELAY","TOOLTIP_DELAY_LONG","SidebarItem","node","toggleNode","togglePin","searchTerm","searchResults","activeSearchIndex","allowPinning","pinnedMode","isPinned","isActive","style","onChange","paddingLeft","depth","isSearchActive","length","id","IconComponent","isExpanded","expandedIcon","icon","isDescendenceModified","check","n","status","UNCHANGED","children","some","child","hasStatus","computedStyle","ellipsisRef","hasEllipsis","title","className","e","stopPropagation","undefined","RenderTitle","ref","toLowerCase","includes","highlightClass","parts","remaining","lowerTerm","i","idx","indexOf","push","slice","displayName","PinnedModeCircleIcon"],"sources":["../../../../../src/ui/components/sidebar/subcomponents/sidebar-item.tsx"],"sourcesContent":["import { cloneElement, forwardRef, useMemo, useRef } from \"react\";\nimport { twMerge } from \"tailwind-merge\";\nimport CaretDown from \"../../../../powerhouse/components/icon-components/CaretDown.js\";\nimport Pin from \"../../../../powerhouse/components/icon-components/Pin.js\";\nimport PinFilled from \"../../../../powerhouse/components/icon-components/PinFilled.js\";\nimport { Icon } from \"../../../../powerhouse/index.js\";\nimport { Tooltip, TooltipProvider } from \"../../tooltip/tooltip.js\";\nimport { type FlattenedNode, NodeStatus, type SidebarNode } from \"../types.js\";\nimport { useEllipsis } from \"../use-ellipsis.js\";\nimport { StatusIcon } from \"./status-icon.js\";\n\ninterface SidebarItemProps {\n node: FlattenedNode;\n toggleNode?: (nodeId: string) => void;\n togglePin: (nodeId: string) => void;\n searchTerm: string;\n searchResults: SidebarNode[];\n activeSearchIndex: number;\n allowPinning: boolean;\n pinnedMode?: boolean;\n isPinned?: boolean;\n isActive?: boolean;\n style?: React.CSSProperties;\n onChange?: (node: SidebarNode) => void;\n}\n\nconst TOOLTIP_DELAY = 700;\nconst TOOLTIP_DELAY_LONG = 172800000; // 2 days to simulate no tooltip\n\nexport const SidebarItem = ({\n node,\n toggleNode,\n togglePin,\n searchTerm,\n searchResults,\n activeSearchIndex,\n allowPinning,\n pinnedMode = false,\n isPinned = false,\n isActive = false,\n style,\n onChange,\n}: SidebarItemProps) => {\n const paddingLeft = node.depth * 24;\n const isSearchActive =\n searchResults.length > 0 && searchResults[activeSearchIndex].id === node.id;\n const IconComponent = node.isExpanded\n ? (node.expandedIcon ?? node.icon)\n : node.icon;\n const isDescendenceModified = useMemo(() => {\n const check = (n: SidebarNode): boolean => {\n // Check current node's status first\n if (n.status !== NodeStatus.UNCHANGED && n.status) {\n return true;\n }\n\n // Then recursively check all children\n if (n.children && n.children.length > 0) {\n return n.children.some((child) => check(child));\n }\n\n return false;\n };\n\n return check(node);\n }, [node]);\n const hasStatus =\n (node.status && node.status !== NodeStatus.UNCHANGED) ||\n isDescendenceModified;\n\n const computedStyle = { ...style, paddingLeft };\n\n // Check if the title has ellipsis to determine if the tooltip should be delayed\n const ellipsisRef = useRef<HTMLDivElement | null>(null);\n const hasEllipsis = useEllipsis(ellipsisRef);\n\n return (\n <TooltipProvider>\n <Tooltip\n content={node.title}\n triggerAsChild\n side=\"bottom\"\n delayDuration={hasEllipsis ? TOOLTIP_DELAY : TOOLTIP_DELAY_LONG}\n >\n <div\n style={computedStyle}\n className={twMerge(\n \"group/sidebar-item-wrapper flex w-full items-center\",\n !pinnedMode && \"pb-2\",\n )}\n >\n <div\n tabIndex={0}\n data-testid=\"sidebar-item\"\n id={`sidebar-item-${node.id}`}\n className={twMerge(\n \"group/sidebar-item relative flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-foreground select-none hover:hover-effect\",\n hasStatus && \"pr-6\",\n allowPinning && (hasStatus ? \"hover:pr-12\" : \"hover:pr-6\"),\n isPinned && (hasStatus ? \"pr-12\" : \"pr-6\"),\n isSearchActive && \"bg-warning/10\",\n // line between pinned items\n pinnedMode &&\n \"after:absolute after:-top-2.5 after:left-[15px] after:h-4 after:w-px after:bg-secondary first:group-first/sidebar-item-wrapper:after:hidden hover:hover-effect\",\n isActive &&\n \"bg-sidebar-accent font-medium text-sidebar-accent-foreground hover:hover-effect\",\n node.className,\n )}\n onClick={() => {\n toggleNode?.(node.id);\n onChange?.(node);\n }}\n >\n <div className=\"flex max-w-full items-center gap-2\">\n {!pinnedMode && (\n <div\n className=\"-m-2 -mr-1 h-full rounded-md py-2 pr-1 pl-2 hover:hover-effect\"\n onClick={(e) => {\n e.stopPropagation();\n toggleNode?.(node.id);\n }}\n >\n <CaretDown\n width=\"16\"\n height=\"16\"\n className={twMerge(\n \"min-w-4\",\n node.isExpanded &&\n node.children &&\n node.children.length > 0\n ? \"\"\n : \"-rotate-90\",\n node.children === undefined || node.children.length === 0\n ? \"text-muted-foreground\"\n : \"text-foreground\",\n )}\n />\n </div>\n )}\n\n {IconComponent ? (\n typeof IconComponent === \"string\" ? (\n <Icon name={IconComponent} size={16} className=\"min-w-4\" />\n ) : (\n // @ts-expect-error -- this is a workaround\n cloneElement(IconComponent, { className: \"min-w-4 w-4\" })\n )\n ) : pinnedMode ? (\n <PinnedModeCircleIcon isPinned={isPinned} />\n ) : null}\n\n <RenderTitle\n ref={ellipsisRef}\n title={node.title}\n searchTerm={searchTerm}\n isSearchActive={isSearchActive}\n pinnedMode={pinnedMode}\n className=\"\"\n />\n\n {allowPinning && (\n <div\n className={twMerge(\n \"absolute top-1/2 flex -translate-y-1/2 items-center justify-center\",\n hasStatus ? \"right-8\" : \"right-2\",\n isPinned\n ? \"text-foreground hover:hover-effect\"\n : \"invisible text-muted-foreground group-hover/sidebar-item:visible hover:hover-effect\",\n )}\n onClick={(e) => {\n e.stopPropagation();\n togglePin(node.id);\n }}\n >\n {isPinned ? (\n <PinFilled width=\"16\" height=\"16\" />\n ) : (\n <Pin width=\"16\" height=\"16\" />\n )}\n </div>\n )}\n {hasStatus && (\n <div\n className={twMerge(\n \"absolute top-1/2 right-2 flex -translate-y-1/2 items-center justify-center\",\n \"text-foreground group-hover/sidebar-item:visible hover:hover-effect\",\n )}\n onClick={(e) => {\n e.stopPropagation();\n togglePin(node.id);\n }}\n >\n <StatusIcon\n status={node.status ?? NodeStatus.UNCHANGED}\n isDescendenceModified={isDescendenceModified}\n />\n </div>\n )}\n </div>\n </div>\n </div>\n </Tooltip>\n </TooltipProvider>\n );\n};\n\nconst RenderTitle = forwardRef<\n HTMLDivElement,\n {\n title: string;\n searchTerm: string;\n isSearchActive: boolean;\n pinnedMode: boolean;\n className?: string;\n }\n>(({ title, searchTerm, isSearchActive, pinnedMode, className }, ref) => {\n return (\n <div ref={ref} className={twMerge(\"truncate text-sm/5\", className)}>\n {searchTerm &&\n title.toLowerCase().includes(searchTerm.toLowerCase()) &&\n !pinnedMode\n ? (() => {\n const highlightClass = isSearchActive\n ? \"bg-warning/10\"\n : \"bg-secondary\";\n const parts: React.ReactNode[] = [];\n let remaining = title;\n const lowerTerm = searchTerm.toLowerCase();\n let i = 0;\n while (remaining.length > 0) {\n const idx = remaining.toLowerCase().indexOf(lowerTerm);\n if (idx === -1) {\n parts.push(remaining);\n break;\n }\n if (idx > 0) parts.push(remaining.slice(0, idx));\n parts.push(\n <span key={i++} className={highlightClass}>\n {remaining.slice(idx, idx + searchTerm.length)}\n </span>,\n );\n remaining = remaining.slice(idx + searchTerm.length);\n }\n return <>{parts}</>;\n })()\n : title}\n </div>\n );\n});\n\nRenderTitle.displayName = \"RenderTitle\";\n\nconst PinnedModeCircleIcon = ({ isPinned }: { isPinned: boolean }) => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"min-w-4\"\n >\n <rect width=\"16\" height=\"16\" rx=\"6.4\" fill=\"transparent\" />\n <path\n d=\"M12 8C12 10.2091 10.2091 12 8 12C5.79086 12 4 10.2091 4 8C4 5.79086 5.79086 4 8 4C10.2091 4 12 5.79086 12 8Z\"\n fill=\"currentColor\"\n className={isPinned ? \"text-muted-foreground\" : \"text-foreground\"}\n />\n </svg>\n);\n"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
5
5
|
const SidebarPinningArea = () => {
|
|
6
6
|
const { pinnedNodePath, togglePin, activeNodeId, onActiveNodeChange } = useSidebar();
|
|
7
7
|
return /* @__PURE__ */ jsx("div", {
|
|
8
|
-
className: "flex flex-col gap-1 border-b border-
|
|
8
|
+
className: "flex flex-col gap-1 border-b border-sidebar-border bg-muted px-2 pt-2 pb-0.5",
|
|
9
9
|
children: pinnedNodePath.map((node, index) => /* @__PURE__ */ jsx(SidebarItem, {
|
|
10
10
|
node: {
|
|
11
11
|
...node,
|