sigma-ui 1.2.0 → 1.3.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.
@@ -373,6 +373,16 @@
373
373
  ],
374
374
  "type": "components:ui"
375
375
  },
376
+ {
377
+ "name": "faceted-filter",
378
+ "dependencies": [],
379
+ "registryDependencies": [],
380
+ "files": [
381
+ "faceted-filter/FacetedFilter.vue",
382
+ "faceted-filter/index.ts"
383
+ ],
384
+ "type": "components:ui"
385
+ },
376
386
  {
377
387
  "name": "form",
378
388
  "dependencies": [
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "faceted-filter",
3
+ "dependencies": [],
4
+ "registryDependencies": [],
5
+ "files": [
6
+ {
7
+ "name": "FacetedFilter.vue",
8
+ "content": "<script setup lang=\"ts\">\nimport { computed, ref } from 'vue';\nimport { CheckIcon, CirclePlusIcon } from 'lucide-vue-next';\nimport { Button } from '@ui/registry/css/ui/button';\nimport { Popover, PopoverContent, PopoverTrigger } from '@ui/registry/css/ui/popover';\nimport { Separator } from '@ui/registry/css/ui/separator';\nimport {\n Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator,\n} from '@ui/registry/css/ui/command';\n\nconst props = withDefaults(defineProps<{\n title: string;\n options: string[];\n modelValue: string[];\n maxBadges?: number;\n allowCreate?: boolean;\n minWidth?: number;\n}>(), {\n maxBadges: 2,\n allowCreate: false,\n minWidth: 200,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string[]];\n 'create': [value: string];\n}>();\n\nconst searchQuery = ref('');\nconst commandKey = ref(0);\n\nconst trimmedSearchQuery = computed(() => searchQuery.value.trim());\nconst selectedValues = computed(() => new Set(props.modelValue));\nconst filteredOptions = computed(() => {\n const normalizedSearch = searchQuery.value.trim().toLowerCase();\n if (!normalizedSearch) return props.options;\n return props.options.filter(option => option.toLowerCase().includes(normalizedSearch));\n});\n\nconst canCreate = computed(() => {\n if (!props.allowCreate) return false;\n const value = trimmedSearchQuery.value;\n if (value.length === 0) return false;\n const normalizedValue = value.toLowerCase();\n return !props.options.some(option => option.toLowerCase() === normalizedValue);\n});\n\nconst selectedBadges = computed(() => {\n return props.options.filter(option => selectedValues.value.has(option)).slice(0, props.maxBadges);\n});\n\nconst contentStyle = computed(() => props.minWidth ? ({ minWidth: `${props.minWidth}px` }) : undefined);\n\nfunction toggleValue(value: string) {\n const next = new Set(props.modelValue);\n if (next.has(value)) next.delete(value);\n else next.add(value);\n emit('update:modelValue', Array.from(next));\n}\n\nfunction createFromSearchQuery() {\n const value = trimmedSearchQuery.value;\n if (!value) return;\n emit('create', value);\n clearSearch();\n commandKey.value += 1;\n}\n\nfunction clearSearch() {\n searchQuery.value = '';\n}\n</script>\n\n<template>\n <Popover>\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n size=\"xs\"\n class=\"sigma-ui-faceted-filter__trigger\"\n >\n <CirclePlusIcon class=\"sigma-ui-faceted-filter__trigger-icon\" />\n {{ props.title }}\n <template v-if=\"selectedValues.size > 0\">\n <Separator\n orientation=\"vertical\"\n class=\"sigma-ui-faceted-filter__separator\"\n />\n <span class=\"sigma-ui-faceted-filter__count\">\n {{ selectedValues.size }}\n </span>\n <div class=\"sigma-ui-faceted-filter__badges\">\n <span\n v-for=\"badge in selectedBadges\"\n :key=\"badge\"\n class=\"sigma-ui-faceted-filter__badge\"\n >\n {{ badge }}\n </span>\n <span\n v-if=\"selectedValues.size > props.maxBadges\"\n class=\"sigma-ui-faceted-filter__badge\"\n >\n +{{ selectedValues.size - props.maxBadges }}\n </span>\n </div>\n </template>\n </Button>\n </PopoverTrigger>\n\n <PopoverContent\n class=\"sigma-ui-faceted-filter__content\"\n align=\"start\"\n :style=\"contentStyle\"\n >\n <Command :key=\"commandKey\">\n <CommandInput\n v-model=\"searchQuery\"\n :placeholder=\"props.title\"\n @keydown.esc=\"clearSearch\"\n />\n <CommandList>\n <CommandEmpty v-if=\"filteredOptions.length === 0 && !canCreate\">\n No results found.\n </CommandEmpty>\n <div\n v-if=\"canCreate\"\n class=\"sigma-ui-faceted-filter__empty-create\"\n >\n <Button\n variant=\"outline\"\n size=\"sm\"\n class=\"sigma-ui-faceted-filter__empty-create-button\"\n @click=\"createFromSearchQuery\"\n >\n <CirclePlusIcon class=\"sigma-ui-faceted-filter__empty-create-icon\" />\n Add {{ trimmedSearchQuery }}\n </Button>\n </div>\n <CommandGroup>\n <CommandItem\n v-for=\"option in filteredOptions\"\n :key=\"option\"\n :value=\"option\"\n @select=\"() => toggleValue(option)\"\n >\n <div\n class=\"sigma-ui-faceted-filter__checkbox\"\n :data-selected=\"selectedValues.has(option) || undefined\"\n >\n <CheckIcon class=\"sigma-ui-faceted-filter__check\" />\n </div>\n <span class=\"sigma-ui-faceted-filter__option-text\">{{ option }}</span>\n </CommandItem>\n </CommandGroup>\n <CommandSeparator v-if=\"$slots.footer\" />\n <div\n v-if=\"$slots.footer\"\n class=\"sigma-ui-faceted-filter__footer\"\n >\n <slot name=\"footer\" />\n </div>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n</template>\n\n<style>\n.sigma-ui-faceted-filter__trigger {\n gap: 0.5rem;\n border-style: dashed;\n}\n\n.sigma-ui-faceted-filter__trigger-icon {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n.sigma-ui-faceted-filter__separator {\n height: 0.875rem;\n margin: 0 0.375rem;\n}\n\n.sigma-ui-faceted-filter__count {\n display: inline-flex;\n height: 1rem;\n align-items: center;\n justify-content: center;\n padding: 0 0.375rem;\n border-radius: 0.375rem;\n background-color: hsl(var(--secondary));\n color: hsl(var(--secondary-foreground));\n font-size: 0.6875rem;\n}\n\n.sigma-ui-faceted-filter__badges {\n display: none;\n gap: 0.375rem;\n}\n\n.sigma-ui-faceted-filter__badge {\n display: inline-flex;\n height: 1rem;\n align-items: center;\n justify-content: center;\n padding: 0 0.375rem;\n border-radius: 0.375rem;\n background-color: hsl(var(--secondary));\n color: hsl(var(--secondary-foreground));\n font-size: 0.6875rem;\n}\n\n.sigma-ui-faceted-filter__content {\n width: 20rem;\n padding: 0;\n}\n\n.sigma-ui-faceted-filter__empty-create {\n padding: 0.625rem;\n}\n\n.sigma-ui-faceted-filter__empty-create-button {\n width: 100%;\n gap: 0.5rem;\n justify-content: flex-start;\n}\n\n.sigma-ui-faceted-filter__empty-create-icon {\n width: 1rem;\n height: 1rem;\n}\n\n.sigma-ui-faceted-filter__checkbox {\n display: flex;\n width: 1rem;\n height: 1rem;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n border: 1px solid hsl(var(--border));\n border-radius: 0.25rem;\n margin-right: 0.625rem;\n opacity: 0.6;\n}\n\n.sigma-ui-faceted-filter__checkbox:not([data-selected]) .sigma-ui-faceted-filter__check {\n visibility: hidden;\n}\n\n.sigma-ui-faceted-filter__checkbox[data-selected] {\n border-color: hsl(var(--primary) / 60%);\n background: hsl(var(--primary) / 15%);\n opacity: 1;\n}\n\n.sigma-ui-faceted-filter__check {\n width: 0.875rem;\n height: 0.875rem;\n color: hsl(var(--primary));\n}\n\n.sigma-ui-faceted-filter__option-text {\n overflow: hidden;\n text-overflow: ellipsis;\n user-select: text;\n white-space: nowrap;\n}\n\n.sigma-ui-faceted-filter__footer {\n padding: 0.625rem 0.625rem 0.75rem;\n}\n\n@media (width >= 1024px) {\n .sigma-ui-faceted-filter__badges {\n display: flex;\n }\n\n .sigma-ui-faceted-filter__count {\n display: none;\n }\n}\n</style>\n"
9
+ },
10
+ {
11
+ "name": "index.ts",
12
+ "content": "export { default as FacetedFilter } from './FacetedFilter.vue';\n"
13
+ }
14
+ ],
15
+ "type": "components:ui"
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "faceted-filter",
3
+ "dependencies": [],
4
+ "registryDependencies": [],
5
+ "files": [
6
+ {
7
+ "name": "FacetedFilter.vue",
8
+ "content": "<script setup lang=\"ts\">\nimport { computed, ref } from 'vue';\nimport { CheckIcon, CirclePlusIcon } from 'lucide-vue-next';\nimport { cn } from '@ui/utils';\nimport { Button } from '@ui/registry/tailwind/ui/button';\nimport { Popover, PopoverContent, PopoverTrigger } from '@ui/registry/tailwind/ui/popover';\nimport { Separator } from '@ui/registry/tailwind/ui/separator';\nimport {\n Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator,\n} from '@ui/registry/tailwind/ui/command';\n\nconst props = withDefaults(defineProps<{\n title: string;\n options: string[];\n modelValue: string[];\n maxBadges?: number;\n allowCreate?: boolean;\n minWidth?: number;\n}>(), {\n maxBadges: 2,\n allowCreate: false,\n minWidth: 200,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string[]];\n 'create': [value: string];\n}>();\n\nconst searchQuery = ref('');\nconst commandKey = ref(0);\n\nconst trimmedSearchQuery = computed(() => searchQuery.value.trim());\nconst selectedValues = computed(() => new Set(props.modelValue));\nconst filteredOptions = computed(() => {\n const normalizedSearch = searchQuery.value.trim().toLowerCase();\n if (!normalizedSearch) return props.options;\n return props.options.filter(option => option.toLowerCase().includes(normalizedSearch));\n});\n\nconst canCreate = computed(() => {\n if (!props.allowCreate) return false;\n const value = trimmedSearchQuery.value;\n if (value.length === 0) return false;\n const normalizedValue = value.toLowerCase();\n return !props.options.some(option => option.toLowerCase() === normalizedValue);\n});\n\nconst selectedBadges = computed(() => {\n return props.options.filter(option => selectedValues.value.has(option)).slice(0, props.maxBadges);\n});\n\nconst contentStyle = computed(() => props.minWidth ? ({ minWidth: `${props.minWidth}px` }) : undefined);\n\nfunction toggleValue(value: string) {\n const next = new Set(props.modelValue);\n if (next.has(value)) next.delete(value);\n else next.add(value);\n emit('update:modelValue', Array.from(next));\n}\n\nfunction createFromSearchQuery() {\n const value = trimmedSearchQuery.value;\n if (!value) return;\n emit('create', value);\n clearSearch();\n commandKey.value += 1;\n}\n\nfunction clearSearch() {\n searchQuery.value = '';\n}\n</script>\n\n<template>\n <Popover>\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n size=\"xs\"\n class=\"gap-2 border-dashed\"\n >\n <CirclePlusIcon class=\"size-3.5\" />\n {{ props.title }}\n <template v-if=\"selectedValues.size > 0\">\n <Separator\n orientation=\"vertical\"\n class=\"mx-1.5 h-3.5\"\n />\n <span class=\"inline-flex h-4 items-center justify-center rounded-md bg-secondary px-1.5 text-[11px] text-secondary-foreground lg:hidden\">\n {{ selectedValues.size }}\n </span>\n <div class=\"hidden gap-1.5 lg:flex\">\n <span\n v-for=\"badge in selectedBadges\"\n :key=\"badge\"\n class=\"inline-flex h-4 items-center justify-center rounded-md bg-secondary px-1.5 text-[11px] text-secondary-foreground\"\n >\n {{ badge }}\n </span>\n <span\n v-if=\"selectedValues.size > props.maxBadges\"\n class=\"inline-flex h-4 items-center justify-center rounded-md bg-secondary px-1.5 text-[11px] text-secondary-foreground\"\n >\n +{{ selectedValues.size - props.maxBadges }}\n </span>\n </div>\n </template>\n </Button>\n </PopoverTrigger>\n\n <PopoverContent\n class=\"w-80 p-0\"\n align=\"start\"\n :style=\"contentStyle\"\n >\n <Command :key=\"commandKey\">\n <CommandInput\n v-model=\"searchQuery\"\n :placeholder=\"props.title\"\n @keydown.esc=\"clearSearch\"\n />\n <CommandList>\n <CommandEmpty v-if=\"filteredOptions.length === 0 && !canCreate\">\n No results found.\n </CommandEmpty>\n <div\n v-if=\"canCreate\"\n class=\"p-2.5\"\n >\n <Button\n variant=\"outline\"\n size=\"sm\"\n class=\"w-full justify-start gap-2\"\n @click=\"createFromSearchQuery\"\n >\n <CirclePlusIcon class=\"size-4\" />\n Add {{ trimmedSearchQuery }}\n </Button>\n </div>\n <CommandGroup>\n <CommandItem\n v-for=\"option in filteredOptions\"\n :key=\"option\"\n :value=\"option\"\n @select=\"() => toggleValue(option)\"\n >\n <div\n :class=\"cn(\n 'mr-2.5 flex size-4 shrink-0 items-center justify-center rounded border border-border opacity-60',\n selectedValues.has(option) && 'border-primary/60 bg-primary/15 opacity-100',\n )\"\n >\n <CheckIcon\n :class=\"cn(\n 'size-3.5 text-primary',\n !selectedValues.has(option) && 'invisible',\n )\"\n />\n </div>\n <span class=\"truncate select-text\">{{ option }}</span>\n </CommandItem>\n </CommandGroup>\n <CommandSeparator v-if=\"$slots.footer\" />\n <div\n v-if=\"$slots.footer\"\n class=\"px-2.5 pb-3 pt-2.5\"\n >\n <slot name=\"footer\" />\n </div>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n</template>\n"
9
+ },
10
+ {
11
+ "name": "index.ts",
12
+ "content": "export { default as FacetedFilter } from './FacetedFilter.vue';\n"
13
+ }
14
+ ],
15
+ "type": "components:ui"
16
+ }
package/dist/index.js CHANGED
@@ -25088,7 +25088,7 @@ Components: ${highlight(DEFAULT_COMPONENTS)}`
25088
25088
  tailwindConfigType: detectedConfig.tailwindConfigType,
25089
25089
  setupTailwind: options8.styleSystem === "tailwind" ? detectedConfig.setupTailwind : false,
25090
25090
  components: DEFAULT_COMPONENTS,
25091
- utils: DEFAULT_UTILS,
25091
+ utils: options8.styleSystem === "tailwind" ? DEFAULT_UTILS : "",
25092
25092
  generatePreflight: options8.styleSystem === "css"
25093
25093
  });
25094
25094
  await writeConfigFile(cwd, config);