{"id":19714343,"url":"https://github.com/jaleelb/emblor","last_synced_at":"2025-05-14T01:07:11.077Z","repository":{"id":192085262,"uuid":"685783651","full_name":"JaleelB/emblor","owner":"JaleelB","description":"A fully-featured tag input component built with shadcn/ui","archived":false,"fork":false,"pushed_at":"2025-03-28T04:01:08.000Z","size":2350,"stargazers_count":1037,"open_issues_count":24,"forks_count":44,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-03T22:07:11.292Z","etag":null,"topics":["autocomplete","input","popover","radix","react-tag-input","shadcn-ui","tag-input"],"latest_commit_sha":null,"homepage":"https://emblor.jaleelbennett.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JaleelB.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-01T02:08:55.000Z","updated_at":"2025-04-03T17:14:02.000Z","dependencies_parsed_at":"2023-09-02T13:38:51.378Z","dependency_job_id":"c324f151-2755-46a6-8a6d-4734a9d064d4","html_url":"https://github.com/JaleelB/emblor","commit_stats":{"total_commits":290,"total_committers":13,"mean_commits":"22.307692307692307","dds":"0.14137931034482754","last_synced_commit":"10f51323f71fdb3e65c683c6008058a2395dc2ae"},"previous_names":["jaleelb/shadcn-tag-input","jaleelb/emblor"],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JaleelB%2Femblor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JaleelB%2Femblor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JaleelB%2Femblor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JaleelB%2Femblor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JaleelB","download_url":"https://codeload.github.com/JaleelB/emblor/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248324501,"owners_count":21084725,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autocomplete","input","popover","radix","react-tag-input","shadcn-ui","tag-input"],"created_at":"2024-11-11T22:30:51.474Z","updated_at":"2025-04-11T01:28:22.577Z","avatar_url":"https://github.com/JaleelB.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"https://github.com/JaleelB/emblor/assets/78449846/7f678789-ef5e-4913-b26c-9317003d6dbc\n\n[Emblor](https://emblor.jaleelbennett.com/) is a highly customizable, accessible, and fully-featured tag input component built with Shadcn UI.\n\n## About\n\nEmblor is built on top of the [Input](https://ui.shadcn.com/docs/components/input), [Popover](https://ui.shadcn.com/docs/components/popover), [Command](https://ui.shadcn.com/docs/components/command) and [Dialog](https://ui.shadcn.com/docs/components/dialog) components from [Shadcn UI](https://ui.shadcn.com/).\n\n## Installation\n\nTo install Emblor, run the command:\n\n```bash\npnpm add emblor\n```\n\n## Features\n\n- **Autocomplete**: Enable autocomplete suggestions for tags.\n- **Validation**: Validate tags based on custom rules.\n- **Limit**: Set a maximum and minimum number of tags.\n- **Duplication**: Allow or disallow duplicate tags.\n- **Character Limit**: Define the maximum length of a tag.\n- **Sorting**: Sort tags alphabetically.\n- **Truncation**: Truncate tags that exceed a certain length.\n- **Popovers**: Use popovers to display tags.\n- **Keyboard Navigation**: Use keyboard shortcuts to interact with the tag input.\n- **Customization**: Change the appearance and behavior of the tags by passing in a custom tag renderer.\n- **Accessibility**: Ensure that the tag input is accessible to all users.\n- **Drag and Drop**: Allow users to reorder tags using drag and drop.\n- **Read-only Mode**: Prevent users from editing the tag input.\n- **Delimiters**: Define custom delimiters for separating tags.\n- **Add on Paste**: Automatically add tags when pasting text.\n\n## Usage\n\nHere's a sample implementation that initializes the component with a list of initial tags and suggestions list. Apart from this, there are multiple events, handlers for which need to be set.\n\nThe example below uses `tailwindcss` `@shadcn/ui` `tailwind-merge` `clsx`:\n\n```tsx\nimport { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';\nimport { Tag, TagInput } from 'emblor';\nimport Link from 'next/link';\nimport { Button, buttonVariants } from '@/components/ui/button';\nimport { z } from 'zod';\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport React from 'react';\nimport { toast } from '@/components/ui/use-toast';\n\nconst FormSchema = z.object({\n  topics: z.array(\n    z.object({\n      id: z.string(),\n      text: z.string(),\n    }),\n  ),\n});\n\nexport default function Hero() {\n  const form = useForm\u003cz.infer\u003ctypeof FormSchema\u003e\u003e({\n    resolver: zodResolver(FormSchema),\n  });\n\n  const [tags, setTags] = React.useState\u003cTag[]\u003e([]);\n\n  const { setValue } = form;\n\n  function onSubmit(data: z.infer\u003ctypeof FormSchema\u003e) {\n    toast({\n      title: 'You submitted the following values:',\n      description: (\n        \u003cpre className=\"mt-2 w-[340px] rounded-md bg-slate-950 p-4\"\u003e\n          \u003ccode className=\"text-white\"\u003e{JSON.stringify(data, null, 2)}\u003c/code\u003e\n        \u003c/pre\u003e\n      ),\n    });\n  }\n\n  return (\n    \u003csection className=\"z-10 max-w-5xl w-full flex flex-col items-center text-center gap-5\"\u003e\n      \u003cdiv className=\"z-10 w-full flex flex-col items-center text-center gap-5\"\u003e\n        \u003ch1 className=\"scroll-m-20 text-4xl font-bold tracking-tight\"\u003eShadcn Tag Input\u003c/h1\u003e\n        \u003cp className=\"text-muted-foreground max-w-[450px]\"\u003e\n          An implementation of a Tag Input component built on top of Shadcn UI\u0026apos;s input component.\n        \u003c/p\u003e\n        \u003cdiv className=\"flex gap-2 mt-1\"\u003e\n          \u003cLink href=\"#try\" className={`${buttonVariants({ variant: 'default', size: 'lg' })} min-w-[150px] shadow-sm`}\u003e\n            Try it out\n          \u003c/Link\u003e\n          \u003cLink\n            href=\"https://github.com/JaleelB/shadcn-tag-input\"\n            className={`${buttonVariants({ variant: 'secondary', size: 'lg' })} shadow-sm`}\n          \u003e\n            Github\n          \u003c/Link\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n\n      \u003cdiv id=\"try\" className=\"w-full py-8\"\u003e\n        \u003cdiv className=\"w-full relative my-4 flex flex-col space-y-2\"\u003e\n          \u003cdiv className=\"preview flex min-h-[350px] w-full justify-center p-10 items-center mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 relative rounded-md border\"\u003e\n            \u003cForm {...form}\u003e\n              \u003cform onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8 flex flex-col items-start\"\u003e\n                \u003cFormField\n                  control={form.control}\n                  name=\"topics\"\n                  render={({ field }) =\u003e (\n                    \u003cFormItem className=\"flex flex-col items-start\"\u003e\n                      \u003cFormLabel className=\"text-left\"\u003eTopics\u003c/FormLabel\u003e\n                      \u003cFormControl\u003e\n                        \u003cTagInput\n                          {...field}\n                          placeholder=\"Enter a topic\"\n                          tags={tags}\n                          className=\"sm:min-w-[450px]\"\n                          setTags={(newTags) =\u003e {\n                            setTags(newTags);\n                            setValue('topics', newTags as [Tag, ...Tag[]]);\n                          }}\n                        /\u003e\n                      \u003c/FormControl\u003e\n                      \u003cFormDescription\u003eThese are the topics that you\u0026apos;re interested in.\u003c/FormDescription\u003e\n                      \u003cFormMessage /\u003e\n                    \u003c/FormItem\u003e\n                  )}\n                /\u003e\n                \u003cButton type=\"submit\"\u003eSubmit\u003c/Button\u003e\n              \u003c/form\u003e\n            \u003c/Form\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/section\u003e\n  );\n}\n```\n\n## API Reference\n\n### TagInput\n\nThe primary component for user interaction. Configure the tag input behavior and appearance using these props, and manage tag data dynamically.\n\n#### Props\n\n```typescript\ntype TagInputProps = {\n  // Placeholder text for the input.\n  placeholder?: string; // default: \"\"\n\n  // Array of tags displayed as pre-selected.\n  tags: Array\u003c{ id: string; text: string }\u003e; // default: []\n\n  // Function to set the state of tags.\n  setTags: React.Dispatch\u003cReact.SetStateAction\u003c{ id: string; text: string }[]\u003e\u003e;\n\n  // Enable or disable the autocomplete feature.\n  enableAutocomplete?: boolean; // default: false\n\n  // List of autocomplete options.\n  autocompleteOptions?: Array\u003c{ id: string; text: string }\u003e; // default: []\n\n  // Maximum number of tags allowed.\n  maxTags?: number; // default: null\n\n  // Minimum number of tags required.\n  minTags?: number; // default: null\n\n  // Make the input read-only.\n  readOnly?: boolean; // default: false\n\n  // Disable the input.\n  disabled?: boolean; // default: false\n\n  // Callback function when a tag is added.\n  onTagAdd?: (tag: string) =\u003e void; // default: null\n\n  // Callback function when a tag is removed.\n  onTagRemove?: (tag: string) =\u003e void; // default: null\n\n  // Allow duplicate tags.\n  allowDuplicates?: boolean; // default: false\n\n  // Maximum length of a tag.\n  maxLength?: number; // default: null\n\n  // Minimum length of a tag.\n  minLength?: number; // default: null\n\n  // Function to validate a tag.\n  validateTag?: (tag: string) =\u003e boolean; // default: null\n\n  // Character used to separate tags.\n  delimiter?: Delimiter; // default: null\n\n  // Show the count of tags.\n  showCount?: boolean; // default: false\n\n  // Placeholder text when tag limit is reached.\n  placeholderWhenFull?: string; // default: \"\"\n\n  styleClasses?: {\n    // Class name styles for the tag input container (use when inlineTags is set to true).\n    inlineTagsContainer?: string;\n\n    // Class name styles for the tag popover sub components\n    tagPopover?: {\n      popoverTrigger?: string;\n      popoverContent?: string;\n    };\n\n    // Class name styles for the tag list sub components (the tag list renders the tags as a list)\n    tagList?: {\n      container?: string;\n      sortableList?: string;\n    };\n\n    // Class name styles for the autocomplete component sub components\n    autoComplete?: {\n      command?: string;\n      popoverTrigger?: string;\n      popoverContent?: string;\n      commandList?: string;\n      commandGroup?: string;\n      commandItem?: string;\n    };\n\n    // Class name styles for the tag\n    tag?: {\n      body?: string;\n      closeButton?: string;\n    };\n\n    // Class name styles for the main input field\n    input?: string;\n  }; // default: {}\n\n  // Sort tags alphabetically.\n  sortTags?: boolean; // default: false\n\n  // List of characters that can be used as delimiters.\n  delimiterList?: string[]; // default: []\n\n  // Truncate tag text to a certain length.\n  truncate?: number; // default: null\n\n  // Function to filter autocomplete options.\n  autocompleteFilter?: (option: string) =\u003e boolean; // default: null\n\n  // Layout direction of the tag inputs.\n  direction?: 'row' | 'column'; // default: 'row'\n\n  // A callback function that is called whenever the input value changes.\n  onInputChange?: (value: string) =\u003e void; // default: null\n\n  // A callback function that is used to render custom tag elements.\n  customTagRenderer?: (tag: { id: string; text: string }) =\u003e React.ReactElement; // default: null\n\n  // Function to be called when the input field gains focus.\n  onFocus?: React.FocusEventHandler\u003cHTMLInputElement\u003e; // default: null\n\n  // Function to be called when the input field loses focus.\n  onBlur?: React.FocusEventHandler\u003cHTMLInputElement\u003e; // default: null\n\n  // Only allow tags that are present in the autocomplete options.\n  restrictTagsToAutocompleteOptions?: boolean; // default: false\n\n  // A callback function to be called when a tag is clicked.\n  onTagClick?: (tag: { id: string; text: string }) =\u003e void; // default: null\n\n  // Enable drag and drop functionality.\n  draggable?: boolean; // default: false\n\n  // Position of the input field in relation to the tags.\n  inputFieldPosition?: 'bottom' | 'top' | 'inline'; // default: 'bottom'\n\n  // Show a button to clear all tags.\n  clearAll?: boolean; // default: false\n\n  // A callback function to be called when the clear all button is clicked.\n  onClearAll?: () =\u003e void; // default: null\n\n  // Additional props to be passed to the input field.\n  inputProps?: React.InputHTMLAttributes\u003cHTMLInputElement\u003e; // default: {}\n\n  // Use a popover to display tags instead of inline.\n  usePopoverForTags?: boolean; // default: false\n\n  // A callback function that generates an id for a newly created tag.\n  generateTagId?: () =\u003e string; // default: crypto.getRandomValues(new Uint32Array(1))[0].toString\n};\n```\n\n### Delimiter\n\nDefine the delimiters that can be used to separate tags within the input.\n\n```typescript\nenum Delimiter {\n  Comma = ',',\n  Enter = 'Enter',\n}\n```\n\n## Documentation\n\nYou can find out more about the API and implementation in the [Documentation](https://emblor.jaleelbennett.com/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaleelb%2Femblor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaleelb%2Femblor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaleelb%2Femblor/lists"}