diff --git a/.env.example b/.env.example index 9ec6526..2f44493 100644 --- a/.env.example +++ b/.env.example @@ -100,3 +100,6 @@ NEXT_PUBLIC_STARGAZE_WEBSITE_URL=https://testnet.publicawesome.dev NEXT_PUBLIC_BADGES_URL=https://badges.publicawesome.dev NEXT_PUBLIC_WEBSITE_URL=https:// NEXT_PUBLIC_SYNC_COLLECTIONS_API_URL="https://..." + +NEXT_PUBLIC_MEILISEARCH_HOST="https://search.publicawesome.dev" +NEXT_PUBLIC_MEILISEARCH_API_KEY= "..." diff --git a/components/Fieldset.tsx b/components/Fieldset.tsx new file mode 100644 index 0000000..3afa4ac --- /dev/null +++ b/components/Fieldset.tsx @@ -0,0 +1,79 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable no-implicit-coercion */ +/* eslint-disable import/no-default-export */ +/* eslint-disable tsdoc/syntax */ +import type { ReactNode } from 'react' + +export interface FieldsetBaseType { + /** + * The input's required id, used to link the label and input, as well as the error message. + */ + id: string + /** + * Error message to show input validation. + */ + error?: string + /** + * Success message to show input validation. + */ + success?: string + /** + * Label to describe the input. + */ + label?: string | ReactNode + /** + * Hint to show optional fields or a hint to the user of what to enter in the input. + */ + hint?: string +} + +type FieldsetType = FieldsetBaseType & { + children: ReactNode +} + +/** + * @name Fieldset + * @description A fieldset component, used to share markup for labels, hints, and errors for Input components. + * + * @example + *
+ * + *
+ */ +export default function Fieldset({ label, hint, id, children, error, success }: FieldsetType) { + return ( +
+ {!!label && ( +
+ + + {typeof hint === 'string' && ( + + {hint} + + )} +
+ )} + + {children} + + {error && ( +
+

+ {error} +

+
+ )} + + {success && ( +
+

+ {success} +

+
+ )} +
+ ) +} diff --git a/components/Input.tsx b/components/Input.tsx new file mode 100644 index 0000000..34bf976 --- /dev/null +++ b/components/Input.tsx @@ -0,0 +1,173 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable import/no-default-export */ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ +/* eslint-disable jsx-a11y/autocomplete-valid */ +/* eslint-disable tsdoc/syntax */ +import type { PropsOf } from '@headlessui/react/dist/types' +import type { ReactNode } from 'react' +import { forwardRef } from 'react' +import { classNames } from 'utils/css' + +import type { FieldsetBaseType } from './Fieldset' +import Fieldset from './Fieldset' +import type { TrailingSelectProps } from './TrailingSelect' +import TrailingSelect from './TrailingSelect' + +/** + * Shared styles for all input components. + */ +export const inputClassNames = { + base: [ + 'block w-full rounded-lg bg-white shadow-sm dark:bg-zinc-900 sm:text-sm', + 'text-white placeholder:text-zinc-500 focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-primary-500 focus:ring-0 focus:ring-offset-0', + ], + valid: 'border-zinc-300 focus:border-zinc-300 dark:border-zinc-800 dark:focus:border-zinc-800', + invalid: '!text-red-500 !border-red-500 focus:!border-red-500', + success: 'text-green border-green focus:border-green', +} + +type InputProps = Omit & FieldsetBaseType, 'className'> & { + directory?: 'true' + mozdirectory?: 'true' + webkitdirectory?: 'true' + leadingAddon?: string + trailingAddon?: string + trailingAddonIcon?: ReactNode + trailingSelectProps?: TrailingSelectProps + autoCompleteOff?: boolean + preventAutoCapitalizeFirstLetter?: boolean + className?: string + icon?: JSX.Element +} + +/** + * @name Input + * @description A standard input component, defaults to the text type. + * + * @example + * // Standard input + * + * + * @example + * // Input component with label, placeholder and type email + * + * + * @example + * // Input component with label and leading and trailing addons + * + * + * @example + * // Input component with label and trailing select + * const [trailingSelectValue, trailingSelectValueSet] = useState('USD'); + * + * trailingSelectValueSet(event.target.value), + * options: ['USD', 'CAD', 'EUR'], + * }} + * /> + */ +const Input = forwardRef( + ( + { + error, + success, + hint, + label, + leadingAddon, + trailingAddon, + trailingAddonIcon, + trailingSelectProps, + id, + className, + type = 'text', + autoCompleteOff = false, + preventAutoCapitalizeFirstLetter, + icon, + ...rest + }, + ref, + ) => { + const cachedClassNames = classNames( + ...inputClassNames.base, + className, + error ? inputClassNames.invalid : inputClassNames.valid, + success ? inputClassNames.success : inputClassNames.valid, + leadingAddon && 'pl-7', + trailingAddon && 'pr-12', + trailingSelectProps && 'pr-16', + icon && 'pl-10', + ) + + const describedBy = [ + ...(error ? [`${id}-error`] : []), + ...(success ? [`${id}-success`] : []), + ...(typeof hint === 'string' ? [`${id}-optional`] : []), + ...(typeof trailingAddon === 'string' ? [`${id}-addon`] : []), + ].join(' ') + + return ( +
+
+ {leadingAddon && ( +
+ {leadingAddon} +
+ )} + + {icon && ( +
+ {icon} +
+ )} + + + + {!trailingAddon && trailingSelectProps && } + + {trailingAddon && ( +
+ + {trailingAddon} + +
+ )} + + {trailingAddonIcon && ( +
+ + {trailingAddonIcon} + +
+ )} +
+
+ ) + }, +) + +Input.displayName = 'Input' + +export default Input diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx index 4e3389e..0eccd53 100644 --- a/components/Sidebar.tsx +++ b/components/Sidebar.tsx @@ -108,6 +108,15 @@ export const Sidebar = () => { > Collection Actions +
  • + Snapshots +