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.
Files changed (239) hide show
  1. package/dist/components/display/index.cjs +1 -1
  2. package/dist/components/display/index.d.ts +0 -5
  3. package/dist/components/display/index.js +22 -27
  4. package/dist/components/form/demo.cjs +1 -1
  5. package/dist/components/form/demo.js +1 -2
  6. package/dist/components/form/index.cjs +1 -1
  7. package/dist/components/form/index.d.ts +0 -1
  8. package/dist/components/form/index.js +8 -9
  9. package/dist/components/form/public.cjs +1 -1
  10. package/dist/components/form/public.d.ts +0 -1
  11. package/dist/components/form/public.js +8 -9
  12. package/dist/components/inputs/index.cjs +1 -1
  13. package/dist/components/inputs/index.d.ts +0 -2
  14. package/dist/components/inputs/index.js +3 -5
  15. package/dist/components/inputs/public.cjs +1 -1
  16. package/dist/components/inputs/public.d.ts +0 -2
  17. package/dist/components/inputs/public.js +3 -5
  18. package/dist/components/layout/index.cjs +1 -1
  19. package/dist/components/layout/index.d.ts +0 -1
  20. package/dist/components/layout/index.js +4 -5
  21. package/dist/components/layout/public.cjs +1 -1
  22. package/dist/components/layout/public.d.ts +0 -1
  23. package/dist/components/layout/public.js +2 -3
  24. package/dist/components/modern/index.cjs +1 -1
  25. package/dist/components/modern/index.d.ts +0 -4
  26. package/dist/components/modern/index.js +12 -16
  27. package/dist/index.cjs +1 -1
  28. package/dist/index.js +117 -130
  29. package/dist/public-component-surface.cjs +1 -1
  30. package/dist/public-component-surface.js +0 -65
  31. package/dist/showcase/fallback.cjs +3 -3
  32. package/dist/showcase/fallback.js +140 -314
  33. package/dist/showcase/premium/index.cjs +1 -1
  34. package/dist/showcase/premium/index.js +431 -497
  35. package/dist/showcase/registry-quality.cjs +1 -1
  36. package/dist/showcase/registry-quality.js +0 -1
  37. package/dist/showcase/tembro-registry.json.cjs +1 -1
  38. package/dist/showcase/tembro-registry.json.js +3 -11
  39. package/package.json +1 -1
  40. package/packages/cli/dist/index.cjs +2 -2
  41. package/packages/cli/vendor/src/components/display/index.ts +0 -5
  42. package/packages/cli/vendor/src/components/form/demo.tsx +0 -1
  43. package/packages/cli/vendor/src/components/form/index.ts +0 -1
  44. package/packages/cli/vendor/src/components/form/public.ts +0 -2
  45. package/packages/cli/vendor/src/components/inputs/index.ts +0 -2
  46. package/packages/cli/vendor/src/components/inputs/public.ts +0 -2
  47. package/packages/cli/vendor/src/components/layout/index.ts +0 -1
  48. package/packages/cli/vendor/src/components/layout/public.ts +0 -1
  49. package/packages/cli/vendor/src/components/modern/index.ts +1 -5
  50. package/packages/cli/vendor/src/public-component-surface.ts +0 -13
  51. package/packages/cli/vendor/src/showcase/fallback.tsx +3 -164
  52. package/packages/cli/vendor/src/showcase/premium/index.ts +0 -22
  53. package/packages/cli/vendor/src/showcase/registry-quality.ts +0 -2
  54. package/packages/cli/vendor/src/showcase/tembro-registry.json +224 -251
  55. package/registry.json +296 -324
  56. package/dist/components/display/aspect-ratio.cjs +0 -1
  57. package/dist/components/display/aspect-ratio.d.ts +0 -6
  58. package/dist/components/display/aspect-ratio.js +0 -21
  59. package/dist/components/display/float-button.cjs +0 -1
  60. package/dist/components/display/float-button.d.ts +0 -6
  61. package/dist/components/display/float-button.js +0 -20
  62. package/dist/components/display/key-value-card.cjs +0 -1
  63. package/dist/components/display/key-value-card.d.ts +0 -16
  64. package/dist/components/display/key-value-card.js +0 -35
  65. package/dist/components/display/keyboard-shortcut.cjs +0 -1
  66. package/dist/components/display/keyboard-shortcut.d.ts +0 -7
  67. package/dist/components/display/keyboard-shortcut.js +0 -18
  68. package/dist/components/display/skeleton-form.cjs +0 -1
  69. package/dist/components/display/skeleton-form.d.ts +0 -9
  70. package/dist/components/display/skeleton-form.js +0 -31
  71. package/dist/components/display/skeleton-table.cjs +0 -1
  72. package/dist/components/display/skeleton-table.d.ts +0 -9
  73. package/dist/components/display/skeleton-table.js +0 -42
  74. package/dist/components/display/tag-list.cjs +0 -1
  75. package/dist/components/display/tag-list.d.ts +0 -17
  76. package/dist/components/display/tag-list.js +0 -30
  77. package/dist/components/display/watermark.cjs +0 -1
  78. package/dist/components/display/watermark.d.ts +0 -6
  79. package/dist/components/display/watermark.js +0 -23
  80. package/dist/components/form/repeater-field.cjs +0 -1
  81. package/dist/components/form/repeater-field.d.ts +0 -16
  82. package/dist/components/form/repeater-field.js +0 -48
  83. package/dist/components/inputs/cascader.cjs +0 -1
  84. package/dist/components/inputs/cascader.d.ts +0 -13
  85. package/dist/components/inputs/cascader.js +0 -58
  86. package/dist/components/inputs/mention-input.cjs +0 -1
  87. package/dist/components/inputs/mention-input.d.ts +0 -18
  88. package/dist/components/inputs/mention-input.js +0 -96
  89. package/dist/components/layout/stack.cjs +0 -1
  90. package/dist/components/layout/stack.d.ts +0 -23
  91. package/dist/components/layout/stack.js +0 -57
  92. package/dist/components/modern/affix.cjs +0 -1
  93. package/dist/components/modern/affix.d.ts +0 -7
  94. package/dist/components/modern/affix.js +0 -18
  95. package/dist/showcase/premium/affix/index.cjs +0 -1
  96. package/dist/showcase/premium/affix/index.d.ts +0 -2
  97. package/dist/showcase/premium/affix/index.js +0 -3
  98. package/dist/showcase/premium/affix/mock.cjs +0 -12
  99. package/dist/showcase/premium/affix/mock.d.ts +0 -2
  100. package/dist/showcase/premium/affix/mock.js +0 -15
  101. package/dist/showcase/premium/affix/showcase.cjs +0 -1
  102. package/dist/showcase/premium/affix/showcase.d.ts +0 -2
  103. package/dist/showcase/premium/affix/showcase.js +0 -64
  104. package/dist/showcase/premium/aspect-ratio/index.cjs +0 -1
  105. package/dist/showcase/premium/aspect-ratio/index.d.ts +0 -2
  106. package/dist/showcase/premium/aspect-ratio/index.js +0 -3
  107. package/dist/showcase/premium/aspect-ratio/mock.cjs +0 -11
  108. package/dist/showcase/premium/aspect-ratio/mock.d.ts +0 -2
  109. package/dist/showcase/premium/aspect-ratio/mock.js +0 -26
  110. package/dist/showcase/premium/aspect-ratio/showcase.cjs +0 -1
  111. package/dist/showcase/premium/aspect-ratio/showcase.d.ts +0 -2
  112. package/dist/showcase/premium/aspect-ratio/showcase.js +0 -53
  113. package/dist/showcase/premium/cascader/index.cjs +0 -1
  114. package/dist/showcase/premium/cascader/index.d.ts +0 -2
  115. package/dist/showcase/premium/cascader/index.js +0 -3
  116. package/dist/showcase/premium/cascader/mock.cjs +0 -9
  117. package/dist/showcase/premium/cascader/mock.d.ts +0 -2
  118. package/dist/showcase/premium/cascader/mock.js +0 -19
  119. package/dist/showcase/premium/cascader/showcase.cjs +0 -1
  120. package/dist/showcase/premium/cascader/showcase.d.ts +0 -2
  121. package/dist/showcase/premium/cascader/showcase.js +0 -43
  122. package/dist/showcase/premium/float-button/index.cjs +0 -1
  123. package/dist/showcase/premium/float-button/index.d.ts +0 -2
  124. package/dist/showcase/premium/float-button/index.js +0 -3
  125. package/dist/showcase/premium/float-button/mock.cjs +0 -10
  126. package/dist/showcase/premium/float-button/mock.d.ts +0 -2
  127. package/dist/showcase/premium/float-button/mock.js +0 -15
  128. package/dist/showcase/premium/float-button/showcase.cjs +0 -1
  129. package/dist/showcase/premium/float-button/showcase.d.ts +0 -2
  130. package/dist/showcase/premium/float-button/showcase.js +0 -28
  131. package/dist/showcase/premium/keyboard-shortcut/index.cjs +0 -1
  132. package/dist/showcase/premium/keyboard-shortcut/index.d.ts +0 -2
  133. package/dist/showcase/premium/keyboard-shortcut/index.js +0 -3
  134. package/dist/showcase/premium/keyboard-shortcut/mock.cjs +0 -5
  135. package/dist/showcase/premium/keyboard-shortcut/mock.d.ts +0 -2
  136. package/dist/showcase/premium/keyboard-shortcut/mock.js +0 -26
  137. package/dist/showcase/premium/keyboard-shortcut/showcase.cjs +0 -1
  138. package/dist/showcase/premium/keyboard-shortcut/showcase.d.ts +0 -1
  139. package/dist/showcase/premium/keyboard-shortcut/showcase.js +0 -64
  140. package/dist/showcase/premium/mention-input/index.cjs +0 -1
  141. package/dist/showcase/premium/mention-input/index.d.ts +0 -2
  142. package/dist/showcase/premium/mention-input/index.js +0 -3
  143. package/dist/showcase/premium/mention-input/mock.cjs +0 -11
  144. package/dist/showcase/premium/mention-input/mock.d.ts +0 -2
  145. package/dist/showcase/premium/mention-input/mock.js +0 -19
  146. package/dist/showcase/premium/mention-input/showcase.cjs +0 -1
  147. package/dist/showcase/premium/mention-input/showcase.d.ts +0 -2
  148. package/dist/showcase/premium/mention-input/showcase.js +0 -35
  149. package/dist/showcase/premium/skeleton-form/index.cjs +0 -1
  150. package/dist/showcase/premium/skeleton-form/index.d.ts +0 -2
  151. package/dist/showcase/premium/skeleton-form/index.js +0 -3
  152. package/dist/showcase/premium/skeleton-form/mock.cjs +0 -5
  153. package/dist/showcase/premium/skeleton-form/mock.d.ts +0 -2
  154. package/dist/showcase/premium/skeleton-form/mock.js +0 -19
  155. package/dist/showcase/premium/skeleton-form/showcase.cjs +0 -1
  156. package/dist/showcase/premium/skeleton-form/showcase.d.ts +0 -2
  157. package/dist/showcase/premium/skeleton-form/showcase.js +0 -52
  158. package/dist/showcase/premium/skeleton-table/index.cjs +0 -1
  159. package/dist/showcase/premium/skeleton-table/index.d.ts +0 -2
  160. package/dist/showcase/premium/skeleton-table/index.js +0 -3
  161. package/dist/showcase/premium/skeleton-table/mock.cjs +0 -5
  162. package/dist/showcase/premium/skeleton-table/mock.d.ts +0 -2
  163. package/dist/showcase/premium/skeleton-table/mock.js +0 -19
  164. package/dist/showcase/premium/skeleton-table/showcase.cjs +0 -1
  165. package/dist/showcase/premium/skeleton-table/showcase.d.ts +0 -2
  166. package/dist/showcase/premium/skeleton-table/showcase.js +0 -59
  167. package/dist/showcase/premium/stack/index.cjs +0 -1
  168. package/dist/showcase/premium/stack/index.d.ts +0 -2
  169. package/dist/showcase/premium/stack/index.js +0 -3
  170. package/dist/showcase/premium/stack/mock.cjs +0 -10
  171. package/dist/showcase/premium/stack/mock.d.ts +0 -2
  172. package/dist/showcase/premium/stack/mock.js +0 -26
  173. package/dist/showcase/premium/stack/showcase.cjs +0 -1
  174. package/dist/showcase/premium/stack/showcase.d.ts +0 -1
  175. package/dist/showcase/premium/stack/showcase.js +0 -47
  176. package/dist/showcase/premium/tag-list/index.cjs +0 -1
  177. package/dist/showcase/premium/tag-list/index.d.ts +0 -2
  178. package/dist/showcase/premium/tag-list/index.js +0 -3
  179. package/dist/showcase/premium/tag-list/mock.cjs +0 -12
  180. package/dist/showcase/premium/tag-list/mock.d.ts +0 -2
  181. package/dist/showcase/premium/tag-list/mock.js +0 -26
  182. package/dist/showcase/premium/tag-list/showcase.cjs +0 -1
  183. package/dist/showcase/premium/tag-list/showcase.d.ts +0 -2
  184. package/dist/showcase/premium/tag-list/showcase.js +0 -83
  185. package/dist/showcase/premium/watermark/index.cjs +0 -1
  186. package/dist/showcase/premium/watermark/index.d.ts +0 -2
  187. package/dist/showcase/premium/watermark/index.js +0 -3
  188. package/dist/showcase/premium/watermark/mock.cjs +0 -11
  189. package/dist/showcase/premium/watermark/mock.d.ts +0 -2
  190. package/dist/showcase/premium/watermark/mock.js +0 -26
  191. package/dist/showcase/premium/watermark/showcase.cjs +0 -1
  192. package/dist/showcase/premium/watermark/showcase.d.ts +0 -2
  193. package/dist/showcase/premium/watermark/showcase.js +0 -34
  194. package/packages/cli/vendor/src/components/display/aspect-ratio.tsx +0 -22
  195. package/packages/cli/vendor/src/components/display/float-button.tsx +0 -27
  196. package/packages/cli/vendor/src/components/display/key-value-card.tsx +0 -50
  197. package/packages/cli/vendor/src/components/display/keyboard-shortcut.tsx +0 -31
  198. package/packages/cli/vendor/src/components/display/skeleton-form.tsx +0 -51
  199. package/packages/cli/vendor/src/components/display/skeleton-table.tsx +0 -59
  200. package/packages/cli/vendor/src/components/display/tag-list.tsx +0 -52
  201. package/packages/cli/vendor/src/components/display/watermark.tsx +0 -20
  202. package/packages/cli/vendor/src/components/form/repeater-field.tsx +0 -93
  203. package/packages/cli/vendor/src/components/inputs/cascader.tsx +0 -41
  204. package/packages/cli/vendor/src/components/inputs/mention-input.tsx +0 -162
  205. package/packages/cli/vendor/src/components/layout/stack.tsx +0 -106
  206. package/packages/cli/vendor/src/components/modern/affix.tsx +0 -21
  207. package/packages/cli/vendor/src/showcase/premium/affix/index.ts +0 -3
  208. package/packages/cli/vendor/src/showcase/premium/affix/mock.ts +0 -26
  209. package/packages/cli/vendor/src/showcase/premium/affix/showcase.tsx +0 -37
  210. package/packages/cli/vendor/src/showcase/premium/aspect-ratio/index.ts +0 -2
  211. package/packages/cli/vendor/src/showcase/premium/aspect-ratio/mock.ts +0 -26
  212. package/packages/cli/vendor/src/showcase/premium/aspect-ratio/showcase.tsx +0 -37
  213. package/packages/cli/vendor/src/showcase/premium/cascader/index.ts +0 -3
  214. package/packages/cli/vendor/src/showcase/premium/cascader/mock.ts +0 -24
  215. package/packages/cli/vendor/src/showcase/premium/cascader/showcase.tsx +0 -20
  216. package/packages/cli/vendor/src/showcase/premium/float-button/index.ts +0 -3
  217. package/packages/cli/vendor/src/showcase/premium/float-button/mock.ts +0 -24
  218. package/packages/cli/vendor/src/showcase/premium/float-button/showcase.tsx +0 -21
  219. package/packages/cli/vendor/src/showcase/premium/keyboard-shortcut/index.ts +0 -2
  220. package/packages/cli/vendor/src/showcase/premium/keyboard-shortcut/mock.ts +0 -20
  221. package/packages/cli/vendor/src/showcase/premium/keyboard-shortcut/showcase.tsx +0 -42
  222. package/packages/cli/vendor/src/showcase/premium/mention-input/index.ts +0 -3
  223. package/packages/cli/vendor/src/showcase/premium/mention-input/mock.ts +0 -26
  224. package/packages/cli/vendor/src/showcase/premium/mention-input/showcase.tsx +0 -27
  225. package/packages/cli/vendor/src/showcase/premium/skeleton-form/index.ts +0 -3
  226. package/packages/cli/vendor/src/showcase/premium/skeleton-form/mock.ts +0 -20
  227. package/packages/cli/vendor/src/showcase/premium/skeleton-form/showcase.tsx +0 -34
  228. package/packages/cli/vendor/src/showcase/premium/skeleton-table/index.ts +0 -3
  229. package/packages/cli/vendor/src/showcase/premium/skeleton-table/mock.ts +0 -20
  230. package/packages/cli/vendor/src/showcase/premium/skeleton-table/showcase.tsx +0 -37
  231. package/packages/cli/vendor/src/showcase/premium/stack/index.ts +0 -2
  232. package/packages/cli/vendor/src/showcase/premium/stack/mock.ts +0 -25
  233. package/packages/cli/vendor/src/showcase/premium/stack/showcase.tsx +0 -22
  234. package/packages/cli/vendor/src/showcase/premium/tag-list/index.ts +0 -2
  235. package/packages/cli/vendor/src/showcase/premium/tag-list/mock.ts +0 -27
  236. package/packages/cli/vendor/src/showcase/premium/tag-list/showcase.tsx +0 -55
  237. package/packages/cli/vendor/src/showcase/premium/watermark/index.ts +0 -2
  238. package/packages/cli/vendor/src/showcase/premium/watermark/mock.ts +0 -26
  239. 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,3 +0,0 @@
1
- export { affixMock } from "./mock"
2
- export { AffixShowcase } from "./showcase"
3
-
@@ -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,2 +0,0 @@
1
- export { aspectRatioMock } from "./mock"
2
- export { AspectRatioShowcase } from "./showcase"
@@ -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,3 +0,0 @@
1
- export { cascaderMock } from "./mock"
2
- export { CascaderShowcase } from "./showcase"
3
-
@@ -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,3 +0,0 @@
1
- export { floatButtonMock } from "./mock"
2
- export { FloatButtonShowcase } from "./showcase"
3
-
@@ -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,2 +0,0 @@
1
- export { keyboardShortcutMock } from "./mock"
2
- export { KeyboardShortcutShowcase } from "./showcase"
@@ -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,3 +0,0 @@
1
- export { mentionInputMock } from "./mock"
2
- export { MentionInputShowcase } from "./showcase"
3
-
@@ -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
-
@@ -1,3 +0,0 @@
1
- export { skeletonFormMock } from "./mock"
2
- export { SkeletonFormShowcase } from "./showcase"
3
-