@fuf-stack/megapixels 0.1.0 → 0.2.0
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/Filter/index.cjs +2 -2
- package/dist/Filter/index.d.cts +107 -1
- package/dist/Filter/index.d.ts +107 -1
- package/dist/Filter/index.js +1 -1
- package/dist/{chunk-X574WZ6N.js → chunk-3ZL7ZLSU.js} +123 -34
- package/dist/chunk-3ZL7ZLSU.js.map +1 -0
- package/dist/{chunk-XMMMIB2C.cjs → chunk-DHHIGH3H.cjs} +118 -29
- package/dist/chunk-DHHIGH3H.cjs.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/package.json +5 -5
- package/dist/chunk-X574WZ6N.js.map +0 -1
- package/dist/chunk-XMMMIB2C.cjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Filter/Filter.tsx","../src/Filter/hooks/useFilterValidation.ts","../src/Filter/Subcomponents/ActiveFilters.tsx","../src/Filter/Subcomponents/FiltersContext.tsx","../src/Filter/Subcomponents/AddFilterMenu.tsx","../src/Filter/Subcomponents/FilterModal.tsx","../src/Filter/Subcomponents/SearchInput.tsx","../src/Filter/filters/createFilter.ts","../src/Filter/filters/boolean/Display.tsx","../src/Filter/filters/boolean/Form.tsx","../src/Filter/filters/boolean/schema.ts","../src/Filter/filters/boolean/boolean.ts","../src/Filter/filters/checkboxgroup/Display.tsx","../src/Filter/filters/checkboxgroup/Form.tsx","../src/Filter/filters/checkboxgroup/schema.ts","../src/Filter/filters/checkboxgroup/checkboxgroup.ts","../src/Filter/index.ts"],"sourcesContent":["import type { TVClassName } from '@fuf-stack/pixel-utils';\nimport type { VetoInput } from '@fuf-stack/veto';\nimport type { ReactNode } from 'react';\nimport type { FiltersConfiguration } from './filters/types';\nimport type { SearchConfiguration } from './Subcomponents/SearchInput';\n\nimport { tv, variantsToClassNames } from '@fuf-stack/pixel-utils';\nimport Form from '@fuf-stack/uniform/Form';\n\nimport { useFilterValidation } from './hooks/useFilterValidation';\nimport ActiveFilters from './Subcomponents/ActiveFilters';\nimport AddFilterMenu from './Subcomponents/AddFilterMenu';\nimport FilterModal from './Subcomponents/FilterModal';\nimport { FiltersContextProvider } from './Subcomponents/FiltersContext';\nimport SearchInput from './Subcomponents/SearchInput';\n\n// filter styling variants\nexport const filterVariants = tv({\n slots: {\n // outer wrapper\n base: '',\n // add filter menu trigger button\n addFilterMenuButton: '',\n // add filter menu item\n addFilterMenuItem: '',\n // active filter label\n activeFilterLabel: 'dark:text-foreground h-8 cursor-pointer rounded-md',\n // filter modal body\n filterModalBody: '',\n // filter modal header\n filterModalHeader: 'text-default-700 flex items-center gap-3',\n // filter modal footer\n filterModalFooter: 'justify-between',\n // form element\n form: 'mb-3 flex flex-wrap gap-3',\n // search input field\n searchInput: '',\n // search input wrapper (inner control)\n searchInputWrapper: '',\n // search motion container\n searchMotionDiv: 'flex w-72 gap-2',\n // search show button\n searchShowButton: '',\n // search submit button\n searchSubmitButton: '',\n // search wrapper\n searchWrapper: 'flex items-center',\n },\n});\n\ntype ClassName = TVClassName<typeof filterVariants>;\n\nexport interface FilterValues {\n search?: string;\n filter?: string | Record<string, unknown>;\n}\n\nexport type FilterChildRenderFn = (values: FilterValues) => ReactNode;\n\n/**\n * Filter\n *\n * Controlled, form-driven filter UI.\n *\n * Responsibilities\n * - Derives initial form values from the controlled `values` prop\n * - Builds a composite validation schema from the filter registry (and optional search)\n * - Exposes ergonomic UI: active filters list, add/remove actions, and per-filter modal\n * - Commits changes by invoking the controlled `onChange` callback on submit\n *\n * Structure\n * - Owns an ex-forms `Form` that wraps the entire filter experience\n * - Optionally renders a search input bound to the `search` field\n * - Renders ActiveFilters, AddFilterMenu, and FilterModal inside a shared context\n * - Optionally renders children as a render-prop with the resolved `values`\n */\nexport interface FilterProps {\n /** Optional render-prop that receives the resolved, controlled values */\n children?: FilterChildRenderFn;\n /** CSS class name */\n className?: ClassName;\n /** Configuration of the filter */\n config: {\n /**\n * Declarative filter configuration. Each entry ties a logical name to a\n * registry filter type and (optionally) per-usage config overrides.\n */\n filters: FiltersConfiguration;\n /** Optional configuration for search field */\n search?: SearchConfiguration;\n };\n /** ex-forms form instance name. Defaults to \"filterComponentForm\". */\n formName?: string;\n /** Controlled setter invoked on submit with the next canonical values */\n onChange: (nextValues: FilterValues) => void;\n /** Controlled committed state: the canonical `search` and `filter` values */\n values: FilterValues;\n}\n\n/**\n * Renders the filter UI bound to a single ex-forms `Form`.\n * The form is the source of truth during user interaction; the committed\n * state is controlled by the parent via `values`/`onChange`.\n */\nconst Filter = ({\n children = undefined,\n className = undefined,\n config,\n formName = 'filterComponentForm',\n onChange,\n values,\n}: FilterProps) => {\n // Submit handler: map form state back into the controlled `values` shape\n const handleSubmit = (nextValues: Record<string, unknown>) => {\n onChange(nextValues as FilterValues);\n };\n\n // Build validation schema for all configured filters (and optional search)\n const validation = useFilterValidation(\n config.filters,\n Boolean(config.search),\n );\n\n // validate controlled values are valid\n const { data: valuesValidated } = validation.validate(values as VetoInput);\n\n // classNames from slots\n const variants = filterVariants();\n const classNames = variantsToClassNames(variants, className, 'base');\n\n return (\n <div className={classNames.base}>\n {/*\n Uniform Form wrapper\n - initialValues derive from controlled props (with optional defaults)\n - validation is built from the registry for all configured filters\n - onSubmit maps form values back into values/onChange\n */}\n <Form\n className={classNames.form}\n // disable debug mode for now\n debug={{ disable: true }}\n initialValues={valuesValidated ?? {}}\n name={formName}\n onSubmit={handleSubmit}\n validation={validation}\n >\n {/* Render search if search config is provided */}\n {config.search ? (\n <SearchInput\n config={config.search}\n classNames={{\n searchInput: classNames.searchInput,\n searchInputWrapper: classNames.searchInputWrapper,\n searchMotionDiv: classNames.searchMotionDiv,\n searchShowButton: classNames.searchShowButton,\n searchSubmitButton: classNames.searchSubmitButton,\n searchWrapper: classNames.searchWrapper,\n }}\n />\n ) : null}\n {/*\n FiltersContextProvider exposes a minimal API for the UI layer:\n - activeFilters/unusedFilters by name\n - helpers to get merged config, value, components, and field names\n - methods to add/remove filters and show/close the modal\n */}\n <FiltersContextProvider config={config.filters}>\n <ActiveFilters className={classNames.activeFilterLabel} />\n <AddFilterMenu\n classNames={{\n addFilterMenuButton: classNames.addFilterMenuButton,\n addFilterMenuItem: classNames.addFilterMenuItem,\n }}\n />\n <FilterModal\n classNames={{\n body: classNames.filterModalBody,\n footer: classNames.filterModalFooter,\n header: classNames.filterModalHeader,\n }}\n />\n </FiltersContextProvider>\n </Form>\n {/* Children can consume derived search string and parsed filter object */}\n {children?.(valuesValidated ?? {})}\n </div>\n );\n};\n\nexport default Filter;\n","import type { VetoTypeAny } from '@fuf-stack/veto';\nimport type { FilterInstance } from '../filters/types';\n\nimport { useMemo } from 'react';\n\nimport { object, string, stringToJSON, veto } from '@fuf-stack/veto';\n\n/** Validation function return type alias. */\ntype ValidationSchema = ReturnType<typeof veto>;\n\n/**\n * useFilterValidation\n *\n * Builds a composite validation schema from all provided filter definitions\n * under \"filter\" and optionally includes a \"search\" string field.\n * Memoized by inputs.\n */\nexport const useFilterValidation = (\n filters: FilterInstance<unknown, unknown>[],\n withSearch?: boolean,\n) => {\n return useMemo<ValidationSchema>(() => {\n let validationObject: Record<string, VetoTypeAny> = {};\n let filterValidation: Record<string, VetoTypeAny> = {};\n\n filters.forEach((f) => {\n filterValidation = {\n ...filterValidation,\n [f.name]: f.validation(f.config),\n };\n });\n\n validationObject = {\n filter: stringToJSON()\n .pipe(object(filterValidation))\n .or(object(filterValidation))\n .optional(),\n ...(withSearch\n ? { search: string({ min: 0 }).nullable().optional() }\n : {}),\n };\n\n return veto(validationObject);\n }, [filters, withSearch]);\n};\n\nexport default useFilterValidation;\n","import Label from '@fuf-stack/pixels/Label';\n\nimport { useFilters } from './FiltersContext';\n\n/**\n * ActiveFilters\n *\n * Shows the list of currently applied filters as clickable chips that open\n * the edit modal. Each chip can be removed via its close action.\n */\ninterface ActiveFiltersProps {\n /** CSS class name to apply to each label */\n className?: string;\n}\n\nconst ActiveFilters = ({ className = undefined }: ActiveFiltersProps) => {\n const {\n activeFilters,\n getFilterValueByName,\n getFilterInstanceByName,\n hasError,\n removeFilter,\n showFilterModal,\n } = useFilters();\n return (\n <>\n {activeFilters.map((name) => {\n const instance = getFilterInstanceByName(name);\n const value = getFilterValueByName(name);\n\n // get the display component from the instance\n const DisplayComponent = instance.components.Display;\n\n return (\n <button\n key={name}\n aria-label={`Open ${name} filter`}\n type=\"button\"\n onClick={() => {\n showFilterModal(name);\n }}\n >\n <Label\n className={className}\n color={hasError(name) ? 'danger' : 'primary'}\n variant=\"flat\"\n onClose={() => {\n removeFilter(name);\n }}\n >\n {instance.icon}\n <DisplayComponent config={instance.config} value={value} />\n </Label>\n </button>\n );\n })}\n </>\n );\n};\n\nexport default ActiveFilters;\n","import type { ReactNode } from 'react';\nimport type { FilterInstance, FiltersConfiguration } from '../filters/types';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { useFormContext } from '@fuf-stack/uniform/hooks';\n\ninterface FiltersContextValue {\n /** Active filters (names only) */\n activeFilters: string[];\n /** Seed default value and open modal for a filter usage. */\n addFilter: (name: string) => void;\n /** Close the modal. */\n closeFilterModal: () => void;\n /** Build fully-qualified form field path for a filter name */\n getFilterFormFieldName: (name: string) => string;\n /** Get current form value for a given filter name */\n getFilterValueByName: (name: string) => unknown;\n /** Get filter instance by name */\n getFilterInstanceByName: (name: string) => FilterInstance<unknown, unknown>;\n /** Validation helper for a specific filter field. */\n hasError: (name: string) => boolean;\n /** Name of the current filter that has its modal open */\n modalFilterName: string | undefined;\n /** Remove a filter from the form. */\n removeFilter: (name: string) => void;\n /** Open the modal for a given filter name. */\n showFilterModal: (name: string) => void;\n /** Filters that are not active (names only) */\n unusedFilters: string[];\n}\n\n/**\n * FiltersContext\n *\n * Central state for the filter UI with a clear boundary:\n * - The parent component controls committed filter values (via value/onChange)\n * - The form acts as an edit buffer (used by the modal)\n *\n * Design:\n * - activeFilters/unusedFilters are names-only and derived from the controlled\n * form state\n * - getFilterInstanceByName gives access to the concrete registry entry to\n * retrieve the correct Form/Display components\n * - add seeds defaults in the form and opens the modal\n * - remove un-registers the form field; if the removed filter is currently\n * open in the modal, the modal is closed without rollback\n * - on a new successful form submit (Apply), the modal closes without rollback\n * by subscribing to ex-forms submit state\n */\nconst FiltersContext = createContext<FiltersContextValue | undefined>(\n undefined,\n);\n\nexport const FiltersContextProvider = ({\n children,\n config,\n}: {\n children: ReactNode;\n config: FiltersConfiguration;\n}) => {\n // ex-forms integration:\n // - setValue/unregister/getFieldState: core helpers to manipulate and validate fields\n // - formState: we subscribe to submit-success to auto-close the modal after Apply\n const {\n formState,\n getFieldState,\n setValue,\n triggerSubmit,\n unregister,\n watch,\n } = useFormContext();\n\n /**\n * currentModalFilter\n *\n * Single source of truth for the filter edit modal and its rollback snapshot.\n * - name: which filter's modal is currently open (null when closed)\n * - hadValue/previousValue: snapshot of the controlled value taken when the\n * modal is opened; used to restore state if the user cancels/closes without\n * applying.\n *\n * Lifecycle semantics:\n * - showFilterModal(name): capture snapshot (current controlled value) and open\n * the modal for that filter.\n * - closeFilterModal(): if a snapshot exists, roll back un-applied edits by\n * restoring the previous value (setValue) or removing the field (unregister)\n * when it did not exist before; then clear currentModalFilter.\n * - On successful submit (Apply): close and clear currentModalFilter WITHOUT rollback\n * so edits remain committed.\n * - removeFilter(name): unregisters the field; when removing the filter that is\n * currently open, close the modal WITHOUT rollback (since removal is explicit).\n */\n const [currentModalFilter, setCurrentModalFilter] = useState<{\n name: string;\n hadValue: boolean;\n previousValue: unknown;\n } | null>(null);\n\n // Read current filter values from the form as the live edit buffer\n const filterValue = watch('filter', {});\n\n /**\n * getFilterFormFieldName\n *\n * Returns the fully-qualified field path for a given filter name,\n * e.g., `${filterUrlParam}.status`.\n */\n const getFilterFormFieldName = useCallback((name: string) => {\n return `filter.${name}`;\n }, []);\n\n /**\n * getFilterValueByName\n *\n * Returns the committed value for a filter from the controlled state.\n */\n const getFilterValueByName = useCallback(\n (name: string) => {\n return (filterValue as Record<string, unknown>)[name];\n },\n [filterValue],\n );\n\n /** Open the filter edit modal for the given filter name. */\n const showFilterModal = useCallback(\n (name: string) => {\n const prev = getFilterValueByName(name);\n setCurrentModalFilter({\n name,\n hadValue: typeof prev !== 'undefined',\n previousValue: prev,\n });\n },\n [getFilterValueByName],\n );\n\n /** Close the filter edit modal. Rollback un-applied edits to controlled state. */\n const closeFilterModal = useCallback(() => {\n if (currentModalFilter?.name) {\n const fieldName = getFilterFormFieldName(currentModalFilter.name);\n // if the filter had a value, set it back to the previous value,\n // otherwise unregister the field\n if (currentModalFilter.hadValue) {\n setValue(fieldName, currentModalFilter.previousValue);\n } else {\n unregister(fieldName);\n }\n }\n setCurrentModalFilter(null);\n }, [getFilterFormFieldName, currentModalFilter, setValue, unregister]);\n\n /**\n * Auto-close on submit success\n *\n * Close the modal only on new successful submissions. We track the last\n * submitCount and only react when it changes AND the form reports a\n * successful submit. This prevents closing when `isSubmitSuccessful` remains\n * true without a new submit event.\n */\n const lastSubmitCountRef = useRef<number>(0);\n useEffect(() => {\n if (\n formState.submitCount !== lastSubmitCountRef.current &&\n formState.isSubmitSuccessful\n ) {\n // On successful submit, close without rollback\n setCurrentModalFilter(null);\n }\n lastSubmitCountRef.current = formState.submitCount;\n }, [\n formState.submitCount,\n formState.isSubmitSuccessful,\n setCurrentModalFilter,\n ]);\n\n /**\n * activeFilters\n *\n * Filter names derived from the controlled form state. A filter is considered\n * active when a field exists at `filter.<name>`. Newly added filters become\n * active immediately (seeded default), and will be rolled back on cancel.\n */\n const activeFilters = useMemo(() => {\n return config\n .filter((f) => {\n return Object.hasOwn(filterValue ?? {}, f.name);\n })\n .map((f) => {\n return f.name;\n });\n }, [config, filterValue]);\n\n /**\n * unusedFilters\n *\n * Complement of activeFilters (names without a corresponding `filter.<name>`\n * field in the controlled form state).\n */\n const unusedFilters = useMemo(() => {\n return config\n .filter((f) => {\n return !Object.hasOwn(filterValue ?? {}, f.name);\n })\n .map((f) => {\n return f.name;\n });\n }, [config, filterValue]);\n\n /**\n * getRegistryFilterByName\n *\n * Looks up the concrete registry entry for a filter by name, enabling access\n * to typed Form/Display components and other registry-level metadata.\n */\n const getFilterInstanceByName = useCallback(\n (name: string) => {\n return config.find((f) => {\n return f.name === name;\n }) as FilterInstance<unknown, unknown>;\n },\n [config],\n );\n\n /**\n * addFilter\n *\n * Seeds the filter with its registry default value inside the form and opens\n * the modal for immediate editing. No URL writes happen here.\n */\n const addFilter = useCallback(\n (name: string) => {\n const inst = getFilterInstanceByName(name);\n showFilterModal(name);\n setValue(getFilterFormFieldName(name), inst.defaultValue);\n },\n [\n getFilterFormFieldName,\n getFilterInstanceByName,\n setValue,\n showFilterModal,\n ],\n );\n\n /**\n * removeFilter\n *\n * Unregisters the filter field from the form. This immediately removes the\n * filter from the active list since derived state watches the form. It\n * closes the modal without rollback if the removed filter is currently open.\n */\n const removeFilter = useCallback(\n (name: string) => {\n // unregister form field\n unregister(getFilterFormFieldName(name));\n // close filter modal if open\n if (currentModalFilter?.name === name) {\n // Explicit removal: close without rollback\n setCurrentModalFilter(null);\n }\n // trigger form submit (to update filter state)\n triggerSubmit();\n },\n [\n getFilterFormFieldName,\n currentModalFilter,\n setCurrentModalFilter,\n triggerSubmit,\n unregister,\n ],\n );\n\n /**\n * hasError\n *\n * Helper that checks the ex-forms field state for a specific filter and\n * reports whether the field is currently invalid.\n */\n const hasError = useCallback(\n (name: string) => {\n return getFieldState(getFilterFormFieldName(name)).invalid;\n },\n [getFieldState, getFilterFormFieldName],\n );\n\n const contextValue: FiltersContextValue = useMemo(() => {\n return {\n activeFilters,\n addFilter,\n closeFilterModal,\n getFilterFormFieldName,\n getFilterValueByName,\n getFilterInstanceByName,\n hasError,\n modalFilterName: currentModalFilter?.name,\n removeFilter,\n showFilterModal,\n unusedFilters,\n };\n }, [\n activeFilters,\n addFilter,\n closeFilterModal,\n getFilterFormFieldName,\n getFilterValueByName,\n getFilterInstanceByName,\n hasError,\n currentModalFilter,\n removeFilter,\n showFilterModal,\n unusedFilters,\n ]);\n\n return (\n <FiltersContext.Provider value={contextValue}>\n {children}\n </FiltersContext.Provider>\n );\n};\n\n/**\n * useFilters\n *\n * Convenience hook to consume the FiltersContext. Throws a descriptive error\n * when used outside of a FiltersContextProvider to make integration mistakes\n * obvious during development.\n */\nexport const useFilters = (): FiltersContextValue => {\n const ctx = useContext(FiltersContext);\n if (!ctx) {\n throw new Error('useFilters must be used within FiltersContextProvider');\n }\n return ctx;\n};\n\nexport default FiltersContext;\n","import { FaSliders } from 'react-icons/fa6';\n\nimport Menu from '@fuf-stack/pixels/Menu';\n\nimport { useFilters } from './FiltersContext';\n\ninterface AddFilterMenuProps {\n /** CSS class name map for slots */\n classNames?: Partial<{\n addFilterMenuItem: string;\n addFilterMenuButton: string;\n }>;\n}\n\n/**\n * AddFilterMenu\n *\n * Renders a menu trigger that opens a list of addable filters. Selecting an\n * item triggers the parent to seed a default value and open the modal.\n */\nconst AddFilterMenu = ({ classNames = {} }: AddFilterMenuProps) => {\n const { unusedFilters, addFilter, getFilterInstanceByName } = useFilters();\n\n const menuItems = unusedFilters.map((name) => {\n const instance = getFilterInstanceByName(name);\n const config = instance.config as { text?: string };\n const label = config?.text ?? name;\n return {\n key: name,\n icon: instance.icon,\n label,\n onClick: () => {\n addFilter(name);\n },\n };\n });\n\n return (\n <Menu\n isDisabled={!menuItems.length}\n items={menuItems}\n placement=\"bottom-start\"\n className={{\n item: classNames.addFilterMenuItem,\n trigger: classNames.addFilterMenuButton,\n }}\n triggerButtonProps={{\n 'aria-label': 'Add Filter',\n disableRipple: true,\n size: 'sm',\n variant: 'bordered',\n }}\n >\n <FaSliders />\n Filter\n </Menu>\n );\n};\n\nexport default AddFilterMenu;\n","import { Suspense } from 'react';\nimport { PiSlidersHorizontalBold } from 'react-icons/pi';\n\nimport Button from '@fuf-stack/pixels/Button';\nimport Modal from '@fuf-stack/pixels/Modal';\nimport SubmitButton from '@fuf-stack/uniform/SubmitButton';\n\nimport { useFilters } from './FiltersContext';\n\ninterface FilterModalProps {\n classNames?: Partial<{\n header: string;\n footer: string;\n body: string;\n }>;\n}\n\nconst FilterModal = ({ classNames = {} }: FilterModalProps) => {\n const {\n closeFilterModal,\n getFilterFormFieldName,\n getFilterInstanceByName,\n modalFilterName,\n removeFilter,\n } = useFilters();\n\n // don't render if no filter is open\n if (!modalFilterName) {\n return null;\n }\n\n const instance = getFilterInstanceByName(modalFilterName);\n const config = instance.config as { text?: string };\n\n // get the form component from the instance\n const FormComponent = instance.components.Form;\n\n return (\n <Modal\n isOpen\n onClose={closeFilterModal}\n className={{\n body: classNames.body,\n footer: classNames.footer,\n header: classNames.header,\n }}\n footer={\n <>\n <Button\n color=\"danger\"\n variant=\"flat\"\n onClick={() => {\n removeFilter(modalFilterName);\n }}\n >\n Remove\n </Button>\n <SubmitButton>Apply Filter</SubmitButton>\n </>\n }\n header={\n <>\n {instance.icon ?? <PiSlidersHorizontalBold />}\n <div>{`${config?.text ?? modalFilterName} Filter`}</div>\n </>\n }\n >\n <Suspense>\n <FormComponent\n config={config}\n fieldName={getFilterFormFieldName(modalFilterName)}\n />\n </Suspense>\n </Modal>\n );\n};\n\nexport default FilterModal;\n","import { useState } from 'react';\nimport { FaSearch } from 'react-icons/fa';\n\nimport { motion } from '@fuf-stack/pixel-motion';\nimport Button from '@fuf-stack/pixels/Button';\nimport { useFormContext } from '@fuf-stack/uniform/hooks';\nimport Input from '@fuf-stack/uniform/Input';\nimport SubmitButton from '@fuf-stack/uniform/SubmitButton';\n\nexport type SearchConfiguration =\n | boolean\n | {\n /** Placeholder shown in the search input */\n placeholder?: string;\n };\n\ninterface SearchInputProps {\n /** Slots class names passed from parent variants */\n classNames?: Partial<{\n searchWrapper: string;\n searchShowButton: string;\n searchMotionDiv: string;\n searchInput: string;\n searchInputWrapper: string;\n searchSubmitButton: string;\n }>;\n /** Search configuration */\n config: SearchConfiguration;\n}\n\n/**\n * SearchInput\n *\n * By default renders only a search button. When clicked, the text input animates in\n * and a trailing submit button is shown.\n */\nconst SearchInput = ({ classNames = {}, config }: SearchInputProps) => {\n const { formState, setFocus, triggerSubmit } = useFormContext();\n\n // Auto-open if there is an initial or externally set search value\n const isInitiallyVisible = !!formState?.defaultValues?.search;\n const [isVisible, setIsVisible] = useState(isInitiallyVisible);\n\n const placeholder =\n typeof config === 'object' ? config.placeholder : undefined;\n\n return (\n <div className={classNames.searchWrapper}>\n {!isVisible && (\n <Button\n ariaLabel=\"Show search input\"\n className={classNames.searchShowButton}\n icon={<FaSearch />}\n size=\"sm\"\n variant=\"bordered\"\n onClick={() => {\n setIsVisible(true);\n }}\n />\n )}\n {isVisible ? (\n <motion.div\n key=\"search-input\"\n animate={{ opacity: 1 }}\n className={classNames.searchMotionDiv}\n initial={{ opacity: 0.5 }}\n onAnimationComplete={() => {\n // if the input was not initially visible, focus it\n if (!isInitiallyVisible) {\n setFocus('search');\n }\n }}\n transition={{\n // if the input was not initially visible, animate in\n duration: isInitiallyVisible ? 0 : 0.3,\n ease: 'circOut',\n }}\n >\n <Input\n clearable\n debounceDelay={0}\n name=\"search\"\n placeholder={placeholder}\n size=\"sm\"\n className={{\n input: classNames.searchInput,\n inputWrapper: classNames.searchInputWrapper,\n }}\n // submit on clear\n onClear={() => {\n triggerSubmit();\n }}\n />\n <SubmitButton\n ariaLabel=\"Trigger search\"\n // eslint-disable-next-line react/no-children-prop\n children={null}\n className={classNames.searchSubmitButton}\n color=\"primary\"\n icon={<FaSearch />}\n size=\"sm\"\n />\n </motion.div>\n ) : null}\n </div>\n );\n};\n\nexport default SearchInput;\n","import type { FilterDefinition, FilterFactory } from './types';\n\n/**\n * createFilter\n *\n * Builds a filter factory from a static FilterDefinition. The returned factory\n * accepts a usage descriptor (name/icon and optional partial config) and\n * produces a concrete FilterInstance with:\n * - merged config (shallow: definition.defaults.config overlaid by overrides)\n * - Form/Display components\n * - validate function (forwarded from the definition)\n * - defaultValue (forwarded from the definition)\n * - name and icon for UI integration\n *\n * @typeParam Config - Configuration object shape for the filter\n * @typeParam Value - Runtime value type for the filter\n * @param definition - Static description of the filter (components, defaults, validate)\n * @returns FilterFactory that creates FilterInstance<Config, Value>\n */\nconst createFilter = <Config, Value>(\n definition: FilterDefinition<Config, Value>,\n): FilterFactory<Config, Value> => {\n return ({ name, icon, config }) => {\n return {\n components: definition.components,\n config: { ...definition.defaults.config, ...(config ?? {}) } as Config,\n defaultValue: definition.defaults.value,\n icon,\n name,\n validation: definition.validation,\n };\n };\n};\n\nexport default createFilter;\n","import type { FilterDisplayProps } from '../types';\nimport type { Config, Value } from './schema';\n\nconst Display = ({\n value,\n config: { text, textPrefix, textNoWord },\n}: FilterDisplayProps<Config, Value>) => {\n if (typeof value === 'boolean') {\n return (\n <>\n {value ? textPrefix : `${textPrefix} ${textNoWord ?? 'no'}`} {text}\n </>\n );\n }\n return <>{`${text}...`}</>;\n};\n\nexport default Display;\n","import type { FilterFormProps } from '../types';\nimport type { Config } from './schema';\n\nimport Switch from '@fuf-stack/uniform/Switch';\n\nconst Form = ({\n fieldName,\n config: { text, textPrefix },\n}: FilterFormProps<Config>) => {\n return <Switch label={`${textPrefix} ${text}`} name={fieldName} />;\n};\n\nexport default Form;\n","import type { vInfer } from '@fuf-stack/veto';\n\nimport { boolean, object, string } from '@fuf-stack/veto';\n\n/** configuration of the filter */\nexport const config = object({\n /**\n * Human‑readable label used in the UI (e.g. in the chip and modal header).\n * Examples: \"Magical\", \"Haunted\"\n */\n text: string(),\n /**\n * Optional word shown before the label when building sentence‑like chips.\n * Examples: \"is\" → \"is Magical\"\n */\n textPrefix: string().optional(),\n /**\n * Optional negation word used when a boolean value is false.\n * Examples: \"not\" → \"is not Magical\"\n */\n textNoWord: string().optional(),\n});\n\n/** validate the filter value */\nexport const validate = (_config?: Config) => {\n return boolean().optional();\n};\n\nexport type Config = vInfer<typeof config>;\nexport type Value = vInfer<ReturnType<typeof validate>>;\n","/* eslint-disable import-x/prefer-default-export */\n\nimport type { Config, Value } from './schema';\n\nimport createFilter from '../createFilter';\nimport Display from './Display';\nimport Form from './Form';\nimport { validate } from './schema';\n\nexport const boolean = createFilter<Config, Value>({\n components: { Display, Form },\n defaults: {\n value: true,\n config: { text: 'Active', textPrefix: 'is', textNoWord: 'no' },\n },\n validation: validate,\n});\n","import type { FilterDisplayProps } from '../types';\nimport type { Config, Value } from './schema';\n\nconst Display = ({\n value,\n config: { text, options },\n}: FilterDisplayProps<Config, Value>) => {\n if (value && value.length > 0) {\n const labels = value\n .map((val) => {\n return (\n options.find((op) => {\n return op.value === val;\n })?.label ?? val\n );\n })\n .join(' ');\n return `${text} is ${labels}`;\n }\n return `${text} is ...`;\n};\n\nexport default Display;\n","import type { FilterFormProps } from '../types';\nimport type { Config } from './schema';\n\nimport CheckboxGroup from '@fuf-stack/uniform/CheckboxGroup';\n\nconst Form = ({ fieldName, config }: FilterFormProps<Config>) => {\n return <CheckboxGroup name={fieldName} options={config.options} />;\n};\n\nexport default Form;\n","import type { vInfer } from '@fuf-stack/veto';\n\nimport { array, object, refineArray, string } from '@fuf-stack/veto';\n\n/** configuration of the filter */\nexport const config = object({\n /**\n * Human‑readable label used in the UI (e.g. label and modal header).\n * Example: \"Snacks\", \"Mood\"\n */\n text: string(),\n /**\n * Options rendered as multiple checkboxes. Each option needs a `label`\n * (what the user sees) and a `value` (what is written into the form state).\n */\n options: array(object({ label: string(), value: string() })),\n});\n\n/** validate the filter value */\nexport const validate = (cfg?: Config) => {\n return refineArray(array(string()).optional())({\n unique: true,\n custom: (values, ctx) => {\n if (!cfg) {\n return;\n }\n values.forEach((value) => {\n if (\n !cfg.options.find((option) => {\n return option?.value === value;\n })\n ) {\n ctx.addIssue({\n code: 'custom',\n message: `Invalid value: ${value}`,\n });\n }\n });\n },\n });\n};\n\nexport type Config = vInfer<typeof config>;\nexport type Value = vInfer<ReturnType<typeof validate>>;\n","/* eslint-disable import-x/prefer-default-export */\n\nimport type { Config, Value } from './schema';\n\nimport createFilter from '../createFilter';\nimport Display from './Display';\nimport Form from './Form';\nimport { validate } from './schema';\n\nexport const checkboxgroup = createFilter<Config, Value>({\n components: { Display, Form },\n defaults: { value: [], config: { text: 'Options', options: [] } },\n validation: validate,\n});\n","import Filter from './Filter';\nimport { boolean } from './filters/boolean/boolean';\nimport { checkboxgroup } from './filters/checkboxgroup/checkboxgroup';\n\n// export types\nexport type * from './filters/types';\n\n// export helpers\nexport { default as createFilter } from './filters/createFilter';\n\n// export all filters\nexport const filters = {\n boolean,\n checkboxgroup,\n};\n\nexport default Filter;\n"],"mappings":"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","names":["filters","useMemo","config","jsx","jsx","jsxs","config","Fragment","jsx","jsxs","config","useState","Button","useFormContext","SubmitButton","jsx","jsxs","config","jsx","jsxs","config","config","Fragment","jsx","jsxs","jsx","Form","object","string","boolean","Display","Display_default","jsx","Form","config","Form_default","object","string","config","validate","Display_default","Form_default","validate","boolean","Filter_default"]}
|
|
@@ -117,7 +117,7 @@ var FiltersContextProvider = ({
|
|
|
117
117
|
]);
|
|
118
118
|
const activeFilters = _react.useMemo.call(void 0, () => {
|
|
119
119
|
return config3.filter((f) => {
|
|
120
|
-
return Object.hasOwn(filterValue
|
|
120
|
+
return Object.hasOwn(filterValue != null ? filterValue : {}, f.name);
|
|
121
121
|
}).map((f) => {
|
|
122
122
|
return f.name;
|
|
123
123
|
});
|
|
@@ -211,7 +211,7 @@ var useFilters = () => {
|
|
|
211
211
|
|
|
212
212
|
// src/Filter/Subcomponents/ActiveFilters.tsx
|
|
213
213
|
|
|
214
|
-
var ActiveFilters = () => {
|
|
214
|
+
var ActiveFilters = ({ className = void 0 }) => {
|
|
215
215
|
const {
|
|
216
216
|
activeFilters,
|
|
217
217
|
getFilterValueByName,
|
|
@@ -230,12 +230,12 @@ var ActiveFilters = () => {
|
|
|
230
230
|
"aria-label": `Open ${name} filter`,
|
|
231
231
|
type: "button",
|
|
232
232
|
onClick: () => {
|
|
233
|
-
|
|
233
|
+
showFilterModal(name);
|
|
234
234
|
},
|
|
235
235
|
children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
236
236
|
_Label2.default,
|
|
237
237
|
{
|
|
238
|
-
className
|
|
238
|
+
className,
|
|
239
239
|
color: hasError(name) ? "danger" : "primary",
|
|
240
240
|
variant: "flat",
|
|
241
241
|
onClose: () => {
|
|
@@ -243,7 +243,7 @@ var ActiveFilters = () => {
|
|
|
243
243
|
},
|
|
244
244
|
children: [
|
|
245
245
|
instance.icon,
|
|
246
|
-
|
|
246
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, DisplayComponent, { config: instance.config, value })
|
|
247
247
|
]
|
|
248
248
|
}
|
|
249
249
|
)
|
|
@@ -256,10 +256,9 @@ var ActiveFilters_default = ActiveFilters;
|
|
|
256
256
|
|
|
257
257
|
// src/Filter/Subcomponents/AddFilterMenu.tsx
|
|
258
258
|
var _fa6 = require('react-icons/fa6');
|
|
259
|
-
|
|
260
259
|
var _Menu = require('@fuf-stack/pixels/Menu'); var _Menu2 = _interopRequireDefault(_Menu);
|
|
261
260
|
|
|
262
|
-
var AddFilterMenu = ({
|
|
261
|
+
var AddFilterMenu = ({ classNames = {} }) => {
|
|
263
262
|
const { unusedFilters, addFilter, getFilterInstanceByName } = useFilters();
|
|
264
263
|
const menuItems = unusedFilters.map((name) => {
|
|
265
264
|
var _a;
|
|
@@ -278,12 +277,16 @@ var AddFilterMenu = ({ className = void 0 }) => {
|
|
|
278
277
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
279
278
|
_Menu2.default,
|
|
280
279
|
{
|
|
281
|
-
className: _pixelutils.cn.call(void 0, className),
|
|
282
280
|
isDisabled: !menuItems.length,
|
|
283
281
|
items: menuItems,
|
|
284
282
|
placement: "bottom-start",
|
|
283
|
+
className: {
|
|
284
|
+
item: classNames.addFilterMenuItem,
|
|
285
|
+
trigger: classNames.addFilterMenuButton
|
|
286
|
+
},
|
|
285
287
|
triggerButtonProps: {
|
|
286
288
|
"aria-label": "Add Filter",
|
|
289
|
+
disableRipple: true,
|
|
287
290
|
size: "sm",
|
|
288
291
|
variant: "bordered"
|
|
289
292
|
},
|
|
@@ -303,7 +306,7 @@ var _Button = require('@fuf-stack/pixels/Button'); var _Button2 = _interopRequir
|
|
|
303
306
|
var _Modal = require('@fuf-stack/pixels/Modal'); var _Modal2 = _interopRequireDefault(_Modal);
|
|
304
307
|
var _SubmitButton = require('@fuf-stack/uniform/SubmitButton'); var _SubmitButton2 = _interopRequireDefault(_SubmitButton);
|
|
305
308
|
|
|
306
|
-
var FilterModal = () => {
|
|
309
|
+
var FilterModal = ({ classNames = {} }) => {
|
|
307
310
|
var _a, _b;
|
|
308
311
|
const {
|
|
309
312
|
closeFilterModal,
|
|
@@ -324,8 +327,9 @@ var FilterModal = () => {
|
|
|
324
327
|
isOpen: true,
|
|
325
328
|
onClose: closeFilterModal,
|
|
326
329
|
className: {
|
|
327
|
-
|
|
328
|
-
|
|
330
|
+
body: classNames.body,
|
|
331
|
+
footer: classNames.footer,
|
|
332
|
+
header: classNames.header
|
|
329
333
|
},
|
|
330
334
|
footer: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
331
335
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
@@ -363,21 +367,21 @@ var _fa = require('react-icons/fa');
|
|
|
363
367
|
var _pixelmotion = require('@fuf-stack/pixel-motion');
|
|
364
368
|
|
|
365
369
|
|
|
366
|
-
|
|
367
370
|
var _Input = require('@fuf-stack/uniform/Input'); var _Input2 = _interopRequireDefault(_Input);
|
|
368
371
|
|
|
369
372
|
|
|
370
|
-
var SearchInput = ({
|
|
373
|
+
var SearchInput = ({ classNames = {}, config: config3 }) => {
|
|
371
374
|
var _a;
|
|
372
375
|
const { formState, setFocus, triggerSubmit } = _hooks.useFormContext.call(void 0, );
|
|
373
376
|
const isInitiallyVisible = !!((_a = formState == null ? void 0 : formState.defaultValues) == null ? void 0 : _a.search);
|
|
374
377
|
const [isVisible, setIsVisible] = _react.useState.call(void 0, isInitiallyVisible);
|
|
375
378
|
const placeholder = typeof config3 === "object" ? config3.placeholder : void 0;
|
|
376
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className:
|
|
379
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: classNames.searchWrapper, children: [
|
|
377
380
|
!isVisible && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
378
381
|
_Button2.default,
|
|
379
382
|
{
|
|
380
|
-
|
|
383
|
+
ariaLabel: "Show search input",
|
|
384
|
+
className: classNames.searchShowButton,
|
|
381
385
|
icon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _fa.FaSearch, {}),
|
|
382
386
|
size: "sm",
|
|
383
387
|
variant: "bordered",
|
|
@@ -390,7 +394,7 @@ var SearchInput = ({ className = void 0, config: config3 }) => {
|
|
|
390
394
|
_pixelmotion.motion.div,
|
|
391
395
|
{
|
|
392
396
|
animate: { opacity: 1 },
|
|
393
|
-
className:
|
|
397
|
+
className: classNames.searchMotionDiv,
|
|
394
398
|
initial: { opacity: 0.5 },
|
|
395
399
|
onAnimationComplete: () => {
|
|
396
400
|
if (!isInitiallyVisible) {
|
|
@@ -411,6 +415,10 @@ var SearchInput = ({ className = void 0, config: config3 }) => {
|
|
|
411
415
|
name: "search",
|
|
412
416
|
placeholder,
|
|
413
417
|
size: "sm",
|
|
418
|
+
className: {
|
|
419
|
+
input: classNames.searchInput,
|
|
420
|
+
inputWrapper: classNames.searchInputWrapper
|
|
421
|
+
},
|
|
414
422
|
onClear: () => {
|
|
415
423
|
triggerSubmit();
|
|
416
424
|
}
|
|
@@ -419,7 +427,9 @@ var SearchInput = ({ className = void 0, config: config3 }) => {
|
|
|
419
427
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
420
428
|
_SubmitButton2.default,
|
|
421
429
|
{
|
|
430
|
+
ariaLabel: "Trigger search",
|
|
422
431
|
children: null,
|
|
432
|
+
className: classNames.searchSubmitButton,
|
|
423
433
|
color: "primary",
|
|
424
434
|
icon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _fa.FaSearch, {}),
|
|
425
435
|
size: "sm"
|
|
@@ -435,6 +445,38 @@ var SearchInput_default = SearchInput;
|
|
|
435
445
|
|
|
436
446
|
// src/Filter/Filter.tsx
|
|
437
447
|
|
|
448
|
+
var filterVariants = _pixelutils.tv.call(void 0, {
|
|
449
|
+
slots: {
|
|
450
|
+
// outer wrapper
|
|
451
|
+
base: "",
|
|
452
|
+
// add filter menu trigger button
|
|
453
|
+
addFilterMenuButton: "",
|
|
454
|
+
// add filter menu item
|
|
455
|
+
addFilterMenuItem: "",
|
|
456
|
+
// active filter label
|
|
457
|
+
activeFilterLabel: "dark:text-foreground h-8 cursor-pointer rounded-md",
|
|
458
|
+
// filter modal body
|
|
459
|
+
filterModalBody: "",
|
|
460
|
+
// filter modal header
|
|
461
|
+
filterModalHeader: "text-default-700 flex items-center gap-3",
|
|
462
|
+
// filter modal footer
|
|
463
|
+
filterModalFooter: "justify-between",
|
|
464
|
+
// form element
|
|
465
|
+
form: "mb-3 flex flex-wrap gap-3",
|
|
466
|
+
// search input field
|
|
467
|
+
searchInput: "",
|
|
468
|
+
// search input wrapper (inner control)
|
|
469
|
+
searchInputWrapper: "",
|
|
470
|
+
// search motion container
|
|
471
|
+
searchMotionDiv: "flex w-72 gap-2",
|
|
472
|
+
// search show button
|
|
473
|
+
searchShowButton: "",
|
|
474
|
+
// search submit button
|
|
475
|
+
searchSubmitButton: "",
|
|
476
|
+
// search wrapper
|
|
477
|
+
searchWrapper: "flex items-center"
|
|
478
|
+
}
|
|
479
|
+
});
|
|
438
480
|
var Filter = ({
|
|
439
481
|
children = void 0,
|
|
440
482
|
className = void 0,
|
|
@@ -451,22 +493,54 @@ var Filter = ({
|
|
|
451
493
|
Boolean(config3.search)
|
|
452
494
|
);
|
|
453
495
|
const { data: valuesValidated } = validation.validate(values);
|
|
454
|
-
|
|
496
|
+
const variants = filterVariants();
|
|
497
|
+
const classNames = _pixelutils.variantsToClassNames.call(void 0, variants, className, "base");
|
|
498
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: classNames.base, children: [
|
|
455
499
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
456
500
|
_Form2.default,
|
|
457
501
|
{
|
|
458
|
-
className:
|
|
502
|
+
className: classNames.form,
|
|
459
503
|
debug: { disable: true },
|
|
460
504
|
initialValues: valuesValidated != null ? valuesValidated : {},
|
|
461
505
|
name: formName,
|
|
462
506
|
onSubmit: handleSubmit,
|
|
463
507
|
validation,
|
|
464
508
|
children: [
|
|
465
|
-
config3.search ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
509
|
+
config3.search ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
510
|
+
SearchInput_default,
|
|
511
|
+
{
|
|
512
|
+
config: config3.search,
|
|
513
|
+
classNames: {
|
|
514
|
+
searchInput: classNames.searchInput,
|
|
515
|
+
searchInputWrapper: classNames.searchInputWrapper,
|
|
516
|
+
searchMotionDiv: classNames.searchMotionDiv,
|
|
517
|
+
searchShowButton: classNames.searchShowButton,
|
|
518
|
+
searchSubmitButton: classNames.searchSubmitButton,
|
|
519
|
+
searchWrapper: classNames.searchWrapper
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
) : null,
|
|
466
523
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, FiltersContextProvider, { config: config3.filters, children: [
|
|
467
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, ActiveFilters_default, {}),
|
|
468
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
469
|
-
|
|
524
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, ActiveFilters_default, { className: classNames.activeFilterLabel }),
|
|
525
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
526
|
+
AddFilterMenu_default,
|
|
527
|
+
{
|
|
528
|
+
classNames: {
|
|
529
|
+
addFilterMenuButton: classNames.addFilterMenuButton,
|
|
530
|
+
addFilterMenuItem: classNames.addFilterMenuItem
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
),
|
|
534
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
535
|
+
FilterModal_default,
|
|
536
|
+
{
|
|
537
|
+
classNames: {
|
|
538
|
+
body: classNames.filterModalBody,
|
|
539
|
+
footer: classNames.filterModalFooter,
|
|
540
|
+
header: classNames.filterModalHeader
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
)
|
|
470
544
|
] })
|
|
471
545
|
]
|
|
472
546
|
}
|
|
@@ -522,11 +596,20 @@ var Form_default = Form2;
|
|
|
522
596
|
// src/Filter/filters/boolean/schema.ts
|
|
523
597
|
|
|
524
598
|
var config = _veto.object.call(void 0, {
|
|
525
|
-
/**
|
|
599
|
+
/**
|
|
600
|
+
* Human‑readable label used in the UI (e.g. in the chip and modal header).
|
|
601
|
+
* Examples: "Magical", "Haunted"
|
|
602
|
+
*/
|
|
526
603
|
text: _veto.string.call(void 0, ),
|
|
527
|
-
/**
|
|
604
|
+
/**
|
|
605
|
+
* Optional word shown before the label when building sentence‑like chips.
|
|
606
|
+
* Examples: "is" → "is Magical"
|
|
607
|
+
*/
|
|
528
608
|
textPrefix: _veto.string.call(void 0, ).optional(),
|
|
529
|
-
/**
|
|
609
|
+
/**
|
|
610
|
+
* Optional negation word used when a boolean value is false.
|
|
611
|
+
* Examples: "not" → "is not Magical"
|
|
612
|
+
*/
|
|
530
613
|
textNoWord: _veto.string.call(void 0, ).optional()
|
|
531
614
|
});
|
|
532
615
|
var validate = (_config) => {
|
|
@@ -554,7 +637,7 @@ var Display2 = ({
|
|
|
554
637
|
return (_b = (_a = options.find((op) => {
|
|
555
638
|
return op.value === val;
|
|
556
639
|
})) == null ? void 0 : _a.label) != null ? _b : val;
|
|
557
|
-
}).join("
|
|
640
|
+
}).join(" ");
|
|
558
641
|
return `${text} is ${labels}`;
|
|
559
642
|
}
|
|
560
643
|
return `${text} is ...`;
|
|
@@ -572,9 +655,15 @@ var Form_default2 = Form3;
|
|
|
572
655
|
// src/Filter/filters/checkboxgroup/schema.ts
|
|
573
656
|
|
|
574
657
|
var config2 = _veto.object.call(void 0, {
|
|
575
|
-
/**
|
|
658
|
+
/**
|
|
659
|
+
* Human‑readable label used in the UI (e.g. label and modal header).
|
|
660
|
+
* Example: "Snacks", "Mood"
|
|
661
|
+
*/
|
|
576
662
|
text: _veto.string.call(void 0, ),
|
|
577
|
-
/**
|
|
663
|
+
/**
|
|
664
|
+
* Options rendered as multiple checkboxes. Each option needs a `label`
|
|
665
|
+
* (what the user sees) and a `value` (what is written into the form state).
|
|
666
|
+
*/
|
|
578
667
|
options: _veto.array.call(void 0, _veto.object.call(void 0, { label: _veto.string.call(void 0, ), value: _veto.string.call(void 0, ) }))
|
|
579
668
|
});
|
|
580
669
|
var validate2 = (cfg) => {
|
|
@@ -617,4 +706,4 @@ var Filter_default2 = Filter_default;
|
|
|
617
706
|
|
|
618
707
|
|
|
619
708
|
exports.createFilter_default = createFilter_default; exports.filters = filters; exports.Filter_default = Filter_default2;
|
|
620
|
-
//# sourceMappingURL=chunk-
|
|
709
|
+
//# sourceMappingURL=chunk-DHHIGH3H.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/pixels/pixels/packages/megapixels/dist/chunk-DHHIGH3H.cjs","../src/Filter/Filter.tsx","../src/Filter/hooks/useFilterValidation.ts","../src/Filter/Subcomponents/ActiveFilters.tsx","../src/Filter/Subcomponents/FiltersContext.tsx","../src/Filter/Subcomponents/AddFilterMenu.tsx","../src/Filter/Subcomponents/FilterModal.tsx","../src/Filter/Subcomponents/SearchInput.tsx","../src/Filter/filters/createFilter.ts","../src/Filter/filters/boolean/Display.tsx","../src/Filter/filters/boolean/Form.tsx","../src/Filter/filters/boolean/schema.ts","../src/Filter/filters/boolean/boolean.ts","../src/Filter/filters/checkboxgroup/Display.tsx","../src/Filter/filters/checkboxgroup/Form.tsx","../src/Filter/filters/checkboxgroup/schema.ts","../src/Filter/filters/checkboxgroup/checkboxgroup.ts","../src/Filter/index.ts"],"names":["filters","config","useMemo","jsx","jsxs","Fragment","useState","Button","SubmitButton","Form","Display","object","Display_default","validate","boolean"],"mappings":"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","file":"/home/runner/work/pixels/pixels/packages/megapixels/dist/chunk-DHHIGH3H.cjs","sourcesContent":[null,"import type { TVClassName } from '@fuf-stack/pixel-utils';\nimport type { VetoInput } from '@fuf-stack/veto';\nimport type { ReactNode } from 'react';\nimport type { FiltersConfiguration } from './filters/types';\nimport type { SearchConfiguration } from './Subcomponents/SearchInput';\n\nimport { tv, variantsToClassNames } from '@fuf-stack/pixel-utils';\nimport Form from '@fuf-stack/uniform/Form';\n\nimport { useFilterValidation } from './hooks/useFilterValidation';\nimport ActiveFilters from './Subcomponents/ActiveFilters';\nimport AddFilterMenu from './Subcomponents/AddFilterMenu';\nimport FilterModal from './Subcomponents/FilterModal';\nimport { FiltersContextProvider } from './Subcomponents/FiltersContext';\nimport SearchInput from './Subcomponents/SearchInput';\n\n// filter styling variants\nexport const filterVariants = tv({\n slots: {\n // outer wrapper\n base: '',\n // add filter menu trigger button\n addFilterMenuButton: '',\n // add filter menu item\n addFilterMenuItem: '',\n // active filter label\n activeFilterLabel: 'dark:text-foreground h-8 cursor-pointer rounded-md',\n // filter modal body\n filterModalBody: '',\n // filter modal header\n filterModalHeader: 'text-default-700 flex items-center gap-3',\n // filter modal footer\n filterModalFooter: 'justify-between',\n // form element\n form: 'mb-3 flex flex-wrap gap-3',\n // search input field\n searchInput: '',\n // search input wrapper (inner control)\n searchInputWrapper: '',\n // search motion container\n searchMotionDiv: 'flex w-72 gap-2',\n // search show button\n searchShowButton: '',\n // search submit button\n searchSubmitButton: '',\n // search wrapper\n searchWrapper: 'flex items-center',\n },\n});\n\ntype ClassName = TVClassName<typeof filterVariants>;\n\nexport interface FilterValues {\n search?: string;\n filter?: string | Record<string, unknown>;\n}\n\nexport type FilterChildRenderFn = (values: FilterValues) => ReactNode;\n\n/**\n * Filter\n *\n * Controlled, form-driven filter UI.\n *\n * Responsibilities\n * - Derives initial form values from the controlled `values` prop\n * - Builds a composite validation schema from the filter registry (and optional search)\n * - Exposes ergonomic UI: active filters list, add/remove actions, and per-filter modal\n * - Commits changes by invoking the controlled `onChange` callback on submit\n *\n * Structure\n * - Owns an ex-forms `Form` that wraps the entire filter experience\n * - Optionally renders a search input bound to the `search` field\n * - Renders ActiveFilters, AddFilterMenu, and FilterModal inside a shared context\n * - Optionally renders children as a render-prop with the resolved `values`\n */\nexport interface FilterProps {\n /** Optional render-prop that receives the resolved, controlled values */\n children?: FilterChildRenderFn;\n /** CSS class name */\n className?: ClassName;\n /** Configuration of the filter */\n config: {\n /**\n * Declarative filter configuration. Each entry ties a logical name to a\n * registry filter type and (optionally) per-usage config overrides.\n */\n filters: FiltersConfiguration;\n /** Optional configuration for search field */\n search?: SearchConfiguration;\n };\n /** ex-forms form instance name. Defaults to \"filterComponentForm\". */\n formName?: string;\n /** Controlled setter invoked on submit with the next canonical values */\n onChange: (nextValues: FilterValues) => void;\n /** Controlled committed state: the canonical `search` and `filter` values */\n values: FilterValues;\n}\n\n/**\n * Renders the filter UI bound to a single ex-forms `Form`.\n * The form is the source of truth during user interaction; the committed\n * state is controlled by the parent via `values`/`onChange`.\n */\nconst Filter = ({\n children = undefined,\n className = undefined,\n config,\n formName = 'filterComponentForm',\n onChange,\n values,\n}: FilterProps) => {\n // Submit handler: map form state back into the controlled `values` shape\n const handleSubmit = (nextValues: Record<string, unknown>) => {\n onChange(nextValues as FilterValues);\n };\n\n // Build validation schema for all configured filters (and optional search)\n const validation = useFilterValidation(\n config.filters,\n Boolean(config.search),\n );\n\n // validate controlled values are valid\n const { data: valuesValidated } = validation.validate(values as VetoInput);\n\n // classNames from slots\n const variants = filterVariants();\n const classNames = variantsToClassNames(variants, className, 'base');\n\n return (\n <div className={classNames.base}>\n {/*\n Uniform Form wrapper\n - initialValues derive from controlled props (with optional defaults)\n - validation is built from the registry for all configured filters\n - onSubmit maps form values back into values/onChange\n */}\n <Form\n className={classNames.form}\n // disable debug mode for now\n debug={{ disable: true }}\n initialValues={valuesValidated ?? {}}\n name={formName}\n onSubmit={handleSubmit}\n validation={validation}\n >\n {/* Render search if search config is provided */}\n {config.search ? (\n <SearchInput\n config={config.search}\n classNames={{\n searchInput: classNames.searchInput,\n searchInputWrapper: classNames.searchInputWrapper,\n searchMotionDiv: classNames.searchMotionDiv,\n searchShowButton: classNames.searchShowButton,\n searchSubmitButton: classNames.searchSubmitButton,\n searchWrapper: classNames.searchWrapper,\n }}\n />\n ) : null}\n {/*\n FiltersContextProvider exposes a minimal API for the UI layer:\n - activeFilters/unusedFilters by name\n - helpers to get merged config, value, components, and field names\n - methods to add/remove filters and show/close the modal\n */}\n <FiltersContextProvider config={config.filters}>\n <ActiveFilters className={classNames.activeFilterLabel} />\n <AddFilterMenu\n classNames={{\n addFilterMenuButton: classNames.addFilterMenuButton,\n addFilterMenuItem: classNames.addFilterMenuItem,\n }}\n />\n <FilterModal\n classNames={{\n body: classNames.filterModalBody,\n footer: classNames.filterModalFooter,\n header: classNames.filterModalHeader,\n }}\n />\n </FiltersContextProvider>\n </Form>\n {/* Children can consume derived search string and parsed filter object */}\n {children?.(valuesValidated ?? {})}\n </div>\n );\n};\n\nexport default Filter;\n","import type { VetoTypeAny } from '@fuf-stack/veto';\nimport type { FilterInstance } from '../filters/types';\n\nimport { useMemo } from 'react';\n\nimport { object, string, stringToJSON, veto } from '@fuf-stack/veto';\n\n/** Validation function return type alias. */\ntype ValidationSchema = ReturnType<typeof veto>;\n\n/**\n * useFilterValidation\n *\n * Builds a composite validation schema from all provided filter definitions\n * under \"filter\" and optionally includes a \"search\" string field.\n * Memoized by inputs.\n */\nexport const useFilterValidation = (\n filters: FilterInstance<unknown, unknown>[],\n withSearch?: boolean,\n) => {\n return useMemo<ValidationSchema>(() => {\n let validationObject: Record<string, VetoTypeAny> = {};\n let filterValidation: Record<string, VetoTypeAny> = {};\n\n filters.forEach((f) => {\n filterValidation = {\n ...filterValidation,\n [f.name]: f.validation(f.config),\n };\n });\n\n validationObject = {\n filter: stringToJSON()\n .pipe(object(filterValidation))\n .or(object(filterValidation))\n .optional(),\n ...(withSearch\n ? { search: string({ min: 0 }).nullable().optional() }\n : {}),\n };\n\n return veto(validationObject);\n }, [filters, withSearch]);\n};\n\nexport default useFilterValidation;\n","import Label from '@fuf-stack/pixels/Label';\n\nimport { useFilters } from './FiltersContext';\n\n/**\n * ActiveFilters\n *\n * Shows the list of currently applied filters as clickable chips that open\n * the edit modal. Each chip can be removed via its close action.\n */\ninterface ActiveFiltersProps {\n /** CSS class name to apply to each label */\n className?: string;\n}\n\nconst ActiveFilters = ({ className = undefined }: ActiveFiltersProps) => {\n const {\n activeFilters,\n getFilterValueByName,\n getFilterInstanceByName,\n hasError,\n removeFilter,\n showFilterModal,\n } = useFilters();\n return (\n <>\n {activeFilters.map((name) => {\n const instance = getFilterInstanceByName(name);\n const value = getFilterValueByName(name);\n\n // get the display component from the instance\n const DisplayComponent = instance.components.Display;\n\n return (\n <button\n key={name}\n aria-label={`Open ${name} filter`}\n type=\"button\"\n onClick={() => {\n showFilterModal(name);\n }}\n >\n <Label\n className={className}\n color={hasError(name) ? 'danger' : 'primary'}\n variant=\"flat\"\n onClose={() => {\n removeFilter(name);\n }}\n >\n {instance.icon}\n <DisplayComponent config={instance.config} value={value} />\n </Label>\n </button>\n );\n })}\n </>\n );\n};\n\nexport default ActiveFilters;\n","import type { ReactNode } from 'react';\nimport type { FilterInstance, FiltersConfiguration } from '../filters/types';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { useFormContext } from '@fuf-stack/uniform/hooks';\n\ninterface FiltersContextValue {\n /** Active filters (names only) */\n activeFilters: string[];\n /** Seed default value and open modal for a filter usage. */\n addFilter: (name: string) => void;\n /** Close the modal. */\n closeFilterModal: () => void;\n /** Build fully-qualified form field path for a filter name */\n getFilterFormFieldName: (name: string) => string;\n /** Get current form value for a given filter name */\n getFilterValueByName: (name: string) => unknown;\n /** Get filter instance by name */\n getFilterInstanceByName: (name: string) => FilterInstance<unknown, unknown>;\n /** Validation helper for a specific filter field. */\n hasError: (name: string) => boolean;\n /** Name of the current filter that has its modal open */\n modalFilterName: string | undefined;\n /** Remove a filter from the form. */\n removeFilter: (name: string) => void;\n /** Open the modal for a given filter name. */\n showFilterModal: (name: string) => void;\n /** Filters that are not active (names only) */\n unusedFilters: string[];\n}\n\n/**\n * FiltersContext\n *\n * Central state for the filter UI with a clear boundary:\n * - The parent component controls committed filter values (via value/onChange)\n * - The form acts as an edit buffer (used by the modal)\n *\n * Design:\n * - activeFilters/unusedFilters are names-only and derived from the controlled\n * form state\n * - getFilterInstanceByName gives access to the concrete registry entry to\n * retrieve the correct Form/Display components\n * - add seeds defaults in the form and opens the modal\n * - remove un-registers the form field; if the removed filter is currently\n * open in the modal, the modal is closed without rollback\n * - on a new successful form submit (Apply), the modal closes without rollback\n * by subscribing to ex-forms submit state\n */\nconst FiltersContext = createContext<FiltersContextValue | undefined>(\n undefined,\n);\n\nexport const FiltersContextProvider = ({\n children,\n config,\n}: {\n children: ReactNode;\n config: FiltersConfiguration;\n}) => {\n // ex-forms integration:\n // - setValue/unregister/getFieldState: core helpers to manipulate and validate fields\n // - formState: we subscribe to submit-success to auto-close the modal after Apply\n const {\n formState,\n getFieldState,\n setValue,\n triggerSubmit,\n unregister,\n watch,\n } = useFormContext();\n\n /**\n * currentModalFilter\n *\n * Single source of truth for the filter edit modal and its rollback snapshot.\n * - name: which filter's modal is currently open (null when closed)\n * - hadValue/previousValue: snapshot of the controlled value taken when the\n * modal is opened; used to restore state if the user cancels/closes without\n * applying.\n *\n * Lifecycle semantics:\n * - showFilterModal(name): capture snapshot (current controlled value) and open\n * the modal for that filter.\n * - closeFilterModal(): if a snapshot exists, roll back un-applied edits by\n * restoring the previous value (setValue) or removing the field (unregister)\n * when it did not exist before; then clear currentModalFilter.\n * - On successful submit (Apply): close and clear currentModalFilter WITHOUT rollback\n * so edits remain committed.\n * - removeFilter(name): unregisters the field; when removing the filter that is\n * currently open, close the modal WITHOUT rollback (since removal is explicit).\n */\n const [currentModalFilter, setCurrentModalFilter] = useState<{\n name: string;\n hadValue: boolean;\n previousValue: unknown;\n } | null>(null);\n\n // Read current filter values from the form as the live edit buffer\n const filterValue = watch('filter', {});\n\n /**\n * getFilterFormFieldName\n *\n * Returns the fully-qualified field path for a given filter name,\n * e.g., `${filterUrlParam}.status`.\n */\n const getFilterFormFieldName = useCallback((name: string) => {\n return `filter.${name}`;\n }, []);\n\n /**\n * getFilterValueByName\n *\n * Returns the committed value for a filter from the controlled state.\n */\n const getFilterValueByName = useCallback(\n (name: string) => {\n return (filterValue as Record<string, unknown>)[name];\n },\n [filterValue],\n );\n\n /** Open the filter edit modal for the given filter name. */\n const showFilterModal = useCallback(\n (name: string) => {\n const prev = getFilterValueByName(name);\n setCurrentModalFilter({\n name,\n hadValue: typeof prev !== 'undefined',\n previousValue: prev,\n });\n },\n [getFilterValueByName],\n );\n\n /** Close the filter edit modal. Rollback un-applied edits to controlled state. */\n const closeFilterModal = useCallback(() => {\n if (currentModalFilter?.name) {\n const fieldName = getFilterFormFieldName(currentModalFilter.name);\n // if the filter had a value, set it back to the previous value,\n // otherwise unregister the field\n if (currentModalFilter.hadValue) {\n setValue(fieldName, currentModalFilter.previousValue);\n } else {\n unregister(fieldName);\n }\n }\n setCurrentModalFilter(null);\n }, [getFilterFormFieldName, currentModalFilter, setValue, unregister]);\n\n /**\n * Auto-close on submit success\n *\n * Close the modal only on new successful submissions. We track the last\n * submitCount and only react when it changes AND the form reports a\n * successful submit. This prevents closing when `isSubmitSuccessful` remains\n * true without a new submit event.\n */\n const lastSubmitCountRef = useRef<number>(0);\n useEffect(() => {\n if (\n formState.submitCount !== lastSubmitCountRef.current &&\n formState.isSubmitSuccessful\n ) {\n // On successful submit, close without rollback\n setCurrentModalFilter(null);\n }\n lastSubmitCountRef.current = formState.submitCount;\n }, [\n formState.submitCount,\n formState.isSubmitSuccessful,\n setCurrentModalFilter,\n ]);\n\n /**\n * activeFilters\n *\n * Filter names derived from the controlled form state. A filter is considered\n * active when a field exists at `filter.<name>`. Newly added filters become\n * active immediately (seeded default), and will be rolled back on cancel.\n */\n const activeFilters = useMemo(() => {\n return config\n .filter((f) => {\n return Object.hasOwn(filterValue ?? {}, f.name);\n })\n .map((f) => {\n return f.name;\n });\n }, [config, filterValue]);\n\n /**\n * unusedFilters\n *\n * Complement of activeFilters (names without a corresponding `filter.<name>`\n * field in the controlled form state).\n */\n const unusedFilters = useMemo(() => {\n return config\n .filter((f) => {\n return !Object.hasOwn(filterValue ?? {}, f.name);\n })\n .map((f) => {\n return f.name;\n });\n }, [config, filterValue]);\n\n /**\n * getRegistryFilterByName\n *\n * Looks up the concrete registry entry for a filter by name, enabling access\n * to typed Form/Display components and other registry-level metadata.\n */\n const getFilterInstanceByName = useCallback(\n (name: string) => {\n return config.find((f) => {\n return f.name === name;\n }) as FilterInstance<unknown, unknown>;\n },\n [config],\n );\n\n /**\n * addFilter\n *\n * Seeds the filter with its registry default value inside the form and opens\n * the modal for immediate editing. No URL writes happen here.\n */\n const addFilter = useCallback(\n (name: string) => {\n const inst = getFilterInstanceByName(name);\n showFilterModal(name);\n setValue(getFilterFormFieldName(name), inst.defaultValue);\n },\n [\n getFilterFormFieldName,\n getFilterInstanceByName,\n setValue,\n showFilterModal,\n ],\n );\n\n /**\n * removeFilter\n *\n * Unregisters the filter field from the form. This immediately removes the\n * filter from the active list since derived state watches the form. It\n * closes the modal without rollback if the removed filter is currently open.\n */\n const removeFilter = useCallback(\n (name: string) => {\n // unregister form field\n unregister(getFilterFormFieldName(name));\n // close filter modal if open\n if (currentModalFilter?.name === name) {\n // Explicit removal: close without rollback\n setCurrentModalFilter(null);\n }\n // trigger form submit (to update filter state)\n triggerSubmit();\n },\n [\n getFilterFormFieldName,\n currentModalFilter,\n setCurrentModalFilter,\n triggerSubmit,\n unregister,\n ],\n );\n\n /**\n * hasError\n *\n * Helper that checks the ex-forms field state for a specific filter and\n * reports whether the field is currently invalid.\n */\n const hasError = useCallback(\n (name: string) => {\n return getFieldState(getFilterFormFieldName(name)).invalid;\n },\n [getFieldState, getFilterFormFieldName],\n );\n\n const contextValue: FiltersContextValue = useMemo(() => {\n return {\n activeFilters,\n addFilter,\n closeFilterModal,\n getFilterFormFieldName,\n getFilterValueByName,\n getFilterInstanceByName,\n hasError,\n modalFilterName: currentModalFilter?.name,\n removeFilter,\n showFilterModal,\n unusedFilters,\n };\n }, [\n activeFilters,\n addFilter,\n closeFilterModal,\n getFilterFormFieldName,\n getFilterValueByName,\n getFilterInstanceByName,\n hasError,\n currentModalFilter,\n removeFilter,\n showFilterModal,\n unusedFilters,\n ]);\n\n return (\n <FiltersContext.Provider value={contextValue}>\n {children}\n </FiltersContext.Provider>\n );\n};\n\n/**\n * useFilters\n *\n * Convenience hook to consume the FiltersContext. Throws a descriptive error\n * when used outside of a FiltersContextProvider to make integration mistakes\n * obvious during development.\n */\nexport const useFilters = (): FiltersContextValue => {\n const ctx = useContext(FiltersContext);\n if (!ctx) {\n throw new Error('useFilters must be used within FiltersContextProvider');\n }\n return ctx;\n};\n\nexport default FiltersContext;\n","import { FaSliders } from 'react-icons/fa6';\n\nimport Menu from '@fuf-stack/pixels/Menu';\n\nimport { useFilters } from './FiltersContext';\n\ninterface AddFilterMenuProps {\n /** CSS class name map for slots */\n classNames?: Partial<{\n addFilterMenuItem: string;\n addFilterMenuButton: string;\n }>;\n}\n\n/**\n * AddFilterMenu\n *\n * Renders a menu trigger that opens a list of addable filters. Selecting an\n * item triggers the parent to seed a default value and open the modal.\n */\nconst AddFilterMenu = ({ classNames = {} }: AddFilterMenuProps) => {\n const { unusedFilters, addFilter, getFilterInstanceByName } = useFilters();\n\n const menuItems = unusedFilters.map((name) => {\n const instance = getFilterInstanceByName(name);\n const config = instance.config as { text?: string };\n const label = config?.text ?? name;\n return {\n key: name,\n icon: instance.icon,\n label,\n onClick: () => {\n addFilter(name);\n },\n };\n });\n\n return (\n <Menu\n isDisabled={!menuItems.length}\n items={menuItems}\n placement=\"bottom-start\"\n className={{\n item: classNames.addFilterMenuItem,\n trigger: classNames.addFilterMenuButton,\n }}\n triggerButtonProps={{\n 'aria-label': 'Add Filter',\n disableRipple: true,\n size: 'sm',\n variant: 'bordered',\n }}\n >\n <FaSliders />\n Filter\n </Menu>\n );\n};\n\nexport default AddFilterMenu;\n","import { Suspense } from 'react';\nimport { PiSlidersHorizontalBold } from 'react-icons/pi';\n\nimport Button from '@fuf-stack/pixels/Button';\nimport Modal from '@fuf-stack/pixels/Modal';\nimport SubmitButton from '@fuf-stack/uniform/SubmitButton';\n\nimport { useFilters } from './FiltersContext';\n\ninterface FilterModalProps {\n classNames?: Partial<{\n header: string;\n footer: string;\n body: string;\n }>;\n}\n\nconst FilterModal = ({ classNames = {} }: FilterModalProps) => {\n const {\n closeFilterModal,\n getFilterFormFieldName,\n getFilterInstanceByName,\n modalFilterName,\n removeFilter,\n } = useFilters();\n\n // don't render if no filter is open\n if (!modalFilterName) {\n return null;\n }\n\n const instance = getFilterInstanceByName(modalFilterName);\n const config = instance.config as { text?: string };\n\n // get the form component from the instance\n const FormComponent = instance.components.Form;\n\n return (\n <Modal\n isOpen\n onClose={closeFilterModal}\n className={{\n body: classNames.body,\n footer: classNames.footer,\n header: classNames.header,\n }}\n footer={\n <>\n <Button\n color=\"danger\"\n variant=\"flat\"\n onClick={() => {\n removeFilter(modalFilterName);\n }}\n >\n Remove\n </Button>\n <SubmitButton>Apply Filter</SubmitButton>\n </>\n }\n header={\n <>\n {instance.icon ?? <PiSlidersHorizontalBold />}\n <div>{`${config?.text ?? modalFilterName} Filter`}</div>\n </>\n }\n >\n <Suspense>\n <FormComponent\n config={config}\n fieldName={getFilterFormFieldName(modalFilterName)}\n />\n </Suspense>\n </Modal>\n );\n};\n\nexport default FilterModal;\n","import { useState } from 'react';\nimport { FaSearch } from 'react-icons/fa';\n\nimport { motion } from '@fuf-stack/pixel-motion';\nimport Button from '@fuf-stack/pixels/Button';\nimport { useFormContext } from '@fuf-stack/uniform/hooks';\nimport Input from '@fuf-stack/uniform/Input';\nimport SubmitButton from '@fuf-stack/uniform/SubmitButton';\n\nexport type SearchConfiguration =\n | boolean\n | {\n /** Placeholder shown in the search input */\n placeholder?: string;\n };\n\ninterface SearchInputProps {\n /** Slots class names passed from parent variants */\n classNames?: Partial<{\n searchWrapper: string;\n searchShowButton: string;\n searchMotionDiv: string;\n searchInput: string;\n searchInputWrapper: string;\n searchSubmitButton: string;\n }>;\n /** Search configuration */\n config: SearchConfiguration;\n}\n\n/**\n * SearchInput\n *\n * By default renders only a search button. When clicked, the text input animates in\n * and a trailing submit button is shown.\n */\nconst SearchInput = ({ classNames = {}, config }: SearchInputProps) => {\n const { formState, setFocus, triggerSubmit } = useFormContext();\n\n // Auto-open if there is an initial or externally set search value\n const isInitiallyVisible = !!formState?.defaultValues?.search;\n const [isVisible, setIsVisible] = useState(isInitiallyVisible);\n\n const placeholder =\n typeof config === 'object' ? config.placeholder : undefined;\n\n return (\n <div className={classNames.searchWrapper}>\n {!isVisible && (\n <Button\n ariaLabel=\"Show search input\"\n className={classNames.searchShowButton}\n icon={<FaSearch />}\n size=\"sm\"\n variant=\"bordered\"\n onClick={() => {\n setIsVisible(true);\n }}\n />\n )}\n {isVisible ? (\n <motion.div\n key=\"search-input\"\n animate={{ opacity: 1 }}\n className={classNames.searchMotionDiv}\n initial={{ opacity: 0.5 }}\n onAnimationComplete={() => {\n // if the input was not initially visible, focus it\n if (!isInitiallyVisible) {\n setFocus('search');\n }\n }}\n transition={{\n // if the input was not initially visible, animate in\n duration: isInitiallyVisible ? 0 : 0.3,\n ease: 'circOut',\n }}\n >\n <Input\n clearable\n debounceDelay={0}\n name=\"search\"\n placeholder={placeholder}\n size=\"sm\"\n className={{\n input: classNames.searchInput,\n inputWrapper: classNames.searchInputWrapper,\n }}\n // submit on clear\n onClear={() => {\n triggerSubmit();\n }}\n />\n <SubmitButton\n ariaLabel=\"Trigger search\"\n // eslint-disable-next-line react/no-children-prop\n children={null}\n className={classNames.searchSubmitButton}\n color=\"primary\"\n icon={<FaSearch />}\n size=\"sm\"\n />\n </motion.div>\n ) : null}\n </div>\n );\n};\n\nexport default SearchInput;\n","import type { FilterDefinition, FilterFactory } from './types';\n\n/**\n * createFilter\n *\n * Builds a filter factory from a static FilterDefinition. The returned factory\n * accepts a usage descriptor (name/icon and optional partial config) and\n * produces a concrete FilterInstance with:\n * - merged config (shallow: definition.defaults.config overlaid by overrides)\n * - Form/Display components\n * - validate function (forwarded from the definition)\n * - defaultValue (forwarded from the definition)\n * - name and icon for UI integration\n *\n * @typeParam Config - Configuration object shape for the filter\n * @typeParam Value - Runtime value type for the filter\n * @param definition - Static description of the filter (components, defaults, validate)\n * @returns FilterFactory that creates FilterInstance<Config, Value>\n */\nconst createFilter = <Config, Value>(\n definition: FilterDefinition<Config, Value>,\n): FilterFactory<Config, Value> => {\n return ({ name, icon, config }) => {\n return {\n components: definition.components,\n config: { ...definition.defaults.config, ...(config ?? {}) } as Config,\n defaultValue: definition.defaults.value,\n icon,\n name,\n validation: definition.validation,\n };\n };\n};\n\nexport default createFilter;\n","import type { FilterDisplayProps } from '../types';\nimport type { Config, Value } from './schema';\n\nconst Display = ({\n value,\n config: { text, textPrefix, textNoWord },\n}: FilterDisplayProps<Config, Value>) => {\n if (typeof value === 'boolean') {\n return (\n <>\n {value ? textPrefix : `${textPrefix} ${textNoWord ?? 'no'}`} {text}\n </>\n );\n }\n return <>{`${text}...`}</>;\n};\n\nexport default Display;\n","import type { FilterFormProps } from '../types';\nimport type { Config } from './schema';\n\nimport Switch from '@fuf-stack/uniform/Switch';\n\nconst Form = ({\n fieldName,\n config: { text, textPrefix },\n}: FilterFormProps<Config>) => {\n return <Switch label={`${textPrefix} ${text}`} name={fieldName} />;\n};\n\nexport default Form;\n","import type { vInfer } from '@fuf-stack/veto';\n\nimport { boolean, object, string } from '@fuf-stack/veto';\n\n/** configuration of the filter */\nexport const config = object({\n /**\n * Human‑readable label used in the UI (e.g. in the chip and modal header).\n * Examples: \"Magical\", \"Haunted\"\n */\n text: string(),\n /**\n * Optional word shown before the label when building sentence‑like chips.\n * Examples: \"is\" → \"is Magical\"\n */\n textPrefix: string().optional(),\n /**\n * Optional negation word used when a boolean value is false.\n * Examples: \"not\" → \"is not Magical\"\n */\n textNoWord: string().optional(),\n});\n\n/** validate the filter value */\nexport const validate = (_config?: Config) => {\n return boolean().optional();\n};\n\nexport type Config = vInfer<typeof config>;\nexport type Value = vInfer<ReturnType<typeof validate>>;\n","/* eslint-disable import-x/prefer-default-export */\n\nimport type { Config, Value } from './schema';\n\nimport createFilter from '../createFilter';\nimport Display from './Display';\nimport Form from './Form';\nimport { validate } from './schema';\n\nexport const boolean = createFilter<Config, Value>({\n components: { Display, Form },\n defaults: {\n value: true,\n config: { text: 'Active', textPrefix: 'is', textNoWord: 'no' },\n },\n validation: validate,\n});\n","import type { FilterDisplayProps } from '../types';\nimport type { Config, Value } from './schema';\n\nconst Display = ({\n value,\n config: { text, options },\n}: FilterDisplayProps<Config, Value>) => {\n if (value && value.length > 0) {\n const labels = value\n .map((val) => {\n return (\n options.find((op) => {\n return op.value === val;\n })?.label ?? val\n );\n })\n .join(' ');\n return `${text} is ${labels}`;\n }\n return `${text} is ...`;\n};\n\nexport default Display;\n","import type { FilterFormProps } from '../types';\nimport type { Config } from './schema';\n\nimport CheckboxGroup from '@fuf-stack/uniform/CheckboxGroup';\n\nconst Form = ({ fieldName, config }: FilterFormProps<Config>) => {\n return <CheckboxGroup name={fieldName} options={config.options} />;\n};\n\nexport default Form;\n","import type { vInfer } from '@fuf-stack/veto';\n\nimport { array, object, refineArray, string } from '@fuf-stack/veto';\n\n/** configuration of the filter */\nexport const config = object({\n /**\n * Human‑readable label used in the UI (e.g. label and modal header).\n * Example: \"Snacks\", \"Mood\"\n */\n text: string(),\n /**\n * Options rendered as multiple checkboxes. Each option needs a `label`\n * (what the user sees) and a `value` (what is written into the form state).\n */\n options: array(object({ label: string(), value: string() })),\n});\n\n/** validate the filter value */\nexport const validate = (cfg?: Config) => {\n return refineArray(array(string()).optional())({\n unique: true,\n custom: (values, ctx) => {\n if (!cfg) {\n return;\n }\n values.forEach((value) => {\n if (\n !cfg.options.find((option) => {\n return option?.value === value;\n })\n ) {\n ctx.addIssue({\n code: 'custom',\n message: `Invalid value: ${value}`,\n });\n }\n });\n },\n });\n};\n\nexport type Config = vInfer<typeof config>;\nexport type Value = vInfer<ReturnType<typeof validate>>;\n","/* eslint-disable import-x/prefer-default-export */\n\nimport type { Config, Value } from './schema';\n\nimport createFilter from '../createFilter';\nimport Display from './Display';\nimport Form from './Form';\nimport { validate } from './schema';\n\nexport const checkboxgroup = createFilter<Config, Value>({\n components: { Display, Form },\n defaults: { value: [], config: { text: 'Options', options: [] } },\n validation: validate,\n});\n","import Filter from './Filter';\nimport { boolean } from './filters/boolean/boolean';\nimport { checkboxgroup } from './filters/checkboxgroup/checkboxgroup';\n\n// export types\nexport type * from './filters/types';\n\n// export helpers\nexport { default as createFilter } from './filters/createFilter';\n\n// export all filters\nexport const filters = {\n boolean,\n checkboxgroup,\n};\n\nexport default Filter;\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunkDHHIGH3Hcjs = require('./chunk-DHHIGH3H.cjs');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
exports.createFilter =
|
|
8
|
+
exports.createFilter = _chunkDHHIGH3Hcjs.createFilter_default; exports.filters = _chunkDHHIGH3Hcjs.filters;
|
|
9
9
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { FilterDefinition, FilterDisplayProps, FilterFactory, FilterFormProps, FilterInstance, FiltersConfiguration, createFilter, filters } from './Filter/index.cjs';
|
|
2
2
|
import 'react/jsx-runtime';
|
|
3
|
+
import 'tailwind-variants';
|
|
4
|
+
import '@fuf-stack/pixel-utils';
|
|
3
5
|
import 'react';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { FilterDefinition, FilterDisplayProps, FilterFactory, FilterFormProps, FilterInstance, FiltersConfiguration, createFilter, filters } from './Filter/index.js';
|
|
2
2
|
import 'react/jsx-runtime';
|
|
3
|
+
import 'tailwind-variants';
|
|
4
|
+
import '@fuf-stack/pixel-utils';
|
|
3
5
|
import 'react';
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuf-stack/megapixels",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "fuf react advanced components library",
|
|
5
5
|
"author": "Fröhlich ∧ Frei",
|
|
6
6
|
"homepage": "https://github.com/fuf-stack/megapixels#readme",
|
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"react-icons": "5.5.0",
|
|
45
|
-
"@fuf-stack/pixel-motion": "1.0.
|
|
45
|
+
"@fuf-stack/pixel-motion": "1.0.24",
|
|
46
|
+
"@fuf-stack/pixels": "1.2.5",
|
|
46
47
|
"@fuf-stack/pixel-utils": "1.0.5",
|
|
47
|
-
"@fuf-stack/
|
|
48
|
-
"@fuf-stack/uniform": "1.2.
|
|
49
|
-
"@fuf-stack/veto": "0.12.6"
|
|
48
|
+
"@fuf-stack/veto": "0.12.6",
|
|
49
|
+
"@fuf-stack/uniform": "1.2.3"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/debug": "4.1.12",
|