@boyernick/standard-ui-react 0.1.1-canary.9 → 0.2.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.
- package/package.json +5 -3
- package/src/accordion.tsx +5 -2
- package/src/alert-dialog.tsx +1 -1
- package/src/attachment.tsx +5 -2
- package/src/autocomplete.tsx +4 -3
- package/src/block-editor.tsx +1747 -0
- package/src/brand.tsx +2 -2
- package/src/breadcrumb.tsx +14 -7
- package/src/button.tsx +22 -11
- package/src/calendar.tsx +6 -3
- package/src/carousel.tsx +220 -53
- package/src/checkbox.tsx +3 -2
- package/src/code-block.tsx +66 -5
- package/src/collapsible.tsx +4 -2
- package/src/combobox.tsx +4 -3
- package/src/command.tsx +27 -12
- package/src/dialog.tsx +33 -10
- package/src/empty.tsx +4 -4
- package/src/field.tsx +4 -3
- package/src/filter-group.tsx +143 -0
- package/src/icons.tsx +28 -1
- package/src/illustrations.tsx +219 -141
- package/src/index.ts +238 -18
- package/src/input.tsx +8 -5
- package/src/kbd.tsx +10 -4
- package/src/lib/focus.ts +41 -0
- package/src/lib/popup.ts +1 -1
- package/src/lifeline/company-icon.tsx +71 -0
- package/src/lifeline/index.ts +42 -0
- package/src/lifeline/lifeline-data.ts +97 -0
- package/src/lifeline/lifeline-desktop.tsx +204 -0
- package/src/lifeline/lifeline-event.tsx +108 -0
- package/src/lifeline/lifeline-fireworks.tsx +302 -0
- package/src/lifeline/lifeline-hover-image.tsx +269 -0
- package/src/lifeline/lifeline-icons.tsx +39 -0
- package/src/lifeline/lifeline-intro-timing.ts +105 -0
- package/src/lifeline/lifeline-labels.tsx +24 -0
- package/src/lifeline/lifeline-layout.ts +1 -0
- package/src/lifeline/lifeline-legend.tsx +31 -0
- package/src/lifeline/lifeline-lightbox.tsx +309 -0
- package/src/lifeline/lifeline-marker.tsx +183 -0
- package/src/lifeline/lifeline-people.tsx +99 -0
- package/src/lifeline/lifeline-photos.tsx +366 -0
- package/src/lifeline/lifeline-shell.tsx +135 -0
- package/src/lifeline/lifeline-utils.ts +83 -0
- package/src/lifeline/lifeline-vertical.tsx +482 -0
- package/src/lifeline/lifeline.tsx +69 -0
- package/src/lifeline/types.ts +112 -0
- package/src/lifeline/use-lifeline-intro.ts +130 -0
- package/src/lifeline/use-lifeline-scroll.ts +1011 -0
- package/src/lifeline/use-lifeline-vertical-scroll.ts +271 -0
- package/src/menubar.tsx +9 -2
- package/src/minimap.tsx +296 -0
- package/src/modal.tsx +608 -0
- package/src/navigation-menu.tsx +338 -67
- package/src/number-field.tsx +336 -61
- package/src/otp-field.tsx +4 -3
- package/src/pagination.tsx +262 -42
- package/src/password-protection.tsx +345 -0
- package/src/popover.tsx +1 -1
- package/src/progress.tsx +5 -0
- package/src/questionnaire.tsx +284 -0
- package/src/radio.tsx +4 -2
- package/src/scroll-area.tsx +4 -1
- package/src/select.tsx +5 -2
- package/src/sidebar.tsx +113 -28
- package/src/slider.tsx +66 -6
- package/src/sounds.tsx +6 -10
- package/src/spinner.tsx +105 -32
- package/src/switch.tsx +4 -1
- package/src/table.tsx +82 -15
- package/src/tabs.tsx +189 -38
- package/src/text-animate.tsx +71 -28
- package/src/textarea.tsx +6 -1
- package/src/timeline.tsx +378 -0
- package/src/toast.tsx +214 -35
- package/src/toggle.tsx +4 -2
- package/src/toolbar.tsx +12 -7
- package/src/tooltip.tsx +43 -3
- package/src/video-player.tsx +396 -127
- package/src/image-modal.tsx +0 -110
- package/src/markdown-editor.tsx +0 -302
package/src/image-modal.tsx
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import type { ComponentProps, ImgHTMLAttributes } from "react"
|
|
4
|
-
import { Button } from "./button"
|
|
5
|
-
import {
|
|
6
|
-
Dialog,
|
|
7
|
-
DialogBackdrop,
|
|
8
|
-
DialogClose,
|
|
9
|
-
DialogPopup,
|
|
10
|
-
DialogPortal,
|
|
11
|
-
DialogTitle,
|
|
12
|
-
DialogTrigger,
|
|
13
|
-
} from "./dialog"
|
|
14
|
-
import { cn } from "./lib/cn"
|
|
15
|
-
import { motion } from "./lib/motion"
|
|
16
|
-
|
|
17
|
-
export type ImageModalProps = ComponentProps<typeof Dialog>
|
|
18
|
-
export type ImageModalTriggerProps = ComponentProps<typeof DialogTrigger>
|
|
19
|
-
export type ImageModalContentProps = ComponentProps<typeof DialogPopup> & {
|
|
20
|
-
src: string
|
|
21
|
-
alt: string
|
|
22
|
-
caption?: string
|
|
23
|
-
imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "alt">
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const ImageModal = (props: ImageModalProps) => <Dialog {...props} />
|
|
27
|
-
|
|
28
|
-
export const ImageModalTrigger = ({
|
|
29
|
-
className,
|
|
30
|
-
...props
|
|
31
|
-
}: ImageModalTriggerProps) => (
|
|
32
|
-
<DialogTrigger
|
|
33
|
-
className={cn(
|
|
34
|
-
"cursor-pointer rounded-xl outline-none focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20",
|
|
35
|
-
className,
|
|
36
|
-
)}
|
|
37
|
-
{...props}
|
|
38
|
-
/>
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
export const ImageModalContent = ({
|
|
42
|
-
src,
|
|
43
|
-
alt,
|
|
44
|
-
caption,
|
|
45
|
-
className,
|
|
46
|
-
imgProps,
|
|
47
|
-
children,
|
|
48
|
-
...props
|
|
49
|
-
}: ImageModalContentProps) => (
|
|
50
|
-
<DialogPortal>
|
|
51
|
-
<DialogBackdrop className="bg-black/70 dark:bg-black/80" />
|
|
52
|
-
<DialogPopup
|
|
53
|
-
data-slot="image-modal-content"
|
|
54
|
-
className={cn(
|
|
55
|
-
"max-h-[90vh] w-full max-w-4xl gap-0 overflow-hidden border-border-primary bg-surface p-0 shadow-lg max-sm:max-w-[calc(100vw-1.5rem)]",
|
|
56
|
-
motion.popupCenter,
|
|
57
|
-
className,
|
|
58
|
-
)}
|
|
59
|
-
{...props}
|
|
60
|
-
>
|
|
61
|
-
<div className="relative">
|
|
62
|
-
<DialogTitle className="sr-only">{alt}</DialogTitle>
|
|
63
|
-
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
64
|
-
<img
|
|
65
|
-
src={src}
|
|
66
|
-
alt={alt}
|
|
67
|
-
className={cn(
|
|
68
|
-
"max-h-[min(80vh,56rem)] w-full object-contain bg-background-secondary",
|
|
69
|
-
imgProps?.className,
|
|
70
|
-
)}
|
|
71
|
-
{...imgProps}
|
|
72
|
-
/>
|
|
73
|
-
<DialogClose
|
|
74
|
-
render={
|
|
75
|
-
<Button
|
|
76
|
-
type="button"
|
|
77
|
-
variant="secondary"
|
|
78
|
-
size="sm"
|
|
79
|
-
iconOnly
|
|
80
|
-
rounded
|
|
81
|
-
aria-label="Close"
|
|
82
|
-
className="absolute top-3 right-3"
|
|
83
|
-
/>
|
|
84
|
-
}
|
|
85
|
-
>
|
|
86
|
-
<CloseIcon />
|
|
87
|
-
</DialogClose>
|
|
88
|
-
</div>
|
|
89
|
-
{caption || children ? (
|
|
90
|
-
<div className="border-t border-border-primary px-4 py-3">
|
|
91
|
-
{caption ? (
|
|
92
|
-
<p className="text-sm text-fg-secondary">{caption}</p>
|
|
93
|
-
) : null}
|
|
94
|
-
{children}
|
|
95
|
-
</div>
|
|
96
|
-
) : null}
|
|
97
|
-
</DialogPopup>
|
|
98
|
-
</DialogPortal>
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
const CloseIcon = () => (
|
|
102
|
-
<svg viewBox="0 0 24 24" fill="none" className="size-4" aria-hidden>
|
|
103
|
-
<path
|
|
104
|
-
d="M7 7l10 10M17 7 7 17"
|
|
105
|
-
stroke="currentColor"
|
|
106
|
-
strokeWidth="2"
|
|
107
|
-
strokeLinecap="round"
|
|
108
|
-
/>
|
|
109
|
-
</svg>
|
|
110
|
-
)
|
package/src/markdown-editor.tsx
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
createContext,
|
|
5
|
-
forwardRef,
|
|
6
|
-
useContext,
|
|
7
|
-
useRef,
|
|
8
|
-
useState,
|
|
9
|
-
type ChangeEvent,
|
|
10
|
-
type ComponentPropsWithoutRef,
|
|
11
|
-
type HTMLAttributes,
|
|
12
|
-
type ReactNode,
|
|
13
|
-
type Ref,
|
|
14
|
-
type TextareaHTMLAttributes,
|
|
15
|
-
} from "react"
|
|
16
|
-
import { cn } from "./lib/cn"
|
|
17
|
-
import { textareaVariants } from "./textarea"
|
|
18
|
-
import { Toggle } from "./toggle"
|
|
19
|
-
import { Toolbar } from "./toolbar"
|
|
20
|
-
|
|
21
|
-
type MarkdownEditorContextValue = {
|
|
22
|
-
value: string
|
|
23
|
-
setValue: (value: string) => void
|
|
24
|
-
inputRef: React.RefObject<HTMLTextAreaElement | null>
|
|
25
|
-
selection: { start: number; end: number }
|
|
26
|
-
setSelection: (selection: { start: number; end: number }) => void
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const MarkdownEditorContext = createContext<MarkdownEditorContextValue | null>(
|
|
30
|
-
null,
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
const useMarkdownEditor = () => {
|
|
34
|
-
const context = useContext(MarkdownEditorContext)
|
|
35
|
-
|
|
36
|
-
if (!context) {
|
|
37
|
-
throw new Error(
|
|
38
|
-
"MarkdownEditor parts must be rendered inside MarkdownEditor",
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return context
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const assignRef = <Value,>(ref: Ref<Value> | undefined, value: Value) => {
|
|
46
|
-
if (typeof ref === "function") {
|
|
47
|
-
ref(value)
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (ref) ref.current = value
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export type MarkdownEditorProps = Omit<
|
|
55
|
-
HTMLAttributes<HTMLDivElement>,
|
|
56
|
-
"onChange"
|
|
57
|
-
> & {
|
|
58
|
-
value?: string
|
|
59
|
-
defaultValue?: string
|
|
60
|
-
onValueChange?: (value: string) => void
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export const MarkdownEditor = ({
|
|
64
|
-
value: controlledValue,
|
|
65
|
-
defaultValue = "",
|
|
66
|
-
onValueChange,
|
|
67
|
-
className,
|
|
68
|
-
children,
|
|
69
|
-
...props
|
|
70
|
-
}: MarkdownEditorProps) => {
|
|
71
|
-
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue)
|
|
72
|
-
const [selection, setSelection] = useState({ start: 0, end: 0 })
|
|
73
|
-
const inputRef = useRef<HTMLTextAreaElement>(null)
|
|
74
|
-
const value = controlledValue ?? uncontrolledValue
|
|
75
|
-
|
|
76
|
-
const setValue = (nextValue: string) => {
|
|
77
|
-
if (controlledValue === undefined) setUncontrolledValue(nextValue)
|
|
78
|
-
onValueChange?.(nextValue)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<MarkdownEditorContext.Provider
|
|
83
|
-
value={{
|
|
84
|
-
value,
|
|
85
|
-
setValue,
|
|
86
|
-
inputRef,
|
|
87
|
-
selection,
|
|
88
|
-
setSelection,
|
|
89
|
-
}}
|
|
90
|
-
>
|
|
91
|
-
<div
|
|
92
|
-
className={cn(
|
|
93
|
-
"overflow-hidden rounded-xl border border-border-primary bg-surface",
|
|
94
|
-
className,
|
|
95
|
-
)}
|
|
96
|
-
{...props}
|
|
97
|
-
>
|
|
98
|
-
{children}
|
|
99
|
-
</div>
|
|
100
|
-
</MarkdownEditorContext.Provider>
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
type Format = {
|
|
105
|
-
label: string
|
|
106
|
-
marker: "*" | "**" | "`"
|
|
107
|
-
className?: string
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const formats: Format[] = [
|
|
111
|
-
{ label: "Bold", marker: "**", className: "font-medium" },
|
|
112
|
-
{ label: "Italic", marker: "*", className: "italic" },
|
|
113
|
-
{ label: "Code", marker: "`", className: "font-mono" },
|
|
114
|
-
]
|
|
115
|
-
|
|
116
|
-
export type MarkdownEditorToolbarProps = ComponentPropsWithoutRef<
|
|
117
|
-
typeof Toolbar
|
|
118
|
-
>
|
|
119
|
-
|
|
120
|
-
export const MarkdownEditorToolbar = ({
|
|
121
|
-
className,
|
|
122
|
-
children,
|
|
123
|
-
...props
|
|
124
|
-
}: MarkdownEditorToolbarProps) => {
|
|
125
|
-
const { value, setValue, inputRef, selection, setSelection } =
|
|
126
|
-
useMarkdownEditor()
|
|
127
|
-
|
|
128
|
-
const isFormatActive = (marker: Format["marker"]) => {
|
|
129
|
-
const { start, end } = selection
|
|
130
|
-
return (
|
|
131
|
-
value.slice(start - marker.length, start) === marker &&
|
|
132
|
-
value.slice(end, end + marker.length) === marker
|
|
133
|
-
)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const handleFormat = (marker: Format["marker"]) => {
|
|
137
|
-
const input = inputRef.current
|
|
138
|
-
|
|
139
|
-
if (!input) return
|
|
140
|
-
|
|
141
|
-
const start = input.selectionStart
|
|
142
|
-
const end = input.selectionEnd
|
|
143
|
-
const selectedText = value.slice(start, end)
|
|
144
|
-
const isWrapped =
|
|
145
|
-
value.slice(start - marker.length, start) === marker &&
|
|
146
|
-
value.slice(end, end + marker.length) === marker
|
|
147
|
-
|
|
148
|
-
if (isWrapped) {
|
|
149
|
-
const nextValue =
|
|
150
|
-
value.slice(0, start - marker.length) +
|
|
151
|
-
selectedText +
|
|
152
|
-
value.slice(end + marker.length)
|
|
153
|
-
|
|
154
|
-
setValue(nextValue)
|
|
155
|
-
requestAnimationFrame(() => {
|
|
156
|
-
input.focus()
|
|
157
|
-
input.setSelectionRange(start - marker.length, end - marker.length)
|
|
158
|
-
setSelection({
|
|
159
|
-
start: start - marker.length,
|
|
160
|
-
end: end - marker.length,
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
return
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const nextValue =
|
|
167
|
-
value.slice(0, start) +
|
|
168
|
-
marker +
|
|
169
|
-
selectedText +
|
|
170
|
-
marker +
|
|
171
|
-
value.slice(end)
|
|
172
|
-
|
|
173
|
-
setValue(nextValue)
|
|
174
|
-
requestAnimationFrame(() => {
|
|
175
|
-
const selectionStart = start + marker.length
|
|
176
|
-
input.focus()
|
|
177
|
-
input.setSelectionRange(selectionStart, selectionStart + selectedText.length)
|
|
178
|
-
setSelection({
|
|
179
|
-
start: selectionStart,
|
|
180
|
-
end: selectionStart + selectedText.length,
|
|
181
|
-
})
|
|
182
|
-
})
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return (
|
|
186
|
-
<Toolbar
|
|
187
|
-
aria-label="Text formatting"
|
|
188
|
-
className={cn(
|
|
189
|
-
"flex w-full rounded-none border-0 border-b border-border-primary px-2 py-1",
|
|
190
|
-
className,
|
|
191
|
-
)}
|
|
192
|
-
{...props}
|
|
193
|
-
>
|
|
194
|
-
{children ??
|
|
195
|
-
formats.map((format) => (
|
|
196
|
-
<Toggle
|
|
197
|
-
key={format.label}
|
|
198
|
-
aria-label={format.label}
|
|
199
|
-
pressed={isFormatActive(format.marker)}
|
|
200
|
-
onPressedChange={() => handleFormat(format.marker)}
|
|
201
|
-
className={format.className}
|
|
202
|
-
>
|
|
203
|
-
{format.label}
|
|
204
|
-
</Toggle>
|
|
205
|
-
))}
|
|
206
|
-
</Toolbar>
|
|
207
|
-
)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export type MarkdownEditorInputProps = TextareaHTMLAttributes<HTMLTextAreaElement>
|
|
211
|
-
|
|
212
|
-
export const MarkdownEditorInput = forwardRef<
|
|
213
|
-
HTMLTextAreaElement,
|
|
214
|
-
MarkdownEditorInputProps
|
|
215
|
-
>(({ className, onChange, onSelect, ...props }, forwardedRef) => {
|
|
216
|
-
const { value, setValue, inputRef, setSelection } = useMarkdownEditor()
|
|
217
|
-
|
|
218
|
-
const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
|
219
|
-
setValue(event.target.value)
|
|
220
|
-
onChange?.(event)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
return (
|
|
224
|
-
<textarea
|
|
225
|
-
ref={(node) => {
|
|
226
|
-
inputRef.current = node
|
|
227
|
-
assignRef(forwardedRef, node)
|
|
228
|
-
}}
|
|
229
|
-
value={value}
|
|
230
|
-
className={cn(
|
|
231
|
-
textareaVariants({ variant: "ghost" }),
|
|
232
|
-
"min-h-56 resize-y rounded-none border-0 p-4 font-mono text-sm focus-visible:ring-0",
|
|
233
|
-
className,
|
|
234
|
-
)}
|
|
235
|
-
onChange={handleChange}
|
|
236
|
-
onSelect={(event) => {
|
|
237
|
-
setSelection({
|
|
238
|
-
start: event.currentTarget.selectionStart,
|
|
239
|
-
end: event.currentTarget.selectionEnd,
|
|
240
|
-
})
|
|
241
|
-
onSelect?.(event)
|
|
242
|
-
}}
|
|
243
|
-
{...props}
|
|
244
|
-
/>
|
|
245
|
-
)
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
MarkdownEditorInput.displayName = "MarkdownEditorInput"
|
|
249
|
-
|
|
250
|
-
const renderBasicMarkdown = (value: string): ReactNode[] =>
|
|
251
|
-
value
|
|
252
|
-
.split(/(\*\*[^*\n]+\*\*|`[^`\n]+`|\*[^*\n]+\*)/g)
|
|
253
|
-
.map((part, index) => {
|
|
254
|
-
if (part.startsWith("**") && part.endsWith("**")) {
|
|
255
|
-
return <strong key={index}>{part.slice(2, -2)}</strong>
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (part.startsWith("`") && part.endsWith("`")) {
|
|
259
|
-
return (
|
|
260
|
-
<code
|
|
261
|
-
key={index}
|
|
262
|
-
className="rounded-xs bg-background-tertiary px-1 py-0.5 font-mono text-sm"
|
|
263
|
-
>
|
|
264
|
-
{part.slice(1, -1)}
|
|
265
|
-
</code>
|
|
266
|
-
)
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (part.startsWith("*") && part.endsWith("*")) {
|
|
270
|
-
return <em key={index}>{part.slice(1, -1)}</em>
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return part
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
export type MarkdownEditorPreviewProps = HTMLAttributes<HTMLDivElement> & {
|
|
277
|
-
emptyText?: string
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
export const MarkdownEditorPreview = ({
|
|
281
|
-
className,
|
|
282
|
-
emptyText = "Nothing to preview",
|
|
283
|
-
...props
|
|
284
|
-
}: MarkdownEditorPreviewProps) => {
|
|
285
|
-
const { value } = useMarkdownEditor()
|
|
286
|
-
|
|
287
|
-
return (
|
|
288
|
-
<div
|
|
289
|
-
className={cn(
|
|
290
|
-
"min-h-56 whitespace-pre-wrap border-t border-border-primary p-4 text-sm text-fg-primary",
|
|
291
|
-
className,
|
|
292
|
-
)}
|
|
293
|
-
{...props}
|
|
294
|
-
>
|
|
295
|
-
{value ? (
|
|
296
|
-
renderBasicMarkdown(value)
|
|
297
|
-
) : (
|
|
298
|
-
<span className="text-fg-quaternary">{emptyText}</span>
|
|
299
|
-
)}
|
|
300
|
-
</div>
|
|
301
|
-
)
|
|
302
|
-
}
|