{"version":3,"file":"filter-chip.cjs","sources":["../../../components/filter-chip/filter-chip.tsx"],"sourcesContent":["'use client';\n\nimport { cva, VariantProps } from 'class-variance-authority';\nimport dayjs from 'dayjs';\nimport { ComponentProps, ReactElement, useCallback, useState } from 'react';\nimport { XIcon } from '~/icons';\nimport {\n  FilterOperation,\n  FilterOperator,\n  FilterSelectOption,\n  FilterType,\n  FilterTypes,\n  filterOperators\n} from '~/types/filters';\nimport { DatePicker, type DatePickerProps } from '../calendar';\nimport { Flex } from '../flex';\nimport { Input } from '../input';\nimport { Select } from '../select';\nimport { BaseSelectProps } from '../select/select-root';\nimport { Text } from '../text';\nimport styles from './filter-chip.module.css';\nimport { Operation } from './filter-chip-operation';\n\nconst chip = cva(styles.chip, {\n  variants: {\n    variant: {\n      default: styles['chip-default'],\n      text: null\n    }\n  },\n  defaultVariants: {\n    variant: 'default'\n  }\n});\n\nexport type FilterChipValue = string | string[] | number | Date;\n\n/**\n * Coerce a `FilterChipValue` to the `Date` the DatePicker expects — filter\n * state hydrated from a serialized query arrives as a string or epoch number.\n * Unparseable values leave the field unselected.\n */\nconst toDateValue = (value: unknown): Date | undefined => {\n  if (value instanceof Date) return value;\n  if (typeof value === 'string' || typeof value === 'number') {\n    const parsed = dayjs(value);\n    return parsed.isValid() ? parsed.toDate() : undefined;\n  }\n  return undefined;\n};\n\n/**\n * Subset of `DatePickerProps` that consumers may forward to the chip's\n * built-in DatePicker via `calendarProps`. `value`/`onSelect`/`defaultValue`\n * are owned by `FilterChip`; `children` would replace the input trigger and\n * break the chip layout.\n */\nexport type FilterChipCalendarProps = Omit<\n  DatePickerProps,\n  'value' | 'onSelect' | 'defaultValue' | 'children'\n>;\n\nexport interface FilterChipProps\n  extends ComponentProps<'div'>,\n    VariantProps<typeof chip> {\n  label: string;\n  value?: FilterChipValue;\n  onRemove?: () => void;\n  columnType?: FilterTypes;\n  options?: FilterSelectOption[];\n  onValueChange?: (value: FilterChipValue, operation: string) => void;\n  onOperationChange?: (operation: string) => void;\n  leadingIcon?: ReactElement;\n  operations?: FilterOperator<string>[];\n  selectProps?: BaseSelectProps;\n  /**\n   * Props forwarded to the underlying `DatePicker` for `columnType=\"date\"`.\n   * `value`/`onSelect`/`defaultValue` are owned by `FilterChip` and excluded;\n   * `children` is excluded so the chip's input trigger isn't replaced.\n   */\n  calendarProps?: FilterChipCalendarProps;\n}\n\n/**\n * A compact, removable filter pill that pairs a label and operator with a\n * value control chosen by `columnType`: a `Select` (`select`/`multiselect`),\n * a `DatePicker` (`date`), or a text `Input` (`string`/`number`). The value\n * control sizes to its content so the chip hugs the active filter. Emits\n * `onValueChange`/`onOperationChange` and renders a remove button when\n * `onRemove` is provided.\n */\nexport const FilterChip = ({\n  label,\n  value,\n  onRemove,\n  className,\n  ref,\n  columnType = FilterType.string,\n  options = [],\n  onValueChange,\n  onOperationChange,\n  leadingIcon,\n  variant,\n  operations,\n  selectProps,\n  calendarProps,\n  ...props\n}: FilterChipProps) => {\n  const computedOperations = operations?.length\n    ? operations\n    : filterOperators[columnType];\n\n  const [operation, setOperation] = useState<FilterOperation | undefined>(\n    computedOperations?.[0]\n  );\n  // `??` not `||` — a falsy option value like `0` is a real selection.\n  const [filterValue, setFilterValue] = useState<any>(value ?? '');\n\n  const showOnRemove = typeof onRemove === 'function';\n  const isMultiSelectColumn = columnType === FilterType.multiselect;\n\n  const handleOperationChange = useCallback(\n    (operation: FilterOperation) => {\n      setOperation(operation);\n      if (operation?.value) onOperationChange?.(operation.value);\n    },\n    [onOperationChange]\n  );\n\n  const handleFilterValueChange = useCallback(\n    (value: any) => {\n      setFilterValue(value);\n      onValueChange?.(value, operation?.value ?? '');\n    },\n    [operation, onValueChange]\n  );\n\n  const renderValueInput = () => {\n    switch (columnType) {\n      case FilterType.multiselect:\n      case FilterType.select:\n        return (\n          <Select\n            value={isMultiSelectColumn ? filterValue : filterValue.toString()}\n            onValueChange={handleFilterValueChange}\n            multiple={isMultiSelectColumn}\n            {...selectProps}\n          >\n            <Select.Trigger\n              iconProps={{\n                style: {\n                  display: 'none'\n                }\n              }}\n              variant='text'\n              className={styles.selectValue}\n              data-slot='filter-chip-value'\n            >\n              <Select.Value placeholder='Select value'>\n                {isMultiSelectColumn && filterValue.length > 1\n                  ? `${filterValue.length} selected`\n                  : undefined}\n              </Select.Value>\n            </Select.Trigger>\n            <Select.Content data-variant='filter'>\n              {options.map(opt => (\n                <Select.Item\n                  key={opt.value.toString()}\n                  value={opt.value.toString()}\n                >\n                  {opt.label}\n                </Select.Item>\n              ))}\n            </Select.Content>\n          </Select>\n        );\n      case FilterType.date:\n        return (\n          <div\n            className={styles.dateFieldWrapper}\n            data-slot='filter-chip-value'\n          >\n            <DatePicker\n              showCalendarIcon={false}\n              {...calendarProps}\n              value={toDateValue(filterValue)}\n              onSelect={date => handleFilterValueChange(date)}\n              slotProps={{\n                ...calendarProps?.slotProps,\n                input: {\n                  classNames: { container: styles.dateField },\n                  ...calendarProps?.slotProps?.input\n                }\n              }}\n            />\n          </div>\n        );\n      default:\n        return (\n          <div\n            className={styles.inputFieldWrapper}\n            data-slot='filter-chip-value'\n          >\n            <Input\n              variant={variant === 'text' ? 'borderless' : 'default'}\n              classNames={{ container: styles.inputField }}\n              value={filterValue}\n              onChange={e => handleFilterValueChange(e.target.value)}\n            />\n          </div>\n        );\n    }\n  };\n\n  return (\n    <Flex\n      align='center'\n      ref={ref}\n      className={chip({ variant, className })}\n      role='group'\n      aria-label={`Filter by ${label}`}\n      data-variant={variant}\n      data-slot='filter-chip'\n      {...props}\n    >\n      <Flex\n        align='center'\n        gap={2}\n        className={styles['chip-label']}\n        data-slot='filter-chip-label'\n      >\n        {leadingIcon && (\n          <span\n            className={styles.leadingIcon}\n            aria-hidden='true'\n            data-slot='filter-chip-leading-icon'\n          >\n            {leadingIcon}\n          </span>\n        )}\n        <Text size='small' weight='regular' data-slot='filter-chip-label-text'>\n          {label}\n        </Text>\n      </Flex>\n      <Operation\n        operations={computedOperations}\n        label={label}\n        value={operation}\n        onChange={handleOperationChange}\n        showAlternateLabel={isMultiSelectColumn && filterValue.length <= 1}\n      />\n      {renderValueInput()}\n      {showOnRemove && (\n        <button\n          className={styles.removeIconContainer}\n          aria-label={`Remove ${label} filter`}\n          onClick={onRemove}\n          data-slot='filter-chip-remove'\n        >\n          <XIcon\n            className={styles.removeIcon}\n            data-slot='filter-chip-remove-icon'\n          />\n        </button>\n      )}\n    </Flex>\n  );\n};\n\nFilterChip.displayName = 'FilterChip';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAuBA;AACE;AACE;AACE;AACA;AACD;AACF;AACD;AACE;AACD;AACF;AAID;;;;AAIG;AACH;;AAC6B;;AAEzB;AACA;;AAEF;AACF;AAkCA;;;;;;;AAOG;;AAkBD;AACE;AACA;AAEF;;AAIA;AAEA;AACA;AAEA;;;AAG0B;AACxB;AAIF;;;AAIE;;;;;AAQI;AASQ;AACE;AACD;;AAQC;;;AAiBV;;AAYQ;AACE;AACA;AACD;;AAKX;;;AAeJ;AAEA;AAqDF;AAEA;;"}