tembro 2.0.3 → 2.0.4
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.
- package/dist/components/display/index.cjs +1 -1
- package/dist/components/display/index.d.ts +0 -5
- package/dist/components/display/index.js +22 -27
- package/dist/components/form/demo.cjs +1 -1
- package/dist/components/form/demo.js +1 -2
- package/dist/components/form/index.cjs +1 -1
- package/dist/components/form/index.d.ts +0 -1
- package/dist/components/form/index.js +8 -9
- package/dist/components/form/public.cjs +1 -1
- package/dist/components/form/public.d.ts +0 -1
- package/dist/components/form/public.js +8 -9
- package/dist/components/inputs/index.cjs +1 -1
- package/dist/components/inputs/index.d.ts +0 -2
- package/dist/components/inputs/index.js +3 -5
- package/dist/components/inputs/public.cjs +1 -1
- package/dist/components/inputs/public.d.ts +0 -2
- package/dist/components/inputs/public.js +3 -5
- package/dist/components/layout/index.cjs +1 -1
- package/dist/components/layout/index.d.ts +0 -1
- package/dist/components/layout/index.js +4 -5
- package/dist/components/layout/public.cjs +1 -1
- package/dist/components/layout/public.d.ts +0 -1
- package/dist/components/layout/public.js +2 -3
- package/dist/components/modern/index.cjs +1 -1
- package/dist/components/modern/index.d.ts +0 -4
- package/dist/components/modern/index.js +12 -16
- package/dist/index.cjs +1 -1
- package/dist/index.js +117 -130
- package/dist/public-component-surface.cjs +1 -1
- package/dist/public-component-surface.js +0 -65
- package/dist/showcase/fallback.cjs +3 -3
- package/dist/showcase/fallback.js +140 -314
- package/dist/showcase/premium/index.cjs +1 -1
- package/dist/showcase/premium/index.js +431 -497
- package/dist/showcase/registry-quality.cjs +1 -1
- package/dist/showcase/registry-quality.js +0 -1
- package/dist/showcase/tembro-registry.json.cjs +1 -1
- package/dist/showcase/tembro-registry.json.js +3 -11
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +2 -2
- package/packages/cli/vendor/src/components/display/index.ts +0 -5
- package/packages/cli/vendor/src/components/form/demo.tsx +0 -1
- package/packages/cli/vendor/src/components/form/index.ts +0 -1
- package/packages/cli/vendor/src/components/form/public.ts +0 -2
- package/packages/cli/vendor/src/components/inputs/index.ts +0 -2
- package/packages/cli/vendor/src/components/inputs/public.ts +0 -2
- package/packages/cli/vendor/src/components/layout/index.ts +0 -1
- package/packages/cli/vendor/src/components/layout/public.ts +0 -1
- package/packages/cli/vendor/src/components/modern/index.ts +1 -5
- package/packages/cli/vendor/src/public-component-surface.ts +0 -13
- package/packages/cli/vendor/src/showcase/fallback.tsx +3 -164
- package/packages/cli/vendor/src/showcase/premium/index.ts +0 -22
- package/packages/cli/vendor/src/showcase/registry-quality.ts +0 -2
- package/packages/cli/vendor/src/showcase/tembro-registry.json +224 -251
- package/registry.json +296 -324
- package/dist/components/display/aspect-ratio.cjs +0 -1
- package/dist/components/display/aspect-ratio.d.ts +0 -6
- package/dist/components/display/aspect-ratio.js +0 -21
- package/dist/components/display/float-button.cjs +0 -1
- package/dist/components/display/float-button.d.ts +0 -6
- package/dist/components/display/float-button.js +0 -20
- package/dist/components/display/key-value-card.cjs +0 -1
- package/dist/components/display/key-value-card.d.ts +0 -16
- package/dist/components/display/key-value-card.js +0 -35
- package/dist/components/display/keyboard-shortcut.cjs +0 -1
- package/dist/components/display/keyboard-shortcut.d.ts +0 -7
- package/dist/components/display/keyboard-shortcut.js +0 -18
- package/dist/components/display/skeleton-form.cjs +0 -1
- package/dist/components/display/skeleton-form.d.ts +0 -9
- package/dist/components/display/skeleton-form.js +0 -31
- package/dist/components/display/skeleton-table.cjs +0 -1
- package/dist/components/display/skeleton-table.d.ts +0 -9
- package/dist/components/display/skeleton-table.js +0 -42
- package/dist/components/display/tag-list.cjs +0 -1
- package/dist/components/display/tag-list.d.ts +0 -17
- package/dist/components/display/tag-list.js +0 -30
- package/dist/components/display/watermark.cjs +0 -1
- package/dist/components/display/watermark.d.ts +0 -6
- package/dist/components/display/watermark.js +0 -23
- package/dist/components/form/repeater-field.cjs +0 -1
- package/dist/components/form/repeater-field.d.ts +0 -16
- package/dist/components/form/repeater-field.js +0 -48
- package/dist/components/inputs/cascader.cjs +0 -1
- package/dist/components/inputs/cascader.d.ts +0 -13
- package/dist/components/inputs/cascader.js +0 -58
- package/dist/components/inputs/mention-input.cjs +0 -1
- package/dist/components/inputs/mention-input.d.ts +0 -18
- package/dist/components/inputs/mention-input.js +0 -96
- package/dist/components/layout/stack.cjs +0 -1
- package/dist/components/layout/stack.d.ts +0 -23
- package/dist/components/layout/stack.js +0 -57
- package/dist/components/modern/affix.cjs +0 -1
- package/dist/components/modern/affix.d.ts +0 -7
- package/dist/components/modern/affix.js +0 -18
- package/dist/showcase/premium/affix/index.cjs +0 -1
- package/dist/showcase/premium/affix/index.d.ts +0 -2
- package/dist/showcase/premium/affix/index.js +0 -3
- package/dist/showcase/premium/affix/mock.cjs +0 -12
- package/dist/showcase/premium/affix/mock.d.ts +0 -2
- package/dist/showcase/premium/affix/mock.js +0 -15
- package/dist/showcase/premium/affix/showcase.cjs +0 -1
- package/dist/showcase/premium/affix/showcase.d.ts +0 -2
- package/dist/showcase/premium/affix/showcase.js +0 -64
- package/dist/showcase/premium/aspect-ratio/index.cjs +0 -1
- package/dist/showcase/premium/aspect-ratio/index.d.ts +0 -2
- package/dist/showcase/premium/aspect-ratio/index.js +0 -3
- package/dist/showcase/premium/aspect-ratio/mock.cjs +0 -11
- package/dist/showcase/premium/aspect-ratio/mock.d.ts +0 -2
- package/dist/showcase/premium/aspect-ratio/mock.js +0 -26
- package/dist/showcase/premium/aspect-ratio/showcase.cjs +0 -1
- package/dist/showcase/premium/aspect-ratio/showcase.d.ts +0 -2
- package/dist/showcase/premium/aspect-ratio/showcase.js +0 -53
- package/dist/showcase/premium/cascader/index.cjs +0 -1
- package/dist/showcase/premium/cascader/index.d.ts +0 -2
- package/dist/showcase/premium/cascader/index.js +0 -3
- package/dist/showcase/premium/cascader/mock.cjs +0 -9
- package/dist/showcase/premium/cascader/mock.d.ts +0 -2
- package/dist/showcase/premium/cascader/mock.js +0 -19
- package/dist/showcase/premium/cascader/showcase.cjs +0 -1
- package/dist/showcase/premium/cascader/showcase.d.ts +0 -2
- package/dist/showcase/premium/cascader/showcase.js +0 -43
- package/dist/showcase/premium/float-button/index.cjs +0 -1
- package/dist/showcase/premium/float-button/index.d.ts +0 -2
- package/dist/showcase/premium/float-button/index.js +0 -3
- package/dist/showcase/premium/float-button/mock.cjs +0 -10
- package/dist/showcase/premium/float-button/mock.d.ts +0 -2
- package/dist/showcase/premium/float-button/mock.js +0 -15
- package/dist/showcase/premium/float-button/showcase.cjs +0 -1
- package/dist/showcase/premium/float-button/showcase.d.ts +0 -2
- package/dist/showcase/premium/float-button/showcase.js +0 -28
- package/dist/showcase/premium/keyboard-shortcut/index.cjs +0 -1
- package/dist/showcase/premium/keyboard-shortcut/index.d.ts +0 -2
- package/dist/showcase/premium/keyboard-shortcut/index.js +0 -3
- package/dist/showcase/premium/keyboard-shortcut/mock.cjs +0 -5
- package/dist/showcase/premium/keyboard-shortcut/mock.d.ts +0 -2
- package/dist/showcase/premium/keyboard-shortcut/mock.js +0 -26
- package/dist/showcase/premium/keyboard-shortcut/showcase.cjs +0 -1
- package/dist/showcase/premium/keyboard-shortcut/showcase.d.ts +0 -1
- package/dist/showcase/premium/keyboard-shortcut/showcase.js +0 -64
- package/dist/showcase/premium/mention-input/index.cjs +0 -1
- package/dist/showcase/premium/mention-input/index.d.ts +0 -2
- package/dist/showcase/premium/mention-input/index.js +0 -3
- package/dist/showcase/premium/mention-input/mock.cjs +0 -11
- package/dist/showcase/premium/mention-input/mock.d.ts +0 -2
- package/dist/showcase/premium/mention-input/mock.js +0 -19
- package/dist/showcase/premium/mention-input/showcase.cjs +0 -1
- package/dist/showcase/premium/mention-input/showcase.d.ts +0 -2
- package/dist/showcase/premium/mention-input/showcase.js +0 -35
- package/dist/showcase/premium/skeleton-form/index.cjs +0 -1
- package/dist/showcase/premium/skeleton-form/index.d.ts +0 -2
- package/dist/showcase/premium/skeleton-form/index.js +0 -3
- package/dist/showcase/premium/skeleton-form/mock.cjs +0 -5
- package/dist/showcase/premium/skeleton-form/mock.d.ts +0 -2
- package/dist/showcase/premium/skeleton-form/mock.js +0 -19
- package/dist/showcase/premium/skeleton-form/showcase.cjs +0 -1
- package/dist/showcase/premium/skeleton-form/showcase.d.ts +0 -2
- package/dist/showcase/premium/skeleton-form/showcase.js +0 -52
- package/dist/showcase/premium/skeleton-table/index.cjs +0 -1
- package/dist/showcase/premium/skeleton-table/index.d.ts +0 -2
- package/dist/showcase/premium/skeleton-table/index.js +0 -3
- package/dist/showcase/premium/skeleton-table/mock.cjs +0 -5
- package/dist/showcase/premium/skeleton-table/mock.d.ts +0 -2
- package/dist/showcase/premium/skeleton-table/mock.js +0 -19
- package/dist/showcase/premium/skeleton-table/showcase.cjs +0 -1
- package/dist/showcase/premium/skeleton-table/showcase.d.ts +0 -2
- package/dist/showcase/premium/skeleton-table/showcase.js +0 -59
- package/dist/showcase/premium/stack/index.cjs +0 -1
- package/dist/showcase/premium/stack/index.d.ts +0 -2
- package/dist/showcase/premium/stack/index.js +0 -3
- package/dist/showcase/premium/stack/mock.cjs +0 -10
- package/dist/showcase/premium/stack/mock.d.ts +0 -2
- package/dist/showcase/premium/stack/mock.js +0 -26
- package/dist/showcase/premium/stack/showcase.cjs +0 -1
- package/dist/showcase/premium/stack/showcase.d.ts +0 -1
- package/dist/showcase/premium/stack/showcase.js +0 -47
- package/dist/showcase/premium/tag-list/index.cjs +0 -1
- package/dist/showcase/premium/tag-list/index.d.ts +0 -2
- package/dist/showcase/premium/tag-list/index.js +0 -3
- package/dist/showcase/premium/tag-list/mock.cjs +0 -12
- package/dist/showcase/premium/tag-list/mock.d.ts +0 -2
- package/dist/showcase/premium/tag-list/mock.js +0 -26
- package/dist/showcase/premium/tag-list/showcase.cjs +0 -1
- package/dist/showcase/premium/tag-list/showcase.d.ts +0 -2
- package/dist/showcase/premium/tag-list/showcase.js +0 -83
- package/dist/showcase/premium/watermark/index.cjs +0 -1
- package/dist/showcase/premium/watermark/index.d.ts +0 -2
- package/dist/showcase/premium/watermark/index.js +0 -3
- package/dist/showcase/premium/watermark/mock.cjs +0 -11
- package/dist/showcase/premium/watermark/mock.d.ts +0 -2
- package/dist/showcase/premium/watermark/mock.js +0 -26
- package/dist/showcase/premium/watermark/showcase.cjs +0 -1
- package/dist/showcase/premium/watermark/showcase.d.ts +0 -2
- package/dist/showcase/premium/watermark/showcase.js +0 -34
- package/packages/cli/vendor/src/components/display/aspect-ratio.tsx +0 -22
- package/packages/cli/vendor/src/components/display/float-button.tsx +0 -27
- package/packages/cli/vendor/src/components/display/key-value-card.tsx +0 -50
- package/packages/cli/vendor/src/components/display/keyboard-shortcut.tsx +0 -31
- package/packages/cli/vendor/src/components/display/skeleton-form.tsx +0 -51
- package/packages/cli/vendor/src/components/display/skeleton-table.tsx +0 -59
- package/packages/cli/vendor/src/components/display/tag-list.tsx +0 -52
- package/packages/cli/vendor/src/components/display/watermark.tsx +0 -20
- package/packages/cli/vendor/src/components/form/repeater-field.tsx +0 -93
- package/packages/cli/vendor/src/components/inputs/cascader.tsx +0 -41
- package/packages/cli/vendor/src/components/inputs/mention-input.tsx +0 -162
- package/packages/cli/vendor/src/components/layout/stack.tsx +0 -106
- package/packages/cli/vendor/src/components/modern/affix.tsx +0 -21
- package/packages/cli/vendor/src/showcase/premium/affix/index.ts +0 -3
- package/packages/cli/vendor/src/showcase/premium/affix/mock.ts +0 -26
- package/packages/cli/vendor/src/showcase/premium/affix/showcase.tsx +0 -37
- package/packages/cli/vendor/src/showcase/premium/aspect-ratio/index.ts +0 -2
- package/packages/cli/vendor/src/showcase/premium/aspect-ratio/mock.ts +0 -26
- package/packages/cli/vendor/src/showcase/premium/aspect-ratio/showcase.tsx +0 -37
- package/packages/cli/vendor/src/showcase/premium/cascader/index.ts +0 -3
- package/packages/cli/vendor/src/showcase/premium/cascader/mock.ts +0 -24
- package/packages/cli/vendor/src/showcase/premium/cascader/showcase.tsx +0 -20
- package/packages/cli/vendor/src/showcase/premium/float-button/index.ts +0 -3
- package/packages/cli/vendor/src/showcase/premium/float-button/mock.ts +0 -24
- package/packages/cli/vendor/src/showcase/premium/float-button/showcase.tsx +0 -21
- package/packages/cli/vendor/src/showcase/premium/keyboard-shortcut/index.ts +0 -2
- package/packages/cli/vendor/src/showcase/premium/keyboard-shortcut/mock.ts +0 -20
- package/packages/cli/vendor/src/showcase/premium/keyboard-shortcut/showcase.tsx +0 -42
- package/packages/cli/vendor/src/showcase/premium/mention-input/index.ts +0 -3
- package/packages/cli/vendor/src/showcase/premium/mention-input/mock.ts +0 -26
- package/packages/cli/vendor/src/showcase/premium/mention-input/showcase.tsx +0 -27
- package/packages/cli/vendor/src/showcase/premium/skeleton-form/index.ts +0 -3
- package/packages/cli/vendor/src/showcase/premium/skeleton-form/mock.ts +0 -20
- package/packages/cli/vendor/src/showcase/premium/skeleton-form/showcase.tsx +0 -34
- package/packages/cli/vendor/src/showcase/premium/skeleton-table/index.ts +0 -3
- package/packages/cli/vendor/src/showcase/premium/skeleton-table/mock.ts +0 -20
- package/packages/cli/vendor/src/showcase/premium/skeleton-table/showcase.tsx +0 -37
- package/packages/cli/vendor/src/showcase/premium/stack/index.ts +0 -2
- package/packages/cli/vendor/src/showcase/premium/stack/mock.ts +0 -25
- package/packages/cli/vendor/src/showcase/premium/stack/showcase.tsx +0 -22
- package/packages/cli/vendor/src/showcase/premium/tag-list/index.ts +0 -2
- package/packages/cli/vendor/src/showcase/premium/tag-list/mock.ts +0 -27
- package/packages/cli/vendor/src/showcase/premium/tag-list/showcase.tsx +0 -55
- package/packages/cli/vendor/src/showcase/premium/watermark/index.ts +0 -2
- package/packages/cli/vendor/src/showcase/premium/watermark/mock.ts +0 -26
- package/packages/cli/vendor/src/showcase/premium/watermark/showcase.tsx +0 -26
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import { cn } from "@/lib/utils"
|
|
4
|
-
|
|
5
|
-
export type MentionOption = {
|
|
6
|
-
label: React.ReactNode
|
|
7
|
-
value: string
|
|
8
|
-
searchText?: string
|
|
9
|
-
insertValue?: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type MentionInputProps = Omit<React.ComponentProps<"textarea">, "value" | "defaultValue"> & {
|
|
13
|
-
value?: string
|
|
14
|
-
defaultValue?: string
|
|
15
|
-
options?: MentionOption[]
|
|
16
|
-
onMentionSelect?: (value: string) => void
|
|
17
|
-
onValueChange?: (value: string) => void
|
|
18
|
-
trigger?: string
|
|
19
|
-
maxSuggestions?: number
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getTriggerMatch(value: string, selectionStart: number, trigger: string) {
|
|
23
|
-
const prefix = value.slice(0, selectionStart)
|
|
24
|
-
const triggerIndex = prefix.lastIndexOf(trigger)
|
|
25
|
-
|
|
26
|
-
if (triggerIndex < 0) return null
|
|
27
|
-
|
|
28
|
-
const previousCharacter = prefix[triggerIndex - 1]
|
|
29
|
-
if (previousCharacter && /\S/.test(previousCharacter)) return null
|
|
30
|
-
|
|
31
|
-
const query = prefix.slice(triggerIndex + trigger.length)
|
|
32
|
-
if (/\s/.test(query)) return null
|
|
33
|
-
|
|
34
|
-
return { triggerIndex, query }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function MentionInput({
|
|
38
|
-
value,
|
|
39
|
-
defaultValue = "",
|
|
40
|
-
options = [],
|
|
41
|
-
onMentionSelect,
|
|
42
|
-
onValueChange,
|
|
43
|
-
className,
|
|
44
|
-
trigger = "@",
|
|
45
|
-
maxSuggestions = 6,
|
|
46
|
-
onChange,
|
|
47
|
-
onKeyDown,
|
|
48
|
-
...props
|
|
49
|
-
}: MentionInputProps) {
|
|
50
|
-
const textareaRef = React.useRef<HTMLTextAreaElement>(null)
|
|
51
|
-
const [internalValue, setInternalValue] = React.useState(defaultValue)
|
|
52
|
-
const [selectionStart, setSelectionStart] = React.useState(0)
|
|
53
|
-
const [activeIndex, setActiveIndex] = React.useState(0)
|
|
54
|
-
const currentValue = value ?? internalValue
|
|
55
|
-
const triggerMatch = getTriggerMatch(currentValue, selectionStart, trigger)
|
|
56
|
-
const filteredOptions = React.useMemo(() => {
|
|
57
|
-
if (!triggerMatch) return []
|
|
58
|
-
const normalizedQuery = triggerMatch.query.trim().toLowerCase()
|
|
59
|
-
|
|
60
|
-
return options
|
|
61
|
-
.filter((option) => {
|
|
62
|
-
if (!normalizedQuery) return true
|
|
63
|
-
const rawLabel = typeof option.label === "string" ? option.label : option.value
|
|
64
|
-
const haystack = `${option.value} ${option.searchText ?? ""} ${rawLabel}`.toLowerCase()
|
|
65
|
-
return haystack.includes(normalizedQuery)
|
|
66
|
-
})
|
|
67
|
-
.slice(0, maxSuggestions)
|
|
68
|
-
}, [maxSuggestions, options, triggerMatch])
|
|
69
|
-
const showSuggestions = filteredOptions.length > 0 && triggerMatch !== null
|
|
70
|
-
|
|
71
|
-
React.useEffect(() => {
|
|
72
|
-
if (!showSuggestions) setActiveIndex(0)
|
|
73
|
-
}, [showSuggestions])
|
|
74
|
-
|
|
75
|
-
function updateValue(nextValue: string, event?: React.ChangeEvent<HTMLTextAreaElement>) {
|
|
76
|
-
if (value === undefined) setInternalValue(nextValue)
|
|
77
|
-
onValueChange?.(nextValue)
|
|
78
|
-
if (event) onChange?.(event)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function insertMention(option: MentionOption) {
|
|
82
|
-
if (!triggerMatch) return
|
|
83
|
-
|
|
84
|
-
const mentionValue = option.insertValue ?? option.value
|
|
85
|
-
const nextValue = `${currentValue.slice(0, triggerMatch.triggerIndex)}${trigger}${mentionValue} ${currentValue.slice(selectionStart)}`
|
|
86
|
-
const nextCursor = triggerMatch.triggerIndex + trigger.length + mentionValue.length + 1
|
|
87
|
-
|
|
88
|
-
if (value === undefined) setInternalValue(nextValue)
|
|
89
|
-
onValueChange?.(nextValue)
|
|
90
|
-
onMentionSelect?.(option.value)
|
|
91
|
-
|
|
92
|
-
requestAnimationFrame(() => {
|
|
93
|
-
textareaRef.current?.focus()
|
|
94
|
-
textareaRef.current?.setSelectionRange(nextCursor, nextCursor)
|
|
95
|
-
setSelectionStart(nextCursor)
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return (
|
|
100
|
-
<div data-slot="mention-input" className="grid gap-2">
|
|
101
|
-
<textarea
|
|
102
|
-
ref={textareaRef}
|
|
103
|
-
value={currentValue}
|
|
104
|
-
className={cn("min-h-24 rounded-md border bg-background p-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring", className)}
|
|
105
|
-
onChange={(event) => {
|
|
106
|
-
updateValue(event.target.value, event)
|
|
107
|
-
setSelectionStart(event.target.selectionStart ?? event.target.value.length)
|
|
108
|
-
}}
|
|
109
|
-
onClick={(event) => setSelectionStart(event.currentTarget.selectionStart ?? 0)}
|
|
110
|
-
onKeyUp={(event) => setSelectionStart(event.currentTarget.selectionStart ?? 0)}
|
|
111
|
-
onKeyDown={(event) => {
|
|
112
|
-
if (showSuggestions) {
|
|
113
|
-
if (event.key === "ArrowDown") {
|
|
114
|
-
event.preventDefault()
|
|
115
|
-
setActiveIndex((current) => (current + 1) % filteredOptions.length)
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
if (event.key === "ArrowUp") {
|
|
119
|
-
event.preventDefault()
|
|
120
|
-
setActiveIndex((current) => (current - 1 + filteredOptions.length) % filteredOptions.length)
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
if (event.key === "Enter" || event.key === "Tab") {
|
|
124
|
-
event.preventDefault()
|
|
125
|
-
insertMention(filteredOptions[activeIndex] ?? filteredOptions[0])
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
if (event.key === "Escape") {
|
|
129
|
-
event.preventDefault()
|
|
130
|
-
setSelectionStart(currentValue.length)
|
|
131
|
-
return
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
onKeyDown?.(event)
|
|
136
|
-
}}
|
|
137
|
-
{...props}
|
|
138
|
-
/>
|
|
139
|
-
{showSuggestions ? (
|
|
140
|
-
<div className="grid gap-1 rounded-xl border bg-card p-2 shadow-sm">
|
|
141
|
-
{filteredOptions.map((option, index) => (
|
|
142
|
-
<button
|
|
143
|
-
key={option.value}
|
|
144
|
-
type="button"
|
|
145
|
-
className={cn(
|
|
146
|
-
"flex items-center justify-between rounded-lg px-3 py-2 text-left text-sm transition-colors hover:bg-muted",
|
|
147
|
-
index === activeIndex && "bg-muted"
|
|
148
|
-
)}
|
|
149
|
-
onMouseDown={(event) => event.preventDefault()}
|
|
150
|
-
onClick={() => insertMention(option)}
|
|
151
|
-
>
|
|
152
|
-
<span className="font-medium">{option.label}</span>
|
|
153
|
-
<span className="text-xs text-muted-foreground">{trigger}{option.insertValue ?? option.value}</span>
|
|
154
|
-
</button>
|
|
155
|
-
))}
|
|
156
|
-
</div>
|
|
157
|
-
) : null}
|
|
158
|
-
</div>
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export { MentionInput }
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import { cn } from "@/lib/utils"
|
|
4
|
-
|
|
5
|
-
type StackGap = "xs" | "sm" | "md" | "lg" | "xl"
|
|
6
|
-
type InlineAlign = "start" | "center" | "end" | "stretch"
|
|
7
|
-
type InlineJustify = "start" | "center" | "end" | "between"
|
|
8
|
-
type GridColumns = 1 | 2 | 3 | 4 | 5 | 6
|
|
9
|
-
|
|
10
|
-
const stackGapClassName: Record<StackGap, string> = {
|
|
11
|
-
xs: "gap-2",
|
|
12
|
-
sm: "gap-3",
|
|
13
|
-
md: "gap-4",
|
|
14
|
-
lg: "gap-6",
|
|
15
|
-
xl: "gap-8",
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const inlineAlignClassName: Record<InlineAlign, string> = {
|
|
19
|
-
start: "items-start",
|
|
20
|
-
center: "items-center",
|
|
21
|
-
end: "items-end",
|
|
22
|
-
stretch: "items-stretch",
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const inlineJustifyClassName: Record<InlineJustify, string> = {
|
|
26
|
-
start: "justify-start",
|
|
27
|
-
center: "justify-center",
|
|
28
|
-
end: "justify-end",
|
|
29
|
-
between: "justify-between",
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const gridColumnClassName: Record<GridColumns, string> = {
|
|
33
|
-
1: "grid-cols-1",
|
|
34
|
-
2: "grid-cols-2",
|
|
35
|
-
3: "grid-cols-3",
|
|
36
|
-
4: "grid-cols-4",
|
|
37
|
-
5: "grid-cols-5",
|
|
38
|
-
6: "grid-cols-6",
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export type StackProps = React.ComponentProps<"div"> & {
|
|
42
|
-
gap?: StackGap
|
|
43
|
-
splitAfter?: React.ReactNode
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function Stack({ gap = "md", splitAfter, className, children, ...props }: StackProps) {
|
|
47
|
-
return (
|
|
48
|
-
<div data-slot="stack" className={cn("flex min-w-0 flex-col", stackGapClassName[gap], className)} {...props}>
|
|
49
|
-
{children}
|
|
50
|
-
{splitAfter ? <div data-slot="stack-split-after">{splitAfter}</div> : null}
|
|
51
|
-
</div>
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export type InlineProps = React.ComponentProps<"div"> & {
|
|
56
|
-
gap?: StackGap
|
|
57
|
-
align?: InlineAlign
|
|
58
|
-
justify?: InlineJustify
|
|
59
|
-
wrap?: boolean
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export type GridProps = React.ComponentProps<"div"> & {
|
|
63
|
-
gap?: StackGap
|
|
64
|
-
columns?: GridColumns
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function Inline({
|
|
68
|
-
gap = "md",
|
|
69
|
-
align = "center",
|
|
70
|
-
justify = "start",
|
|
71
|
-
wrap = true,
|
|
72
|
-
className,
|
|
73
|
-
children,
|
|
74
|
-
...props
|
|
75
|
-
}: InlineProps) {
|
|
76
|
-
return (
|
|
77
|
-
<div
|
|
78
|
-
data-slot="inline"
|
|
79
|
-
className={cn(
|
|
80
|
-
"flex min-w-0",
|
|
81
|
-
stackGapClassName[gap],
|
|
82
|
-
inlineAlignClassName[align],
|
|
83
|
-
inlineJustifyClassName[justify],
|
|
84
|
-
wrap ? "flex-wrap" : "flex-nowrap",
|
|
85
|
-
className
|
|
86
|
-
)}
|
|
87
|
-
{...props}
|
|
88
|
-
>
|
|
89
|
-
{children}
|
|
90
|
-
</div>
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function Grid({ gap = "md", columns = 2, className, children, ...props }: GridProps) {
|
|
95
|
-
return (
|
|
96
|
-
<div
|
|
97
|
-
data-slot="grid"
|
|
98
|
-
className={cn("grid min-w-0", stackGapClassName[gap], gridColumnClassName[columns], className)}
|
|
99
|
-
{...props}
|
|
100
|
-
>
|
|
101
|
-
{children}
|
|
102
|
-
</div>
|
|
103
|
-
)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export { Grid, Inline, Stack }
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import { cn } from "@/lib/utils"
|
|
4
|
-
|
|
5
|
-
export type AffixProps = React.ComponentProps<"div"> & {
|
|
6
|
-
offsetTop?: number
|
|
7
|
-
zIndex?: number
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function Affix({ offsetTop = 0, zIndex = 30, className, style, ...props }: AffixProps) {
|
|
11
|
-
return (
|
|
12
|
-
<div
|
|
13
|
-
data-slot="affix"
|
|
14
|
-
className={cn("sticky", className)}
|
|
15
|
-
style={{ top: offsetTop, zIndex, ...style }}
|
|
16
|
-
{...props}
|
|
17
|
-
/>
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export { Affix }
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { ComponentDemoMock } from "../types"
|
|
2
|
-
|
|
3
|
-
export const affixMock: ComponentDemoMock = {
|
|
4
|
-
code: `import { Affix } from "@/index"
|
|
5
|
-
|
|
6
|
-
export function Example() {
|
|
7
|
-
return (
|
|
8
|
-
<div className="relative h-48 overflow-y-auto border p-3">
|
|
9
|
-
<Affix offsetTop={12} zIndex={40} className="bg-primary text-primary-foreground px-3 py-2">
|
|
10
|
-
Sticky header
|
|
11
|
-
</Affix>
|
|
12
|
-
<p className="pt-10">Scroll inside container to test sticky placement.</p>
|
|
13
|
-
</div>
|
|
14
|
-
)
|
|
15
|
-
}`,
|
|
16
|
-
cliCommand: "npx tembro add affix",
|
|
17
|
-
highlights: [
|
|
18
|
-
"Pin content to an offset within a scrollable container",
|
|
19
|
-
"Simple API with top and z-index control",
|
|
20
|
-
],
|
|
21
|
-
scenarios: [
|
|
22
|
-
{ title: "Toolbar", description: "Keep action buttons visible while users scroll long forms." },
|
|
23
|
-
{ title: "Quick filters", description: "Keep filters always reachable in long list pages." },
|
|
24
|
-
],
|
|
25
|
-
}
|
|
26
|
-
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import { Affix } from "@/index"
|
|
4
|
-
|
|
5
|
-
export function AffixShowcase() {
|
|
6
|
-
const [offsetTop, setOffsetTop] = React.useState(0)
|
|
7
|
-
const [zIndex, setZIndex] = React.useState(40)
|
|
8
|
-
|
|
9
|
-
return (
|
|
10
|
-
<div className="space-y-4">
|
|
11
|
-
<div className="flex flex-wrap gap-2">
|
|
12
|
-
<button type="button" className="rounded-md border px-2.5 py-1 text-sm" onClick={() => setOffsetTop((value) => Math.max(0, value - 8))}>
|
|
13
|
-
Offset -8
|
|
14
|
-
</button>
|
|
15
|
-
<button type="button" className="rounded-md border px-2.5 py-1 text-sm" onClick={() => setOffsetTop((value) => value + 8)}>
|
|
16
|
-
Offset +8
|
|
17
|
-
</button>
|
|
18
|
-
<button type="button" className="rounded-md border px-2.5 py-1 text-sm" onClick={() => setZIndex((value) => (value === 30 ? 80 : 30))}>
|
|
19
|
-
Toggle z-index
|
|
20
|
-
</button>
|
|
21
|
-
</div>
|
|
22
|
-
<p className="text-sm text-muted-foreground">Current settings: offsetTop={offsetTop}, zIndex={zIndex}</p>
|
|
23
|
-
<div className="relative h-48 overflow-y-auto rounded-lg border bg-muted/20 p-3">
|
|
24
|
-
<div className="h-80 space-y-2">
|
|
25
|
-
<Affix offsetTop={offsetTop} zIndex={zIndex} className="mb-3 bg-primary/95 rounded-md px-3 py-2 text-primary-foreground">
|
|
26
|
-
Sticky anchor bar
|
|
27
|
-
</Affix>
|
|
28
|
-
<p>Scroll this area to verify sticky behavior.</p>
|
|
29
|
-
<p>Affix stays visible at the selected top offset.</p>
|
|
30
|
-
<p>Useful in dashboard toolbars and contextual quick actions.</p>
|
|
31
|
-
<p className="h-64" />
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { ComponentDemoMock } from "../types"
|
|
2
|
-
|
|
3
|
-
export const aspectRatioMock: ComponentDemoMock = {
|
|
4
|
-
code: `import { AspectRatio } from "@/index"
|
|
5
|
-
|
|
6
|
-
export function Example() {
|
|
7
|
-
return (
|
|
8
|
-
<AspectRatio ratio={16 / 9} className="rounded-xl border bg-muted">
|
|
9
|
-
<div className="grid size-full place-items-center text-sm font-medium text-foreground">
|
|
10
|
-
16:9 media slot
|
|
11
|
-
</div>
|
|
12
|
-
</AspectRatio>
|
|
13
|
-
)
|
|
14
|
-
}`,
|
|
15
|
-
cliCommand: "npx tembro add aspect-ratio",
|
|
16
|
-
highlights: [
|
|
17
|
-
"Controls media card proportions in one place",
|
|
18
|
-
"Works for images, iframes, and any fixed-ratio container",
|
|
19
|
-
"Stable in dashboard and content-heavy layouts",
|
|
20
|
-
],
|
|
21
|
-
scenarios: [
|
|
22
|
-
{ title: "Media hero", description: "Reserve consistent 16:9 ratio for previews." },
|
|
23
|
-
{ title: "Card media", description: "Keep media and text alignment predictable." },
|
|
24
|
-
{ title: "Document preview", description: "Prevent jumpy layouts while content loads." },
|
|
25
|
-
],
|
|
26
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
const ratioLabels: Array<{ label: string; ratio: number }> = [
|
|
4
|
-
{ label: "16:9", ratio: 16 / 9 },
|
|
5
|
-
{ label: "4:3", ratio: 4 / 3 },
|
|
6
|
-
{ label: "1:1", ratio: 1 },
|
|
7
|
-
{ label: "3:4", ratio: 3 / 4 },
|
|
8
|
-
]
|
|
9
|
-
|
|
10
|
-
import { Button, AspectRatio } from "@/index"
|
|
11
|
-
export function AspectRatioShowcase() {
|
|
12
|
-
const [active, setActive] = React.useState(0)
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<div className="space-y-5">
|
|
16
|
-
<section className="flex flex-wrap gap-3">
|
|
17
|
-
{ratioLabels.map((item, index) => (
|
|
18
|
-
<Button
|
|
19
|
-
key={item.label}
|
|
20
|
-
variant={active === index ? "default" : "outline"}
|
|
21
|
-
size="sm"
|
|
22
|
-
onClick={() => setActive(index)}
|
|
23
|
-
>
|
|
24
|
-
{item.label}
|
|
25
|
-
</Button>
|
|
26
|
-
))}
|
|
27
|
-
</section>
|
|
28
|
-
|
|
29
|
-
<AspectRatio ratio={ratioLabels[active].ratio} className="rounded-2xl border border-border bg-card">
|
|
30
|
-
<div className="grid h-full place-items-center bg-[linear-gradient(135deg,color-mix(in_oklch,var(--aui-page-bg),transparent_75%),transparent)] p-6 text-center">
|
|
31
|
-
<p className="text-sm text-muted-foreground">{ratioLabels[active].label}</p>
|
|
32
|
-
<p className="text-xl font-semibold text-foreground">Adaptive ratio surface</p>
|
|
33
|
-
</div>
|
|
34
|
-
</AspectRatio>
|
|
35
|
-
</div>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { ComponentDemoMock } from "../types"
|
|
2
|
-
|
|
3
|
-
export const cascaderMock: ComponentDemoMock = {
|
|
4
|
-
code: `import { Cascader } from "@/index"
|
|
5
|
-
|
|
6
|
-
const options = [{ value: "eu", label: "Europe", children: [{ value: "uk", label: "United Kingdom", children: [{ value: "london", label: "London" }] }] }]
|
|
7
|
-
|
|
8
|
-
export function Example() {
|
|
9
|
-
const [value, setValue] = useState<string[]>([])
|
|
10
|
-
|
|
11
|
-
return <Cascader options={options} value={value} onValueChange={setValue} />
|
|
12
|
-
}`,
|
|
13
|
-
cliCommand: "npx tembro add cascader",
|
|
14
|
-
highlights: [
|
|
15
|
-
"Three-level dependency selects",
|
|
16
|
-
"Controlled by string path value",
|
|
17
|
-
"Disabled states for unavailable levels",
|
|
18
|
-
],
|
|
19
|
-
scenarios: [
|
|
20
|
-
{ title: "Location selector", description: "Choose region / country / city in one compact control." },
|
|
21
|
-
{ title: "Category trees", description: "Pick nested taxonomy in forms." },
|
|
22
|
-
],
|
|
23
|
-
}
|
|
24
|
-
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import { Cascader, type CascaderOption } from "@/index"
|
|
4
|
-
|
|
5
|
-
const locations: CascaderOption[] = [
|
|
6
|
-
{ value: "eu", label: "Europe", children: [{ value: "uk", label: "United Kingdom", children: [{ value: "london", label: "London" }] }] },
|
|
7
|
-
{ value: "us", label: "United States", children: [{ value: "ca", label: "California", children: [{ value: "sf", label: "San Francisco" }] }] },
|
|
8
|
-
]
|
|
9
|
-
|
|
10
|
-
export function CascaderShowcase() {
|
|
11
|
-
const [value, setValue] = React.useState<string[]>([])
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<div className="space-y-3">
|
|
15
|
-
<Cascader options={locations} value={value} onValueChange={setValue} />
|
|
16
|
-
<p className="text-sm text-muted-foreground">Selected path: {value.length ? value.join(" / ") : "Not selected"}</p>
|
|
17
|
-
</div>
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { ComponentDemoMock } from "../types"
|
|
2
|
-
|
|
3
|
-
export const floatButtonMock: ComponentDemoMock = {
|
|
4
|
-
code: `import { FloatButton } from "@/index"
|
|
5
|
-
import { Plus } from "lucide-react"
|
|
6
|
-
|
|
7
|
-
export function Example() {
|
|
8
|
-
return (
|
|
9
|
-
<FloatButton placement="bottom-right">
|
|
10
|
-
<Plus className="size-4" />
|
|
11
|
-
</FloatButton>
|
|
12
|
-
)
|
|
13
|
-
}`,
|
|
14
|
-
cliCommand: "npx tembro add float-button",
|
|
15
|
-
highlights: [
|
|
16
|
-
"Fixed-position shortcut button",
|
|
17
|
-
"Placement variants across corners",
|
|
18
|
-
],
|
|
19
|
-
scenarios: [
|
|
20
|
-
{ title: "Quick action", description: "Keep add/create action always reachable." },
|
|
21
|
-
{ title: "Chat widgets", description: "Mount floating controls without complex absolute layout." },
|
|
22
|
-
],
|
|
23
|
-
}
|
|
24
|
-
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import { FloatButton } from "@/index"
|
|
4
|
-
import { PlusIcon } from "lucide-react"
|
|
5
|
-
|
|
6
|
-
const placements = ["bottom-right", "bottom-left", "top-right", "top-left"] as const
|
|
7
|
-
|
|
8
|
-
export function FloatButtonShowcase() {
|
|
9
|
-
const [index, setIndex] = React.useState(0)
|
|
10
|
-
const placement = placements[index % placements.length]
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<div className="relative h-48 rounded-lg border bg-muted/20 p-6">
|
|
14
|
-
<p className="mb-3 text-sm text-muted-foreground">Current placement: {placement}</p>
|
|
15
|
-
<FloatButton onClick={() => setIndex((value) => value + 1)} aria-label="Open quick actions" placement={placement}>
|
|
16
|
-
<PlusIcon className="size-5" />
|
|
17
|
-
</FloatButton>
|
|
18
|
-
</div>
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { ComponentDemoMock } from "../types"
|
|
2
|
-
|
|
3
|
-
export const keyboardShortcutMock: ComponentDemoMock = {
|
|
4
|
-
code: `import { KeyboardShortcut } from "@/index"
|
|
5
|
-
|
|
6
|
-
export function Example() {
|
|
7
|
-
return <KeyboardShortcut keys={["Ctrl", "K"]} />
|
|
8
|
-
}`,
|
|
9
|
-
cliCommand: "npx tembro add keyboard-shortcut",
|
|
10
|
-
highlights: [
|
|
11
|
-
"Compact visual hint for one or many keys",
|
|
12
|
-
"Accepts key arrays or raw children",
|
|
13
|
-
"Useful inside command palette, docs hints and action toolbars",
|
|
14
|
-
],
|
|
15
|
-
scenarios: [
|
|
16
|
-
{ title: "Search hint", description: "Show the command palette trigger beside a search action." },
|
|
17
|
-
{ title: "Editor help", description: "Reveal save or publish shortcuts close to the action." },
|
|
18
|
-
{ title: "Docs legend", description: "Document power-user flows without adding a heavy table." },
|
|
19
|
-
],
|
|
20
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { Button, KeyboardShortcut } from "@/index"
|
|
2
|
-
|
|
3
|
-
const panelClass = "border-t border-[color:var(--aui-divider)] py-6"
|
|
4
|
-
|
|
5
|
-
export function KeyboardShortcutShowcase() {
|
|
6
|
-
return (
|
|
7
|
-
<div className="space-y-0">
|
|
8
|
-
<section className="pb-6">
|
|
9
|
-
<p className="text-xs font-semibold uppercase tracking-[0.24em] aui-text-muted">Installed display primitive</p>
|
|
10
|
-
<h3 className="mt-3 text-2xl font-semibold tracking-tight aui-text-strong">KeyboardShortcut shows power-user hints without noise</h3>
|
|
11
|
-
<p className="mt-3 max-w-2xl text-sm leading-6 aui-text-muted">
|
|
12
|
-
Keep shortcut chips short and nearby to the action they describe. They should reinforce real behavior, not decorative text.
|
|
13
|
-
</p>
|
|
14
|
-
</section>
|
|
15
|
-
|
|
16
|
-
<section className={panelClass}>
|
|
17
|
-
<div className="grid gap-6 xl:grid-cols-[1fr_360px]">
|
|
18
|
-
<div className="rounded-xl border border-[color:var(--aui-divider)] bg-[color:var(--aui-page-bg-alt)] p-4">
|
|
19
|
-
<div className="flex flex-wrap items-center gap-3">
|
|
20
|
-
<Button variant="outline">Search routes</Button>
|
|
21
|
-
<KeyboardShortcut keys={["Ctrl", "K"]} />
|
|
22
|
-
</div>
|
|
23
|
-
<div className="mt-4 flex flex-wrap items-center gap-3">
|
|
24
|
-
<Button variant="secondary">Save draft</Button>
|
|
25
|
-
<KeyboardShortcut keys={["Ctrl", "S"]} />
|
|
26
|
-
<Button>Publish</Button>
|
|
27
|
-
<KeyboardShortcut keys={["Shift", "Enter"]} />
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
<div className="rounded-xl border border-[color:var(--aui-divider)] p-4">
|
|
32
|
-
<p className="text-sm font-semibold aui-text-strong">Guidance</p>
|
|
33
|
-
<div className="mt-3 space-y-3 text-sm leading-6 aui-text-muted">
|
|
34
|
-
<p>Show shortcuts only where the action is relevant. Repeating the same hint everywhere weakens it.</p>
|
|
35
|
-
<p>Use concise labels like `Ctrl`, `Cmd`, `Shift`, `Enter`, not long prose inside the chip.</p>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
</div>
|
|
39
|
-
</section>
|
|
40
|
-
</div>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { ComponentDemoMock } from "../types"
|
|
2
|
-
|
|
3
|
-
export const mentionInputMock: ComponentDemoMock = {
|
|
4
|
-
code: `import { MentionInput } from "@/index"
|
|
5
|
-
|
|
6
|
-
export function Example() {
|
|
7
|
-
return (
|
|
8
|
-
<MentionInput
|
|
9
|
-
value="Assign @alice and @bob."
|
|
10
|
-
options={[{ label: "Alice", value: "alice" }, { label: "Bob", value: "bob" }]}
|
|
11
|
-
onValueChange={(value) => console.log(value)}
|
|
12
|
-
/>
|
|
13
|
-
)
|
|
14
|
-
}`,
|
|
15
|
-
cliCommand: "npx tembro add mention-input",
|
|
16
|
-
highlights: [
|
|
17
|
-
"Trigger-based suggestion filtering",
|
|
18
|
-
"Keyboard navigation for suggestions",
|
|
19
|
-
"Auto insert with controlled value",
|
|
20
|
-
],
|
|
21
|
-
scenarios: [
|
|
22
|
-
{ title: "Comments", description: "Mention teammates in internal notes." },
|
|
23
|
-
{ title: "Chat input", description: "Add quick user references while composing." },
|
|
24
|
-
],
|
|
25
|
-
}
|
|
26
|
-
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import { MentionInput } from "@/index"
|
|
4
|
-
|
|
5
|
-
const defaultOptions = [
|
|
6
|
-
{ label: "Alice", value: "alice" },
|
|
7
|
-
{ label: "Bob", value: "bob" },
|
|
8
|
-
{ label: "Charlie", value: "charlie" },
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
export function MentionInputShowcase() {
|
|
12
|
-
const [value, setValue] = React.useState("Hi @alice, please review this task.")
|
|
13
|
-
const [mentions, setMentions] = React.useState<string[]>([])
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<div className="space-y-3">
|
|
17
|
-
<MentionInput
|
|
18
|
-
value={value}
|
|
19
|
-
options={defaultOptions}
|
|
20
|
-
onValueChange={setValue}
|
|
21
|
-
onMentionSelect={(selected) => setMentions((current) => [...new Set([...current, selected])])}
|
|
22
|
-
/>
|
|
23
|
-
<p className="text-sm text-muted-foreground">Inserted mentions: {mentions.join(", ") || "None"}</p>
|
|
24
|
-
</div>
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
|