hooks#useUuid TypeScript Examples

The following examples show how to use hooks#useUuid. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: useIds.tsx    From exevo-pan with The Unlicense 6 votes vote down vote up
useIds = (): UseIdsObject => {
  const tabId = useUuid('tab')
  const panelId = useUuid('panel')

  const getTabId = useCallback((index: number) => `${index}-${tabId}`, [tabId])

  const getPanelId = useCallback(
    (index: number) => `${index}-${panelId}`,
    [panelId],
  )

  return { getTabId, getPanelId }
}
Example #2
Source File: index.tsx    From exevo-pan with The Unlicense 5 votes vote down vote up
Accordion = ({
  title,
  initialValue = false,
  open: openProp,
  onClick,
  border = false,
  children,
  ...props
}: AccordionProps) => {
  const {
    translations: { common },
  } = useTranslations()

  const buttonId = useUuid()
  const contentId = useUuid()

  const [innerOpen, setOpen] = useState(initialValue)
  const open = openProp ?? innerOpen

  const handleClick = useCallback(
    (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
      setOpen((prev) => !prev)
      onClick?.(event)
    },
    [onClick],
  )

  return (
    <div {...props}>
      <button
        id={buttonId}
        type="button"
        aria-expanded={open}
        aria-controls={contentId}
        aria-label={common.Accordion[open ? 'close' : 'open']}
        onClick={handleClick}
        suppressHydrationWarning
        className={clsx(
          'text-onSurface text-tsm flex w-full cursor-pointer items-center justify-between transition-all',
          border && 'border-separator',
          open ? 'py-2' : 'py-1',
        )}
        style={{
          borderBottomStyle: 'solid',
          borderBottomWidth: border ? '1px' : 0,
        }}
      >
        {title}
        <ArrowIcon
          className="fill-onSurface ml-auto shrink-0 rounded transition-all"
          style={{ transform: clsx(open && 'rotate(180deg)') }}
        />
      </button>
      <div id={contentId} aria-describedby={buttonId} suppressHydrationWarning>
        {open && children}
      </div>
    </div>
  )
}
Example #3
Source File: index.tsx    From exevo-pan with The Unlicense 5 votes vote down vote up
LabeledTextBox = ({
  className,
  children,
  labelText,
  warning = false,
  ...props
}: LabeledTextBoxProps) => {
  const {
    translations: { common },
  } = useTranslations()

  const labelId = useUuid()

  return (
    <div
      className={clsx(
        'border-1 relative rounded-md border-solid px-3 pt-2.5 pb-2',
        warning ? 'border-red' : 'border-separator',
        className,
      )}
      aria-labelledby={labelText ? labelId : undefined}
      suppressHydrationWarning
      {...props}
    >
      {labelText && (
        <span
          id={labelId}
          suppressHydrationWarning
          className={clsx(
            'bg-surface absolute top-0 left-2 flex select-none items-center px-1 text-[9px] font-light uppercase tracking-wider',
            warning ? 'text-red' : 'text-onSurface',
          )}
          style={{ transform: 'translateY(-50%)' }}
        >
          {labelText}
          {warning && (
            <Image
              src={warningSrc}
              title={common.WarningLabel}
              unoptimized
              className={styles.warning}
            />
          )}
        </span>
      )}
      {children}
    </div>
  )
}
Example #4
Source File: index.tsx    From exevo-pan with The Unlicense 5 votes vote down vote up
TagButton = ({ ...props }: React.HTMLAttributes<HTMLDivElement>) => {
  const {
    translations: { common },
  } = useTranslations()

  const [hoverState, setHoverState] = useState<HoveredState>('initial')

  const handleHover = useCallback(() => {
    setHoverState('hover')
  }, [])

  const handleUnhover = useCallback(() => {
    setHoverState('off')
  }, [])

  const labelId = useUuid()

  return (
    <NextLink href={routes.ADVERTISE}>
      <div
        suppressHydrationWarning
        tabIndex={0}
        onMouseOver={handleHover}
        onFocus={handleHover}
        onMouseOut={handleUnhover}
        onBlur={handleUnhover}
        aria-describedby={labelId}
        onClick={(event) => event.stopPropagation()}
        className="relative h-11 w-7 shrink-0 cursor-pointer self-start"
        {...props}
      >
        <div
          className="z-2 absolute top-1/2 left-1/2 h-[72px] w-16"
          style={{ transform: 'translate(-50%, -50%)' }}
        />
        <div
          className={clsx(
            'animate-swing z-1 relative h-full w-full',
            hoverState === 'hover' && 'animate-tilt',
            hoverState === 'off' && 'animate-letGo',
            hoverState === 'initial' && 'animate-swing',
          )}
          style={{
            transformOrigin: '50% 0',
            animationDelay: clsx(hoverState === 'initial' && '1s'),
          }}
        >
          <TagIcon
            className="h-full w-full"
            style={{ filter: 'drop-shadow(2px 2px 2px rgba(0, 0, 0, 0.3)' }}
          />
          <AdvertiseIcon
            className="z-1 fill-onPrimary absolute h-4 w-4"
            style={{
              top: 'calc(50% + 6px)',
              left: 'calc(50% - 1px)',
              transform: 'translate(-50%, -50%)',
            }}
          />
        </div>

        <p
          suppressHydrationWarning
          id={labelId}
          className={clsx(
            'text-onSurface absolute bottom-0 left-1/2 text-center text-xs transition-opacity',
            hoverState !== 'hover' && 'opacity-0',
          )}
          style={{ transform: 'translateX(-50%)' }}
        >
          {common.CharacterCard.highlightLabelText}
        </p>
      </div>
    </NextLink>
  )
}
Example #5
Source File: index.tsx    From exevo-pan with The Unlicense 5 votes vote down vote up
PieChart = ({
  title,
  pieDataSet,
  className,
  ...props
}: PieChartProps) => {
  const titleId = useUuid()
  const { colors } = useTheme()

  const options = useMemo(
    () => ({
      responsive: true,
      maintainAspectRatio: false,
      animation: {
        duration: 400,
        easing: 'easeOutCubic',
      },
      legend: {
        display: true,
        position: 'bottom',
        labels: {
          fontColor: colors.onSurface,
          boxWidth: 12,
        },
      },
      tooltips: {
        displayColors: false,
        callbacks: {
          label: (
            tooltipItem: Record<string, number>,
            data: Record<string, Record<string, string>[]>,
          ) => {
            const { index } = tooltipItem
            return `${data.labels[index]}: ${data.datasets[0].data[index]}%`
          },
        },
      },
    }),
    [colors],
  )

  const chartData = useMemo(
    () => ({
      labels: Object.keys(pieDataSet).map(capitalizeFirstLetter),
      datasets: [
        {
          label: title,
          data: Object.keys(pieDataSet).map((item) => pieDataSet[item]),
          fill: false,
          backgroundColor: chartColors,
          borderColor: chartColors,
          borderWidth: 0,
        },
      ],
    }),
    [title, pieDataSet],
  )

  return (
    <section
      className={clsx('card p-5 transition-colors', className)}
      {...props}
    >
      <h4
        id={titleId}
        className="text-onSurface mb-2 text-center text-base font-light"
      >
        {title}
      </h4>
      <div aria-describedby={titleId} className="w-full">
        <Doughnut data={chartData} options={options} />
      </div>
    </section>
  )
}
Example #6
Source File: index.tsx    From exevo-pan with The Unlicense 4 votes vote down vote up
Input = ({
  className,
  style,
  allowClear = false,
  errorMessage,
  value: valueProp,
  onChange,
  hasAlert = true,
  ...props
}: InputProps) => {
  const {
    translations: { common },
  } = useTranslations()

  const errorId = useUuid()

  const [value, setValue] = useState<InputValue>(valueProp ?? '')
  const derivedValue = valueProp ?? value
  const isClearButtonActive = allowClear && !!derivedValue
  const isInvalid = !!errorMessage

  const inputRef = useRef<HTMLInputElement>(null)

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setValue(event.target.value)
  }

  const handleClearClick = () => {
    if (inputRef.current) {
      if (isClearButtonActive) {
        const event = new Event('input', { bubbles: true })
        setValue('')
        inputRef.current.value = ''
        inputRef.current.dispatchEvent(event)
      }
      inputRef.current.focus()
    }
  }

  const handleInput = (event: React.ChangeEvent<HTMLInputElement>) => {
    onChange?.(event)
  }

  return (
    <div className={clsx('text-tsm', className)} style={style}>
      <div
        className={clsx(
          'border-1 bg-surface flex w-full cursor-text rounded-md border-solid transition-colors',
          isInvalid
            ? 'border-red'
            : 'border-separator focus-within:border-primary',
        )}
        onClick={() => inputRef.current?.focus()}
      >
        <input
          ref={inputRef}
          value={derivedValue}
          onChange={handleChange}
          onInput={handleInput}
          aria-invalid={isInvalid}
          aria-errormessage={isInvalid ? errorId : undefined}
          autoComplete="off"
          className="text-tsm text-onSurface w-full border-none bg-transparent py-2.5 px-4 outline-none transition-all"
          style={{ paddingRight: isClearButtonActive ? 0 : undefined }}
          {...props}
        />
        {allowClear && (
          <button
            type="button"
            aria-label={common.ClearInputLabel}
            disabled={!isClearButtonActive}
            aria-hidden={!isClearButtonActive}
            onClick={handleClearClick}
            className={clsx(
              'text-none w-[40px] shrink-0 transition-opacity',
              isClearButtonActive
                ? 'cursor-pointer'
                : 'pointer-events-none opacity-0',
            )}
          >
            <ClearIcon className="fill-onSurface h-5 w-5" />
          </button>
        )}
      </div>
      {hasAlert && (
        <span
          id={errorId}
          aria-hidden={!isInvalid}
          role="alert"
          className={clsx(
            'text-red px-2.5 text-xs transition-opacity',
            !isInvalid && 'opacity-0',
          )}
          suppressHydrationWarning
        >
          {errorMessage}
        </span>
      )}
    </div>
  )
}
Example #7
Source File: index.tsx    From exevo-pan with The Unlicense 4 votes vote down vote up
AutocompleteInput = ({
  className,
  style,
  itemList = [],
  onItemSelect,
  ...props
}: AutocompleteInputProps) => {
  const listboxId = useUuid()

  const [currentList, setCurrentList] = useState<Option[]>(itemList)
  const [{ listboxStatus, highlightedIndex, inputValue }, dispatch] =
    useReducer(AutocompleteInputReducer, {
      listboxStatus: false,
      highlightedIndex: undefined,
      inputValue: '',
    })

  const handleChange = useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      dispatch({ type: 'USER_TYPING', value: event.target.value })
      setCurrentList(filterByTerm(event.target.value, itemList))
    },
    [itemList],
  )

  const handleKeyboard: React.KeyboardEventHandler<HTMLInputElement> = (
    event,
  ) => {
    switch (event.code) {
      case 'Tab':
      case 'Escape':
        dispatch({ type: 'SET_LISTBOX_STATUS', value: false })
        break
      case 'ArrowUp':
        if (currentList.length)
          dispatch({
            type: 'ARROW_NAVIGATION',
            value: -1,
            list: currentList,
          })
        event.preventDefault()
        break
      case 'ArrowDown':
        if (currentList.length)
          dispatch({
            type: 'ARROW_NAVIGATION',
            value: 1,
            list: currentList,
          })
        event.preventDefault()
        break
      case 'Enter':
      case 'NumpadEnter':
        if (currentList.length === 1) {
          onItemSelect?.(currentList[0])
          dispatch({ type: 'OPTION_SELECTED' })
        } else if (highlightedIndex !== undefined) {
          onItemSelect?.(currentList[highlightedIndex])
          dispatch({ type: 'OPTION_SELECTED' })
        }
        break
      default:
        break
    }
  }

  useEffect(() => {
    if (highlightedIndex !== undefined) {
      const item = document.getElementById(
        indexToId(highlightedIndex, listboxId) as string,
      )
      item?.scrollIntoView({
        block: 'nearest',
      })
    }
  }, [highlightedIndex, listboxId])

  useEffect(() => {
    setCurrentList(itemList)
  }, [itemList])

  const onSelectOption = useCallback(
    (option: Option) => {
      onItemSelect?.(option)
      dispatch({ type: 'OPTION_SELECTED' })
    },
    [onItemSelect],
  )

  return (
    <div className={clsx('child:w-full relative', className)} style={style}>
      <Popover
        placement="bottom"
        trigger="none"
        visible={listboxStatus}
        content={
          <Listbox
            id={listboxId}
            highlightedIndex={highlightedIndex}
            onSelectOption={onSelectOption}
            className="max-h-[210px]"
          >
            {currentList.map((item) => (
              <Option key={item.value} value={item.value}>
                {item.name}
              </Option>
            ))}
          </Listbox>
        }
      >
        <Input
          aria-haspopup
          role="combobox"
          aria-autocomplete="list"
          aria-expanded={listboxStatus}
          aria-owns={listboxId}
          allowClear
          value={inputValue}
          onChange={handleChange}
          onFocus={() => dispatch({ type: 'SET_LISTBOX_STATUS', value: true })}
          onClick={() => dispatch({ type: 'SET_LISTBOX_STATUS', value: true })}
          onKeyDown={handleKeyboard}
          hasAlert={false}
          {...props}
        />
      </Popover>
      <button
        type="button"
        onMouseUp={() => dispatch({ type: 'SET_LISTBOX_STATUS', value: false })}
        hidden={!listboxStatus}
        className="fixed top-0 left-0 h-screen w-screen"
      />
    </div>
  )
}