@lattice-php/lattice 0.36.1 → 0.38.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/dist/form/components/base/field.d.ts +14 -4
- package/dist/form/components/base/field.js +33 -10
- package/dist/form/components/base/field.js.map +1 -1
- package/dist/form/components/fields/builder.js +4 -2
- package/dist/form/components/fields/builder.js.map +1 -1
- package/dist/form/components/fields/choice.js +6 -5
- package/dist/form/components/fields/choice.js.map +1 -1
- package/dist/form/components/fields/color-picker-field.js +2 -2
- package/dist/form/components/fields/color-picker-field.js.map +1 -1
- package/dist/form/components/fields/date-input.js +2 -1
- package/dist/form/components/fields/date-input.js.map +1 -1
- package/dist/form/components/fields/date-picker-field.d.ts +3 -1
- package/dist/form/components/fields/date-picker-field.js +2 -2
- package/dist/form/components/fields/date-picker-field.js.map +1 -1
- package/dist/form/components/fields/date-time-input.js +2 -1
- package/dist/form/components/fields/date-time-input.js.map +1 -1
- package/dist/form/components/fields/file-upload.js +4 -4
- package/dist/form/components/fields/file-upload.js.map +1 -1
- package/dist/form/components/fields/number-input.js +3 -3
- package/dist/form/components/fields/number-input.js.map +1 -1
- package/dist/form/components/fields/otp-input.js +2 -2
- package/dist/form/components/fields/otp-input.js.map +1 -1
- package/dist/form/components/fields/password-input.js +6 -6
- package/dist/form/components/fields/password-input.js.map +1 -1
- package/dist/form/components/fields/repeater.js +4 -2
- package/dist/form/components/fields/repeater.js.map +1 -1
- package/dist/form/components/fields/rich-editor-field.js +7 -5
- package/dist/form/components/fields/rich-editor-field.js.map +1 -1
- package/dist/form/components/fields/select.js +5 -5
- package/dist/form/components/fields/select.js.map +1 -1
- package/dist/form/components/fields/simple-field.d.ts +2 -1
- package/dist/form/components/fields/simple-field.js +2 -2
- package/dist/form/components/fields/simple-field.js.map +1 -1
- package/dist/form/components/fields/text-input.js +2 -2
- package/dist/form/components/fields/text-input.js.map +1 -1
- package/dist/form/components/fields/textarea.js +2 -2
- package/dist/form/components/fields/textarea.js.map +1 -1
- package/dist/form/components/fields/time-input.js +2 -2
- package/dist/form/components/fields/time-input.js.map +1 -1
- package/dist/form/components/fields/toggle.js +6 -6
- package/dist/form/components/fields/toggle.js.map +1 -1
- package/dist/form/index.d.ts +1 -0
- package/dist/form/index.js +2 -1
- package/dist/form/toolkit.d.ts +1 -1
- package/dist/table/components/column-visibility-menu.js +1 -1
- package/dist/table/components/filter-controls.js +1 -1
- package/dist/table/components/filter-value-input.js +1 -1
- package/dist/table/components/table-search.js +1 -1
- package/dist/types/generated.d.ts +12 -2
- package/dist/ui/code-block-view.d.ts +3 -0
- package/dist/ui/code-block-view.js +135 -0
- package/dist/ui/code-block-view.js.map +1 -0
- package/dist/ui/code-block.d.ts +25 -0
- package/dist/ui/code-block.js +54 -0
- package/dist/ui/code-block.js.map +1 -0
- package/dist/ui/copyable-text.d.ts +3 -1
- package/dist/ui/copyable-text.js +19 -13
- package/dist/ui/copyable-text.js.map +1 -1
- package/dist/ui/index.d.ts +3 -0
- package/dist/ui/index.js +3 -1
- package/dist/ui/plugin.js +2 -0
- package/dist/ui/plugin.js.map +1 -1
- package/dist/ui/segmented-pills.d.ts +2 -1
- package/dist/ui/segmented-pills.js +3 -2
- package/dist/ui/segmented-pills.js.map +1 -1
- package/dist/vite.js.map +1 -1
- package/dist-standalone/chunks/{chart-view-CR1X2aGE.js → chart-view-CFV7BU_D.js} +17 -17
- package/dist-standalone/chunks/code-block-view-fzD5e55N.js +34 -0
- package/dist-standalone/chunks/date-picker-field-DepKtR_m.js +1 -0
- package/dist-standalone/chunks/dist-BtOmkEqQ.js +1 -0
- package/dist-standalone/chunks/jsx-runtime-0vZSBttN.js +1 -0
- package/dist-standalone/chunks/{jsx-runtime-CN5ucv1d.js → react-dom-DJCfT20h.js} +1 -1
- package/dist-standalone/chunks/rich-editor-field-BZQIPOLc.js +158 -0
- package/dist-standalone/chunks/{subscriptions-BV4g0s0U.js → subscriptions-4IpkpPX3.js} +1 -1
- package/dist-standalone/chunks/{time-picker-kbhOHEPd.js → time-picker-DUjYyeeP.js} +1 -1
- package/dist-standalone/chunks/use-dependent-field-DvH7mUyx.js +41 -0
- package/dist-standalone/chunks/use-effect-dispatcher-BMWOfaFE.js +97 -0
- package/dist-standalone/chunks/w3c-keyname-BOAvb0qz.js +1 -0
- package/dist-standalone/lattice.css +1 -1
- package/dist-standalone/lattice.js +5 -4
- package/dist-standalone/manifest.json +1 -1
- package/package.json +9 -1
- package/dist-standalone/chunks/date-picker-field-B4Xzow-A.js +0 -1
- package/dist-standalone/chunks/dist-ci5ng714.js +0 -1
- package/dist-standalone/chunks/icon-button-D3AltHNz.js +0 -41
- package/dist-standalone/chunks/rich-editor-field-JM3jBEV1.js +0 -158
- package/dist-standalone/chunks/use-effect-dispatcher-CJXFkAv1.js +0 -97
package/dist/form/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FormValuesProvider } from "./hooks/values.js";
|
|
2
|
+
import { FormFieldFrame } from "./components/base/field.js";
|
|
2
3
|
import { BuilderComponent } from "./components/fields/builder.js";
|
|
3
4
|
import { CheckboxComponent } from "./components/fields/checkbox.js";
|
|
4
5
|
import { ChoiceComponent } from "./components/fields/choice.js";
|
|
@@ -19,4 +20,4 @@ import { TimeInputComponent } from "./components/fields/time-input.js";
|
|
|
19
20
|
import { ToggleComponent } from "./components/fields/toggle.js";
|
|
20
21
|
import { WizardComponent, WizardStepComponent } from "./components/wizard.js";
|
|
21
22
|
import { formComponents } from "./plugin.js";
|
|
22
|
-
export { BuilderComponent, CheckboxComponent, ChoiceComponent, ColorPickerFieldComponent, DateInputComponent, DateTimeInputComponent, FileUploadComponent, FormComponent, FormValuesProvider, HiddenInputComponent, NumberInputComponent, OtpInputComponent, PasswordInputComponent, RepeaterComponent, SelectComponent, TextInputComponent, TextareaComponent, TimeInputComponent, ToggleComponent, WizardComponent, WizardStepComponent, formComponents };
|
|
23
|
+
export { BuilderComponent, CheckboxComponent, ChoiceComponent, ColorPickerFieldComponent, DateInputComponent, DateTimeInputComponent, FileUploadComponent, FormComponent, FormFieldFrame, FormValuesProvider, HiddenInputComponent, NumberInputComponent, OtpInputComponent, PasswordInputComponent, RepeaterComponent, SelectComponent, TextInputComponent, TextareaComponent, TimeInputComponent, ToggleComponent, WizardComponent, WizardStepComponent, formComponents };
|
package/dist/form/toolkit.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Entry point for building custom form fields outside the package. Form
|
|
3
3
|
* modules not re-exported here are internal and may change without notice.
|
|
4
4
|
*/
|
|
5
|
-
export { FormFieldFrame } from './components/base/field.js';
|
|
5
|
+
export { FormFieldFrame, type FormFieldControlProps } from './components/base/field.js';
|
|
6
6
|
export { useFormContext } from './hooks/context.js';
|
|
7
7
|
export { FieldScopeProvider, useFieldScope } from './hooks/field-scope.js';
|
|
8
8
|
export { fieldProps, walkFields } from './lib/field-props.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useT } from "../../i18n/instance.js";
|
|
2
2
|
import { Popover, PopoverContent, PopoverTrigger } from "../../ui/popover.js";
|
|
3
|
-
import { Checkbox } from "../../ui/checkbox.js";
|
|
4
3
|
import { IconButton } from "../../ui/icon-button.js";
|
|
4
|
+
import { Checkbox } from "../../ui/checkbox.js";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
//#region resources/js/table/components/column-visibility-menu.tsx
|
|
7
7
|
function ColumnVisibilityMenu({ columns, hasHidden, isVisible, onReset, onToggle, processing, visibleColumnCount }) {
|
|
@@ -8,8 +8,8 @@ import { ResolvedNodesProvider } from "../../form/hooks/resolved-nodes.js";
|
|
|
8
8
|
import { FormValuesProvider, useFormValues, useSetFormValue } from "../../form/hooks/values.js";
|
|
9
9
|
import { FieldCommitOverrideProvider } from "../../form/hooks/use-field-commit.js";
|
|
10
10
|
import { TableCellProvider } from "../../form/hooks/row-layout-context.js";
|
|
11
|
-
import { Checkbox } from "../../ui/checkbox.js";
|
|
12
11
|
import { IconButton } from "../../ui/icon-button.js";
|
|
12
|
+
import { Checkbox } from "../../ui/checkbox.js";
|
|
13
13
|
import { filterValue, isActiveFilterValue } from "../lib/filter-values.js";
|
|
14
14
|
import { isTruthy } from "../../lib/is-truthy.js";
|
|
15
15
|
import { useCallback, useMemo, useRef } from "react";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useT } from "../../i18n/instance.js";
|
|
2
2
|
import { cn } from "../../lib/utils.js";
|
|
3
3
|
import { Icon } from "../../icons/sprite.js";
|
|
4
|
-
import { Input } from "../../ui/input.js";
|
|
5
4
|
import { IconButton } from "../../ui/icon-button.js";
|
|
5
|
+
import { Input } from "../../ui/input.js";
|
|
6
6
|
import { NativeSelect } from "../../ui/native-select.js";
|
|
7
7
|
import { useEffect, useState } from "react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -2,8 +2,8 @@ import { useT } from "../../i18n/instance.js";
|
|
|
2
2
|
import { cn } from "../../lib/utils.js";
|
|
3
3
|
import { Icon } from "../../icons/sprite.js";
|
|
4
4
|
import { useDebouncedCallback } from "../../lib/use-debounced-callback.js";
|
|
5
|
-
import { Input } from "../../ui/input.js";
|
|
6
5
|
import { IconButton } from "../../ui/icon-button.js";
|
|
6
|
+
import { Input } from "../../ui/input.js";
|
|
7
7
|
import { useEffect, useRef, useState } from "react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
//#region resources/js/table/components/table-search.tsx
|
|
@@ -322,6 +322,15 @@ export type Choice = {
|
|
|
322
322
|
export type CloseModal = {
|
|
323
323
|
readonly modal: string | null;
|
|
324
324
|
};
|
|
325
|
+
export type CodeBlock = {
|
|
326
|
+
code: string;
|
|
327
|
+
copyable: boolean;
|
|
328
|
+
language: CodeBlockLanguage;
|
|
329
|
+
lineNumbers: boolean;
|
|
330
|
+
maxHeight: number | null;
|
|
331
|
+
wrap: boolean;
|
|
332
|
+
};
|
|
333
|
+
export type CodeBlockLanguage = "text" | "json" | "javascript" | "shell" | "php";
|
|
325
334
|
export type Collapsible = {
|
|
326
335
|
collapsed: boolean;
|
|
327
336
|
rememberState: boolean;
|
|
@@ -409,6 +418,7 @@ export type ComponentPropsMap = {
|
|
|
409
418
|
"chat.box": ChatBox;
|
|
410
419
|
"chat.part.text": TextPart;
|
|
411
420
|
"chat.part.tool-call": ToolCallPart;
|
|
421
|
+
"code-block": CodeBlock;
|
|
412
422
|
collapsible: Collapsible;
|
|
413
423
|
dropdown: Dropdown;
|
|
414
424
|
"field.builder": Builder;
|
|
@@ -841,7 +851,7 @@ export type MoneyColumn = {
|
|
|
841
851
|
toggleable: boolean;
|
|
842
852
|
width: ColumnWidth;
|
|
843
853
|
};
|
|
844
|
-
export type NodeType = "action" | "action.bulk" | "action.group" | "avatar" | "badge" | "breadcrumbs" | "button" | "callouts" | "card" | "chart" | "chat.box" | "chat.part.text" | "chat.part.tool-call" | "collapsible" | "dropdown" | "field.builder" | "field.checkbox" | "field.choice" | "field.color-picker" | "field.date-input" | "field.date-time-input" | "field.file-upload" | "field.hidden-input" | "field.number-input" | "field.otp" | "field.password-input" | "field.repeater" | "field.rich-editor" | "field.select" | "field.text-input" | "field.textarea" | "field.time-input" | "field.toggle" | "floating-panel" | "form" | "fragment" | "grid" | "heading" | "icon" | "image" | "link" | "menu" | "menu-item" | "modal" | "notifications" | "outlet" | "progress" | "raw-block" | "remote.data-list" | "section" | "segmented-control" | "separator" | "sidebar" | "stack" | "tab" | "table" | "tabs" | "text" | "tooltip" | "topbar" | "wizard" | "wizard-step";
|
|
854
|
+
export type NodeType = "action" | "action.bulk" | "action.group" | "avatar" | "badge" | "breadcrumbs" | "button" | "callouts" | "card" | "chart" | "chat.box" | "chat.part.text" | "chat.part.tool-call" | "code-block" | "collapsible" | "dropdown" | "field.builder" | "field.checkbox" | "field.choice" | "field.color-picker" | "field.date-input" | "field.date-time-input" | "field.file-upload" | "field.hidden-input" | "field.number-input" | "field.otp" | "field.password-input" | "field.repeater" | "field.rich-editor" | "field.select" | "field.text-input" | "field.textarea" | "field.time-input" | "field.toggle" | "floating-panel" | "form" | "fragment" | "grid" | "heading" | "icon" | "image" | "link" | "menu" | "menu-item" | "modal" | "notifications" | "outlet" | "progress" | "raw-block" | "remote.data-list" | "section" | "segmented-control" | "separator" | "sidebar" | "stack" | "tab" | "table" | "tabs" | "text" | "tooltip" | "topbar" | "wizard" | "wizard-step";
|
|
845
855
|
export type NotificationItem = {
|
|
846
856
|
readonly actions: Node[];
|
|
847
857
|
readonly body: Translatable | string | null;
|
|
@@ -1390,7 +1400,7 @@ export type Translatable = {
|
|
|
1390
1400
|
payload: Record<string, string>;
|
|
1391
1401
|
replacements: Record<string, string | number | boolean>;
|
|
1392
1402
|
};
|
|
1393
|
-
export type UiNodeType = "avatar" | "badge" | "button" | "card" | "chart" | "collapsible" | "floating-panel" | "grid" | "heading" | "icon" | "image" | "link" | "modal" | "progress" | "raw-block" | "section" | "segmented-control" | "separator" | "stack" | "tab" | "tabs" | "text" | "tooltip";
|
|
1403
|
+
export type UiNodeType = "avatar" | "badge" | "button" | "card" | "chart" | "code-block" | "collapsible" | "floating-panel" | "grid" | "heading" | "icon" | "image" | "link" | "modal" | "progress" | "raw-block" | "section" | "segmented-control" | "separator" | "stack" | "tab" | "tabs" | "text" | "tooltip";
|
|
1394
1404
|
export type UnreadCount = {
|
|
1395
1405
|
readonly unreadCount: number;
|
|
1396
1406
|
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { javascript } from "@codemirror/lang-javascript";
|
|
4
|
+
import { json } from "@codemirror/lang-json";
|
|
5
|
+
import { php } from "@codemirror/lang-php";
|
|
6
|
+
import { HighlightStyle, StreamLanguage, syntaxHighlighting } from "@codemirror/language";
|
|
7
|
+
import { shell } from "@codemirror/legacy-modes/mode/shell";
|
|
8
|
+
import { Compartment, EditorState } from "@codemirror/state";
|
|
9
|
+
import { EditorView, lineNumbers } from "@codemirror/view";
|
|
10
|
+
import { tags } from "@lezer/highlight";
|
|
11
|
+
//#region resources/js/ui/code-block-view.tsx
|
|
12
|
+
var languages = {
|
|
13
|
+
text: [],
|
|
14
|
+
json: json(),
|
|
15
|
+
javascript: javascript(),
|
|
16
|
+
shell: StreamLanguage.define(shell),
|
|
17
|
+
php: php()
|
|
18
|
+
};
|
|
19
|
+
var highlightStyle = HighlightStyle.define([
|
|
20
|
+
{
|
|
21
|
+
tag: tags.comment,
|
|
22
|
+
color: "var(--lt-muted-fg)",
|
|
23
|
+
fontStyle: "italic"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
tag: [
|
|
27
|
+
tags.keyword,
|
|
28
|
+
tags.operatorKeyword,
|
|
29
|
+
tags.controlKeyword
|
|
30
|
+
],
|
|
31
|
+
color: "var(--lt-primary)"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
tag: [tags.string, tags.special(tags.string)],
|
|
35
|
+
color: "var(--lt-success)"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
tag: [
|
|
39
|
+
tags.number,
|
|
40
|
+
tags.bool,
|
|
41
|
+
tags.null
|
|
42
|
+
],
|
|
43
|
+
color: "var(--lt-info)"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
tag: [tags.function(tags.variableName), tags.labelName],
|
|
47
|
+
color: "var(--lt-accent-fg)"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
tag: [tags.typeName, tags.className],
|
|
51
|
+
color: "var(--lt-danger)"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
tag: tags.meta,
|
|
55
|
+
color: "var(--lt-muted-fg)"
|
|
56
|
+
}
|
|
57
|
+
]);
|
|
58
|
+
var codeBlockTheme = EditorView.theme({
|
|
59
|
+
"&": {
|
|
60
|
+
backgroundColor: "transparent",
|
|
61
|
+
color: "var(--lt-fg)",
|
|
62
|
+
fontSize: "0.75rem"
|
|
63
|
+
},
|
|
64
|
+
".cm-scroller": {
|
|
65
|
+
fontFamily: "var(--lt-font-mono)",
|
|
66
|
+
lineHeight: "1.5",
|
|
67
|
+
overflow: "auto"
|
|
68
|
+
},
|
|
69
|
+
".cm-content": { padding: "0.75rem" },
|
|
70
|
+
".cm-gutters": {
|
|
71
|
+
backgroundColor: "transparent",
|
|
72
|
+
borderRightColor: "var(--lt-border)",
|
|
73
|
+
color: "var(--lt-muted-fg)"
|
|
74
|
+
},
|
|
75
|
+
".cm-lineNumbers .cm-gutterElement": { padding: "0 0.75rem" },
|
|
76
|
+
".cm-line": { padding: "0" }
|
|
77
|
+
});
|
|
78
|
+
function CodeBlockView({ children, language, lineNumbers: lineNumbers$1, maxHeight, wrap }) {
|
|
79
|
+
const container = useRef(null);
|
|
80
|
+
const content = useRef(children);
|
|
81
|
+
const editor = useRef(null);
|
|
82
|
+
content.current = children;
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (!container.current) return;
|
|
85
|
+
const languageCompartment = new Compartment();
|
|
86
|
+
const customLanguage = typeof language === "function" ? language : null;
|
|
87
|
+
const initialLanguage = typeof language === "function" ? [] : languages[language];
|
|
88
|
+
const view = new EditorView({
|
|
89
|
+
doc: content.current,
|
|
90
|
+
parent: container.current,
|
|
91
|
+
extensions: [
|
|
92
|
+
EditorState.readOnly.of(true),
|
|
93
|
+
EditorView.editable.of(false),
|
|
94
|
+
EditorView.contentAttributes.of({ role: "code" }),
|
|
95
|
+
languageCompartment.of(initialLanguage),
|
|
96
|
+
syntaxHighlighting(highlightStyle),
|
|
97
|
+
codeBlockTheme,
|
|
98
|
+
lineNumbers$1 ? lineNumbers() : [],
|
|
99
|
+
maxHeight === null ? [] : EditorView.theme({ "&": { maxHeight: `${maxHeight}px` } }),
|
|
100
|
+
wrap ? EditorView.lineWrapping : []
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
editor.current = view;
|
|
104
|
+
let active = true;
|
|
105
|
+
if (customLanguage) customLanguage().then((extension) => {
|
|
106
|
+
if (active) view.dispatch({ effects: languageCompartment.reconfigure(extension) });
|
|
107
|
+
}).catch((error) => {
|
|
108
|
+
if (active) console.warn("[lattice] CodeBlock language failed to load.", error);
|
|
109
|
+
});
|
|
110
|
+
return () => {
|
|
111
|
+
active = false;
|
|
112
|
+
editor.current = null;
|
|
113
|
+
view.destroy();
|
|
114
|
+
};
|
|
115
|
+
}, [
|
|
116
|
+
language,
|
|
117
|
+
lineNumbers$1,
|
|
118
|
+
maxHeight,
|
|
119
|
+
wrap
|
|
120
|
+
]);
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
const view = editor.current;
|
|
123
|
+
if (!view || view.state.doc.toString() === children) return;
|
|
124
|
+
view.dispatch({ changes: {
|
|
125
|
+
from: 0,
|
|
126
|
+
to: view.state.doc.length,
|
|
127
|
+
insert: children
|
|
128
|
+
} });
|
|
129
|
+
}, [children]);
|
|
130
|
+
return /* @__PURE__ */ jsx("div", { ref: container });
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { CodeBlockView as default };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=code-block-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block-view.js","names":[],"sources":["../../resources/js/ui/code-block-view.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { javascript } from \"@codemirror/lang-javascript\";\nimport { json } from \"@codemirror/lang-json\";\nimport { php } from \"@codemirror/lang-php\";\nimport { HighlightStyle, StreamLanguage, syntaxHighlighting } from \"@codemirror/language\";\nimport { shell } from \"@codemirror/legacy-modes/mode/shell\";\nimport { Compartment, EditorState, type Extension } from \"@codemirror/state\";\nimport { EditorView, lineNumbers as showLineNumbers } from \"@codemirror/view\";\nimport { tags } from \"@lezer/highlight\";\nimport type { CodeBlockLanguage, CodeBlockViewProps } from \"./code-block\";\n\nconst languages: Record<CodeBlockLanguage, Extension> = {\n text: [],\n json: json(),\n javascript: javascript(),\n shell: StreamLanguage.define(shell),\n php: php(),\n};\n\nconst highlightStyle = HighlightStyle.define([\n { tag: tags.comment, color: \"var(--lt-muted-fg)\", fontStyle: \"italic\" },\n { tag: [tags.keyword, tags.operatorKeyword, tags.controlKeyword], color: \"var(--lt-primary)\" },\n { tag: [tags.string, tags.special(tags.string)], color: \"var(--lt-success)\" },\n { tag: [tags.number, tags.bool, tags.null], color: \"var(--lt-info)\" },\n { tag: [tags.function(tags.variableName), tags.labelName], color: \"var(--lt-accent-fg)\" },\n { tag: [tags.typeName, tags.className], color: \"var(--lt-danger)\" },\n { tag: tags.meta, color: \"var(--lt-muted-fg)\" },\n]);\n\nconst codeBlockTheme = EditorView.theme({\n \"&\": {\n backgroundColor: \"transparent\",\n color: \"var(--lt-fg)\",\n fontSize: \"0.75rem\",\n },\n \".cm-scroller\": {\n fontFamily: \"var(--lt-font-mono)\",\n lineHeight: \"1.5\",\n overflow: \"auto\",\n },\n \".cm-content\": { padding: \"0.75rem\" },\n \".cm-gutters\": {\n backgroundColor: \"transparent\",\n borderRightColor: \"var(--lt-border)\",\n color: \"var(--lt-muted-fg)\",\n },\n \".cm-lineNumbers .cm-gutterElement\": { padding: \"0 0.75rem\" },\n \".cm-line\": { padding: \"0\" },\n});\n\nfunction CodeBlockView({ children, language, lineNumbers, maxHeight, wrap }: CodeBlockViewProps) {\n const container = useRef<HTMLDivElement>(null);\n const content = useRef(children);\n const editor = useRef<EditorView>(null);\n content.current = children;\n\n useEffect(() => {\n if (!container.current) {\n return;\n }\n\n const languageCompartment = new Compartment();\n const customLanguage = typeof language === \"function\" ? language : null;\n const initialLanguage = typeof language === \"function\" ? [] : languages[language];\n const view = new EditorView({\n doc: content.current,\n parent: container.current,\n extensions: [\n EditorState.readOnly.of(true),\n EditorView.editable.of(false),\n EditorView.contentAttributes.of({ role: \"code\" }),\n languageCompartment.of(initialLanguage),\n syntaxHighlighting(highlightStyle),\n codeBlockTheme,\n lineNumbers ? showLineNumbers() : [],\n maxHeight === null\n ? []\n : EditorView.theme({\n \"&\": { maxHeight: `${maxHeight}px` },\n }),\n wrap ? EditorView.lineWrapping : [],\n ],\n });\n editor.current = view;\n let active = true;\n\n if (customLanguage) {\n void customLanguage()\n .then((extension) => {\n if (active) {\n view.dispatch({ effects: languageCompartment.reconfigure(extension) });\n }\n })\n .catch((error: unknown) => {\n if (active) {\n console.warn(\"[lattice] CodeBlock language failed to load.\", error);\n }\n });\n }\n\n return () => {\n active = false;\n editor.current = null;\n view.destroy();\n };\n }, [language, lineNumbers, maxHeight, wrap]);\n\n useEffect(() => {\n const view = editor.current;\n\n if (!view || view.state.doc.toString() === children) {\n return;\n }\n\n view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: children } });\n }, [children]);\n\n return <div ref={container} />;\n}\n\nexport default CodeBlockView;\n"],"mappings":";;;;;;;;;;;AAWA,IAAM,YAAkD;CACtD,MAAM,CAAC;CACP,MAAM,KAAK;CACX,YAAY,WAAW;CACvB,OAAO,eAAe,OAAO,KAAK;CAClC,KAAK,IAAI;AACX;AAEA,IAAM,iBAAiB,eAAe,OAAO;CAC3C;EAAE,KAAK,KAAK;EAAS,OAAO;EAAsB,WAAW;CAAS;CACtE;EAAE,KAAK;GAAC,KAAK;GAAS,KAAK;GAAiB,KAAK;EAAc;EAAG,OAAO;CAAoB;CAC7F;EAAE,KAAK,CAAC,KAAK,QAAQ,KAAK,QAAQ,KAAK,MAAM,CAAC;EAAG,OAAO;CAAoB;CAC5E;EAAE,KAAK;GAAC,KAAK;GAAQ,KAAK;GAAM,KAAK;EAAI;EAAG,OAAO;CAAiB;CACpE;EAAE,KAAK,CAAC,KAAK,SAAS,KAAK,YAAY,GAAG,KAAK,SAAS;EAAG,OAAO;CAAsB;CACxF;EAAE,KAAK,CAAC,KAAK,UAAU,KAAK,SAAS;EAAG,OAAO;CAAmB;CAClE;EAAE,KAAK,KAAK;EAAM,OAAO;CAAqB;AAChD,CAAC;AAED,IAAM,iBAAiB,WAAW,MAAM;CACtC,KAAK;EACH,iBAAiB;EACjB,OAAO;EACP,UAAU;CACZ;CACA,gBAAgB;EACd,YAAY;EACZ,YAAY;EACZ,UAAU;CACZ;CACA,eAAe,EAAE,SAAS,UAAU;CACpC,eAAe;EACb,iBAAiB;EACjB,kBAAkB;EAClB,OAAO;CACT;CACA,qCAAqC,EAAE,SAAS,YAAY;CAC5D,YAAY,EAAE,SAAS,IAAI;AAC7B,CAAC;AAED,SAAS,cAAc,EAAE,UAAU,UAAU,aAAA,eAAa,WAAW,QAA4B;CAC/F,MAAM,YAAY,OAAuB,IAAI;CAC7C,MAAM,UAAU,OAAO,QAAQ;CAC/B,MAAM,SAAS,OAAmB,IAAI;CACtC,QAAQ,UAAU;CAElB,gBAAgB;EACd,IAAI,CAAC,UAAU,SACb;EAGF,MAAM,sBAAsB,IAAI,YAAY;EAC5C,MAAM,iBAAiB,OAAO,aAAa,aAAa,WAAW;EACnE,MAAM,kBAAkB,OAAO,aAAa,aAAa,CAAC,IAAI,UAAU;EACxE,MAAM,OAAO,IAAI,WAAW;GAC1B,KAAK,QAAQ;GACb,QAAQ,UAAU;GAClB,YAAY;IACV,YAAY,SAAS,GAAG,IAAI;IAC5B,WAAW,SAAS,GAAG,KAAK;IAC5B,WAAW,kBAAkB,GAAG,EAAE,MAAM,OAAO,CAAC;IAChD,oBAAoB,GAAG,eAAe;IACtC,mBAAmB,cAAc;IACjC;IACA,gBAAc,YAAgB,IAAI,CAAC;IACnC,cAAc,OACV,CAAC,IACD,WAAW,MAAM,EACf,KAAK,EAAE,WAAW,GAAG,UAAU,IAAI,EACrC,CAAC;IACL,OAAO,WAAW,eAAe,CAAC;GACpC;EACF,CAAC;EACD,OAAO,UAAU;EACjB,IAAI,SAAS;EAEb,IAAI,gBACF,eAAoB,CAAC,CAClB,MAAM,cAAc;GACnB,IAAI,QACF,KAAK,SAAS,EAAE,SAAS,oBAAoB,YAAY,SAAS,EAAE,CAAC;EAEzE,CAAC,CAAC,CACD,OAAO,UAAmB;GACzB,IAAI,QACF,QAAQ,KAAK,gDAAgD,KAAK;EAEtE,CAAC;EAGL,aAAa;GACX,SAAS;GACT,OAAO,UAAU;GACjB,KAAK,QAAQ;EACf;CACF,GAAG;EAAC;EAAU;EAAa;EAAW;CAAI,CAAC;CAE3C,gBAAgB;EACd,MAAM,OAAO,OAAO;EAEpB,IAAI,CAAC,QAAQ,KAAK,MAAM,IAAI,SAAS,MAAM,UACzC;EAGF,KAAK,SAAS,EAAE,SAAS;GAAE,MAAM;GAAG,IAAI,KAAK,MAAM,IAAI;GAAQ,QAAQ;EAAS,EAAE,CAAC;CACrF,GAAG,CAAC,QAAQ,CAAC;CAEb,OAAO,oBAAC,OAAD,EAAK,KAAK,UAAY,CAAA;AAC/B"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ComponentProps } from 'react';
|
|
2
|
+
import { Extension } from '@codemirror/state';
|
|
3
|
+
import { RendererComponent } from '../core/types.js';
|
|
4
|
+
type CodeBlockLanguage = "text" | "json" | "javascript" | "shell" | "php";
|
|
5
|
+
type CodeBlockLanguageLoader = () => Promise<Extension>;
|
|
6
|
+
interface CodeBlockProps extends Omit<ComponentProps<"div">, "children"> {
|
|
7
|
+
children: string;
|
|
8
|
+
copyable?: boolean;
|
|
9
|
+
language?: CodeBlockLanguage | CodeBlockLanguageLoader;
|
|
10
|
+
lineNumbers?: boolean;
|
|
11
|
+
maxHeight?: number | null;
|
|
12
|
+
wrap?: boolean;
|
|
13
|
+
}
|
|
14
|
+
interface CodeBlockViewProps {
|
|
15
|
+
children: string;
|
|
16
|
+
language: CodeBlockLanguage | CodeBlockLanguageLoader;
|
|
17
|
+
lineNumbers: boolean;
|
|
18
|
+
maxHeight: number | null;
|
|
19
|
+
wrap: boolean;
|
|
20
|
+
}
|
|
21
|
+
declare function CodeBlock({ "aria-label": ariaLabel, children, className, copyable, language, lineNumbers, maxHeight, role, wrap, ...props }: CodeBlockProps): import("react").JSX.Element;
|
|
22
|
+
declare const CodeBlockComponent: RendererComponent<"code-block">;
|
|
23
|
+
export default CodeBlockComponent;
|
|
24
|
+
export { CodeBlock };
|
|
25
|
+
export type { CodeBlockLanguage, CodeBlockLanguageLoader, CodeBlockProps, CodeBlockViewProps };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { CopyButton } from "./copyable-text.js";
|
|
3
|
+
import { Fragment, Suspense, lazy } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region resources/js/ui/code-block.tsx
|
|
6
|
+
var CodeBlockView = lazy(() => import("./code-block-view.js"));
|
|
7
|
+
function CodeBlock({ "aria-label": ariaLabel, children, className, copyable = false, language = "text", lineNumbers = false, maxHeight = null, role = ariaLabel ? "region" : void 0, wrap = false, ...props }) {
|
|
8
|
+
const fallback = /* @__PURE__ */ jsx("pre", {
|
|
9
|
+
className: cn("max-w-full overflow-auto p-3 font-lt-mono text-xs", wrap && "whitespace-pre-wrap wrap-anywhere"),
|
|
10
|
+
style: { maxHeight: maxHeight ?? void 0 },
|
|
11
|
+
children: lineNumbers ? /* @__PURE__ */ jsx("code", {
|
|
12
|
+
className: "grid grid-cols-[auto_1fr]",
|
|
13
|
+
children: children.split("\n").map((line, index) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
14
|
+
"aria-hidden": "true",
|
|
15
|
+
className: "mr-3 border-r border-lt-border pr-3 text-right text-lt-muted-fg select-none",
|
|
16
|
+
children: index + 1
|
|
17
|
+
}), /* @__PURE__ */ jsx("span", { children: line || "" })] }, index))
|
|
18
|
+
}) : children
|
|
19
|
+
});
|
|
20
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
21
|
+
"data-slot": "code-block",
|
|
22
|
+
"aria-label": ariaLabel,
|
|
23
|
+
role,
|
|
24
|
+
...props,
|
|
25
|
+
className: cn("relative max-w-full overflow-hidden rounded-lt-sm bg-lt-muted text-lt-fg", className),
|
|
26
|
+
children: [copyable ? /* @__PURE__ */ jsx(CopyButton, {
|
|
27
|
+
value: children,
|
|
28
|
+
label: ariaLabel ?? "code",
|
|
29
|
+
iconOnly: true,
|
|
30
|
+
className: "absolute top-3 right-6 z-10 bg-lt-bg/80 hover:bg-lt-bg"
|
|
31
|
+
}) : null, /* @__PURE__ */ jsx(Suspense, {
|
|
32
|
+
fallback,
|
|
33
|
+
children: /* @__PURE__ */ jsx(CodeBlockView, {
|
|
34
|
+
language,
|
|
35
|
+
lineNumbers,
|
|
36
|
+
maxHeight,
|
|
37
|
+
wrap,
|
|
38
|
+
children
|
|
39
|
+
})
|
|
40
|
+
})]
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
var CodeBlockComponent = ({ node }) => /* @__PURE__ */ jsx(CodeBlock, {
|
|
44
|
+
copyable: node.props.copyable,
|
|
45
|
+
language: node.props.language,
|
|
46
|
+
lineNumbers: node.props.lineNumbers,
|
|
47
|
+
maxHeight: node.props.maxHeight,
|
|
48
|
+
wrap: node.props.wrap,
|
|
49
|
+
children: node.props.code
|
|
50
|
+
});
|
|
51
|
+
//#endregion
|
|
52
|
+
export { CodeBlock, CodeBlockComponent as default };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=code-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block.js","names":[],"sources":["../../resources/js/ui/code-block.tsx"],"sourcesContent":["import { Fragment, lazy, Suspense } from \"react\";\nimport type { ComponentProps } from \"react\";\nimport type { Extension } from \"@codemirror/state\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { CopyButton } from \"./copyable-text\";\n\nconst CodeBlockView = lazy(() => import(\"./code-block-view\"));\n\ntype CodeBlockLanguage = \"text\" | \"json\" | \"javascript\" | \"shell\" | \"php\";\ntype CodeBlockLanguageLoader = () => Promise<Extension>;\n\ninterface CodeBlockProps extends Omit<ComponentProps<\"div\">, \"children\"> {\n children: string;\n copyable?: boolean;\n language?: CodeBlockLanguage | CodeBlockLanguageLoader;\n lineNumbers?: boolean;\n maxHeight?: number | null;\n wrap?: boolean;\n}\n\ninterface CodeBlockViewProps {\n children: string;\n language: CodeBlockLanguage | CodeBlockLanguageLoader;\n lineNumbers: boolean;\n maxHeight: number | null;\n wrap: boolean;\n}\n\nfunction CodeBlock({\n \"aria-label\": ariaLabel,\n children,\n className,\n copyable = false,\n language = \"text\",\n lineNumbers = false,\n maxHeight = null,\n role = ariaLabel ? \"region\" : undefined,\n wrap = false,\n ...props\n}: CodeBlockProps) {\n const fallback = (\n <pre\n className={cn(\n \"max-w-full overflow-auto p-3 font-lt-mono text-xs\",\n wrap && \"whitespace-pre-wrap wrap-anywhere\",\n )}\n style={{ maxHeight: maxHeight ?? undefined }}\n >\n {lineNumbers ? (\n <code className=\"grid grid-cols-[auto_1fr]\">\n {children.split(\"\\n\").map((line, index) => (\n <Fragment key={index}>\n <span\n aria-hidden=\"true\"\n className=\"mr-3 border-r border-lt-border pr-3 text-right text-lt-muted-fg select-none\"\n >\n {index + 1}\n </span>\n <span>{line || \"\\u200b\"}</span>\n </Fragment>\n ))}\n </code>\n ) : (\n children\n )}\n </pre>\n );\n\n return (\n <div\n data-slot=\"code-block\"\n aria-label={ariaLabel}\n role={role}\n {...props}\n className={cn(\n \"relative max-w-full overflow-hidden rounded-lt-sm bg-lt-muted text-lt-fg\",\n className,\n )}\n >\n {copyable ? (\n <CopyButton\n value={children}\n label={ariaLabel ?? \"code\"}\n iconOnly\n className=\"absolute top-3 right-6 z-10 bg-lt-bg/80 hover:bg-lt-bg\"\n />\n ) : null}\n <Suspense fallback={fallback}>\n <CodeBlockView\n language={language}\n lineNumbers={lineNumbers}\n maxHeight={maxHeight}\n wrap={wrap}\n >\n {children}\n </CodeBlockView>\n </Suspense>\n </div>\n );\n}\n\nconst CodeBlockComponent: RendererComponent<\"code-block\"> = ({ node }) => (\n <CodeBlock\n copyable={node.props.copyable}\n language={node.props.language}\n lineNumbers={node.props.lineNumbers}\n maxHeight={node.props.maxHeight}\n wrap={node.props.wrap}\n >\n {node.props.code}\n </CodeBlock>\n);\n\nexport default CodeBlockComponent;\nexport { CodeBlock };\nexport type { CodeBlockLanguage, CodeBlockLanguageLoader, CodeBlockProps, CodeBlockViewProps };\n"],"mappings":";;;;;AAOA,IAAM,gBAAgB,WAAW,OAAO,uBAAoB;AAsB5D,SAAS,UAAU,EACjB,cAAc,WACd,UACA,WACA,WAAW,OACX,WAAW,QACX,cAAc,OACd,YAAY,MACZ,OAAO,YAAY,WAAW,KAAA,GAC9B,OAAO,OACP,GAAG,SACc;CACjB,MAAM,WACJ,oBAAC,OAAD;EACE,WAAW,GACT,qDACA,QAAQ,mCACV;EACA,OAAO,EAAE,WAAW,aAAa,KAAA,EAAU;EAE1C,UAAA,cACC,oBAAC,QAAD;GAAM,WAAU;GACb,UAAA,SAAS,MAAM,IAAI,CAAC,CAAC,KAAK,MAAM,UAC/B,qBAAC,UAAD,EAAA,UAAA,CACE,oBAAC,QAAD;IACE,eAAY;IACZ,WAAU;IAET,UAAA,QAAQ;GACL,CAAA,GACN,oBAAC,QAAD,EAAA,UAAO,QAAQ,IAAe,CAAA,CACtB,EAAA,GARK,KAQL,CACX;EACG,CAAA,IAEN;CAEC,CAAA;CAGP,OACE,qBAAC,OAAD;EACE,aAAU;EACV,cAAY;EACN;EACN,GAAI;EACJ,WAAW,GACT,4EACA,SACF;EARF,UAAA,CAUG,WACC,oBAAC,YAAD;GACE,OAAO;GACP,OAAO,aAAa;GACpB,UAAA;GACA,WAAU;EACX,CAAA,IACC,MACJ,oBAAC,UAAD;GAAoB;GAClB,UAAA,oBAAC,eAAD;IACY;IACG;IACF;IACL;IAEL;GACY,CAAA;EACP,CAAA,CACP;;AAET;AAEA,IAAM,sBAAuD,EAAE,WAC7D,oBAAC,WAAD;CACE,UAAU,KAAK,MAAM;CACrB,UAAU,KAAK,MAAM;CACrB,aAAa,KAAK,MAAM;CACxB,WAAW,KAAK,MAAM;CACtB,MAAM,KAAK,MAAM;CAEhB,UAAA,KAAK,MAAM;AACH,CAAA"}
|
|
@@ -5,8 +5,10 @@ interface CopyButtonProps {
|
|
|
5
5
|
label: string;
|
|
6
6
|
testId?: string;
|
|
7
7
|
className?: string;
|
|
8
|
+
iconOnly?: boolean;
|
|
9
|
+
children?: ReactNode;
|
|
8
10
|
}
|
|
9
|
-
export declare function CopyButton({ value, label, testId, className }: CopyButtonProps): ReactNode;
|
|
11
|
+
export declare function CopyButton({ value, label, testId, className, iconOnly, children, }: CopyButtonProps): ReactNode;
|
|
10
12
|
interface CopyableTextProps {
|
|
11
13
|
value: string;
|
|
12
14
|
label: string;
|
package/dist/ui/copyable-text.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useT } from "../i18n/instance.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { Button } from "./button.js";
|
|
3
|
+
import { IconButton } from "./icon-button.js";
|
|
4
4
|
import { useEffect, useState } from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
//#region resources/js/ui/copyable-text.tsx
|
|
@@ -13,7 +13,7 @@ async function copyToClipboard(text) {
|
|
|
13
13
|
return false;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
function CopyButton({ value, label, testId, className }) {
|
|
16
|
+
function CopyButton({ value, label, testId, className, iconOnly = false, children }) {
|
|
17
17
|
const { t } = useT("lattice");
|
|
18
18
|
const [copied, setCopied] = useState(false);
|
|
19
19
|
useEffect(() => {
|
|
@@ -21,21 +21,27 @@ function CopyButton({ value, label, testId, className }) {
|
|
|
21
21
|
const timeout = window.setTimeout(() => setCopied(false), 1500);
|
|
22
22
|
return () => window.clearTimeout(timeout);
|
|
23
23
|
}, [copied]);
|
|
24
|
-
function handleCopy() {
|
|
25
|
-
copyToClipboard(value);
|
|
26
|
-
setCopied(true);
|
|
24
|
+
async function handleCopy() {
|
|
25
|
+
if (await copyToClipboard(value)) setCopied(true);
|
|
27
26
|
}
|
|
28
27
|
const ariaLabel = copied ? t("common.copied-value", "Copied {{label}}", { label }) : t("common.copy-value", "Copy {{label}}", { label });
|
|
29
|
-
return /* @__PURE__ */
|
|
28
|
+
if (iconOnly) return /* @__PURE__ */ jsx(IconButton, {
|
|
29
|
+
icon: copied ? "check" : "copy",
|
|
30
|
+
label: ariaLabel,
|
|
31
|
+
"data-test": testId,
|
|
32
|
+
className,
|
|
33
|
+
onClick: () => void handleCopy()
|
|
34
|
+
});
|
|
35
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
30
36
|
type: "button",
|
|
37
|
+
size: "sm",
|
|
38
|
+
emphasis: "outline",
|
|
39
|
+
icon: copied ? "check" : "copy",
|
|
31
40
|
"data-test": testId,
|
|
32
|
-
className
|
|
41
|
+
className,
|
|
33
42
|
"aria-label": ariaLabel,
|
|
34
|
-
onClick: handleCopy,
|
|
35
|
-
children:
|
|
36
|
-
name: copied ? "check" : "copy",
|
|
37
|
-
className: "size-lt-icon-xs"
|
|
38
|
-
}), copied ? t("common.copied", "Copied") : t("common.copy", "Copy")]
|
|
43
|
+
onClick: () => void handleCopy(),
|
|
44
|
+
children: copied ? t("common.copied", "Copied") : children ?? t("common.copy", "Copy")
|
|
39
45
|
});
|
|
40
46
|
}
|
|
41
47
|
function CopyableText({ value, label, testId, children }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copyable-text.js","names":[],"sources":["../../resources/js/ui/copyable-text.tsx"],"sourcesContent":["import { useT } from \"@lattice-php/lattice/i18n\";\nimport {
|
|
1
|
+
{"version":3,"file":"copyable-text.js","names":[],"sources":["../../resources/js/ui/copyable-text.tsx"],"sourcesContent":["import { useT } from \"@lattice-php/lattice/i18n\";\nimport { type ReactNode, useEffect, useState } from \"react\";\nimport { Button } from \"./button\";\nimport { IconButton } from \"./icon-button\";\n\nexport async function copyToClipboard(text: string): Promise<boolean> {\n if (!navigator?.clipboard) {\n return false;\n }\n\n try {\n await navigator.clipboard.writeText(text);\n\n return true;\n } catch {\n return false;\n }\n}\n\ninterface CopyButtonProps {\n value: string;\n label: string;\n testId?: string;\n className?: string;\n iconOnly?: boolean;\n children?: ReactNode;\n}\n\nexport function CopyButton({\n value,\n label,\n testId,\n className,\n iconOnly = false,\n children,\n}: CopyButtonProps): ReactNode {\n const { t } = useT(\"lattice\");\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n if (!copied) {\n return;\n }\n\n const timeout = window.setTimeout(() => setCopied(false), 1500);\n\n return () => window.clearTimeout(timeout);\n }, [copied]);\n\n async function handleCopy(): Promise<void> {\n if (await copyToClipboard(value)) {\n setCopied(true);\n }\n }\n\n const ariaLabel = copied\n ? t(\"common.copied-value\", \"Copied {{label}}\", { label })\n : t(\"common.copy-value\", \"Copy {{label}}\", { label });\n\n if (iconOnly) {\n return (\n <IconButton\n icon={copied ? \"check\" : \"copy\"}\n label={ariaLabel}\n data-test={testId}\n className={className}\n onClick={() => void handleCopy()}\n />\n );\n }\n\n return (\n <Button\n type=\"button\"\n size=\"sm\"\n emphasis=\"outline\"\n icon={copied ? \"check\" : \"copy\"}\n data-test={testId}\n className={className}\n aria-label={ariaLabel}\n onClick={() => void handleCopy()}\n >\n {copied ? t(\"common.copied\", \"Copied\") : (children ?? t(\"common.copy\", \"Copy\"))}\n </Button>\n );\n}\n\ninterface CopyableTextProps {\n value: string;\n label: string;\n testId?: string;\n children?: ReactNode;\n}\n\nexport function CopyableText({ value, label, testId, children }: CopyableTextProps): ReactNode {\n return (\n <div className=\"inline-flex items-center gap-2\">\n {children ?? value}\n <CopyButton value={value} label={label} testId={testId} />\n </div>\n );\n}\n"],"mappings":";;;;;;AAKA,eAAsB,gBAAgB,MAAgC;CACpE,IAAI,CAAC,WAAW,WACd,OAAO;CAGT,IAAI;EACF,MAAM,UAAU,UAAU,UAAU,IAAI;EAExC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAWA,SAAgB,WAAW,EACzB,OACA,OACA,QACA,WACA,WAAW,OACX,YAC6B;CAC7B,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAE1C,gBAAgB;EACd,IAAI,CAAC,QACH;EAGF,MAAM,UAAU,OAAO,iBAAiB,UAAU,KAAK,GAAG,IAAI;EAE9D,aAAa,OAAO,aAAa,OAAO;CAC1C,GAAG,CAAC,MAAM,CAAC;CAEX,eAAe,aAA4B;EACzC,IAAI,MAAM,gBAAgB,KAAK,GAC7B,UAAU,IAAI;CAElB;CAEA,MAAM,YAAY,SACd,EAAE,uBAAuB,oBAAoB,EAAE,MAAM,CAAC,IACtD,EAAE,qBAAqB,kBAAkB,EAAE,MAAM,CAAC;CAEtD,IAAI,UACF,OACE,oBAAC,YAAD;EACE,MAAM,SAAS,UAAU;EACzB,OAAO;EACP,aAAW;EACA;EACX,eAAe,KAAK,WAAW;CAChC,CAAA;CAIL,OACE,oBAAC,QAAD;EACE,MAAK;EACL,MAAK;EACL,UAAS;EACT,MAAM,SAAS,UAAU;EACzB,aAAW;EACA;EACX,cAAY;EACZ,eAAe,KAAK,WAAW;EAE9B,UAAA,SAAS,EAAE,iBAAiB,QAAQ,IAAK,YAAY,EAAE,eAAe,MAAM;CACvE,CAAA;AAEZ;AASA,SAAgB,aAAa,EAAE,OAAO,OAAO,QAAQ,YAA0C;CAC7F,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACG,YAAY,OACb,oBAAC,YAAD;GAAmB;GAAc;GAAe;EAAS,CAAA,CACtD;;AAET"}
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export { Button, buttonVariants } from './button.js';
|
|
|
5
5
|
export type { Emphasis, Variant } from './button.js';
|
|
6
6
|
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './card.js';
|
|
7
7
|
export { Checkbox } from './checkbox.js';
|
|
8
|
+
export { CodeBlock } from './code-block.js';
|
|
9
|
+
export type { CodeBlockLanguage, CodeBlockLanguageLoader, CodeBlockProps } from './code-block.js';
|
|
8
10
|
export { Combobox } from './combobox.js';
|
|
9
11
|
export { ConfirmDialog } from './confirm-dialog.js';
|
|
10
12
|
export { ActionTrigger, ActionTriggerProvider, useActionTrigger, useClickBehavior, } from './click-behavior.js';
|
|
@@ -18,6 +20,7 @@ export { Input } from './input.js';
|
|
|
18
20
|
export { default as InputError } from './input-error.js';
|
|
19
21
|
export { InputOTP } from './input-otp.js';
|
|
20
22
|
export { Label } from './label.js';
|
|
23
|
+
export { NativeSelect } from './native-select.js';
|
|
21
24
|
export { TextLink } from './link.js';
|
|
22
25
|
export { default as PasswordInput } from './password-input.js';
|
|
23
26
|
export { Popover, PopoverClose, PopoverContent, PopoverTrigger } from './popover.js';
|
package/dist/ui/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { Badge } from "./badge.js";
|
|
|
11
11
|
import { InfoTooltip } from "./info-tooltip.js";
|
|
12
12
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./card.js";
|
|
13
13
|
import { CopyButton, CopyableText, copyToClipboard } from "./copyable-text.js";
|
|
14
|
+
import { CodeBlock } from "./code-block.js";
|
|
14
15
|
import { PreviewableImage } from "./image-preview.js";
|
|
15
16
|
import { TextLink } from "./link.js";
|
|
16
17
|
import { SegmentedPills } from "./segmented-pills.js";
|
|
@@ -24,4 +25,5 @@ import { InputOTP } from "./input-otp.js";
|
|
|
24
25
|
import PasswordInput from "./password-input.js";
|
|
25
26
|
import { Combobox } from "./combobox.js";
|
|
26
27
|
import { Textarea } from "./textarea.js";
|
|
27
|
-
|
|
28
|
+
import { NativeSelect } from "./native-select.js";
|
|
29
|
+
export { ActionMenuProvider, ActionTrigger, ActionTriggerProvider, AffixGroup, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CodeBlock, Combobox, ConfirmDialog, CopyButton, CopyableText, Dialog, DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, InfoTooltip, Input, InputError, InputOTP, Label, NativeSelect, PasswordInput, Popover, PopoverClose, PopoverContent, PopoverTrigger, PreviewableImage, SegmentedPills, Skeleton, Spinner, TextLink, Textarea, actionMenuItemClassName, buttonVariants, copyToClipboard, uiComponents, useActionMenu, useActionTrigger, useClickBehavior };
|
package/dist/ui/plugin.js
CHANGED
|
@@ -4,6 +4,7 @@ import AvatarComponent from "./avatar.js";
|
|
|
4
4
|
import BadgeComponent from "./badge.js";
|
|
5
5
|
import CardComponent from "./card.js";
|
|
6
6
|
import ChartComponent from "./chart.js";
|
|
7
|
+
import CodeBlockComponent from "./code-block.js";
|
|
7
8
|
import CollapsibleComponent from "./collapsible.js";
|
|
8
9
|
import FloatingPanelComponent from "./floating-panel.js";
|
|
9
10
|
import FragmentComponent from "./fragment.js";
|
|
@@ -30,6 +31,7 @@ var uiComponents = createPlugin({
|
|
|
30
31
|
button: eagerComponent(ButtonComponent),
|
|
31
32
|
card: eagerComponent(CardComponent),
|
|
32
33
|
chart: eagerComponent(ChartComponent),
|
|
34
|
+
"code-block": eagerComponent(CodeBlockComponent),
|
|
33
35
|
collapsible: eagerComponent(CollapsibleComponent),
|
|
34
36
|
"floating-panel": eagerComponent(FloatingPanelComponent),
|
|
35
37
|
fragment: eagerComponent(FragmentComponent),
|
package/dist/ui/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../../resources/js/ui/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n eagerComponent,\n type ComponentRegistryFor,\n} from \"@lattice-php/lattice/core/registry\";\nimport type { FragmentNodeType, UiNodeType } from \"@lattice-php/lattice/types/generated\";\nimport AvatarComponent from \"./avatar\";\nimport BadgeComponent from \"./badge\";\nimport ButtonComponent from \"./button\";\nimport CardComponent from \"./card\";\nimport ChartComponent from \"./chart\";\nimport CollapsibleComponent from \"./collapsible\";\nimport FloatingPanelComponent from \"./floating-panel\";\nimport FragmentComponent from \"./fragment\";\nimport GridComponent from \"./grid\";\nimport HeadingComponent from \"./heading\";\nimport IconComponent from \"./icon\";\nimport ImageComponent from \"./image\";\nimport LinkComponent from \"./link\";\nimport ModalComponent from \"./modal\";\nimport ProgressComponent from \"./progress\";\nimport RawBlockComponent from \"./raw-block\";\nimport SectionComponent from \"./section\";\nimport SegmentedControlComponent from \"./segmented-control\";\nimport SeparatorComponent from \"./separator\";\nimport StackComponent from \"./stack\";\nimport TabComponent, { TabsComponent } from \"./tabs\";\nimport TextComponent from \"./text\";\nimport TooltipComponent from \"./tooltip\";\n\ntype UiComponentType = UiNodeType | FragmentNodeType;\n\nexport const uiComponents = createPlugin({\n components: {\n avatar: eagerComponent(AvatarComponent),\n badge: eagerComponent(BadgeComponent),\n button: eagerComponent(ButtonComponent),\n card: eagerComponent(CardComponent),\n chart: eagerComponent(ChartComponent),\n collapsible: eagerComponent(CollapsibleComponent),\n \"floating-panel\": eagerComponent(FloatingPanelComponent),\n fragment: eagerComponent(FragmentComponent),\n grid: eagerComponent(GridComponent),\n heading: eagerComponent(HeadingComponent),\n icon: eagerComponent(IconComponent),\n image: eagerComponent(ImageComponent),\n link: eagerComponent(LinkComponent),\n modal: eagerComponent(ModalComponent),\n progress: eagerComponent(ProgressComponent),\n \"raw-block\": eagerComponent(RawBlockComponent),\n section: eagerComponent(SectionComponent),\n \"segmented-control\": eagerComponent(SegmentedControlComponent),\n separator: eagerComponent(SeparatorComponent),\n stack: eagerComponent(StackComponent),\n tab: eagerComponent(TabComponent),\n tabs: eagerComponent(TabsComponent),\n text: eagerComponent(TextComponent),\n tooltip: eagerComponent(TooltipComponent),\n } satisfies ComponentRegistryFor<UiComponentType>,\n name: \"lattice/ui\",\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../../resources/js/ui/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n eagerComponent,\n type ComponentRegistryFor,\n} from \"@lattice-php/lattice/core/registry\";\nimport type { FragmentNodeType, UiNodeType } from \"@lattice-php/lattice/types/generated\";\nimport AvatarComponent from \"./avatar\";\nimport BadgeComponent from \"./badge\";\nimport ButtonComponent from \"./button\";\nimport CardComponent from \"./card\";\nimport ChartComponent from \"./chart\";\nimport CodeBlockComponent from \"./code-block\";\nimport CollapsibleComponent from \"./collapsible\";\nimport FloatingPanelComponent from \"./floating-panel\";\nimport FragmentComponent from \"./fragment\";\nimport GridComponent from \"./grid\";\nimport HeadingComponent from \"./heading\";\nimport IconComponent from \"./icon\";\nimport ImageComponent from \"./image\";\nimport LinkComponent from \"./link\";\nimport ModalComponent from \"./modal\";\nimport ProgressComponent from \"./progress\";\nimport RawBlockComponent from \"./raw-block\";\nimport SectionComponent from \"./section\";\nimport SegmentedControlComponent from \"./segmented-control\";\nimport SeparatorComponent from \"./separator\";\nimport StackComponent from \"./stack\";\nimport TabComponent, { TabsComponent } from \"./tabs\";\nimport TextComponent from \"./text\";\nimport TooltipComponent from \"./tooltip\";\n\ntype UiComponentType = UiNodeType | FragmentNodeType;\n\nexport const uiComponents = createPlugin({\n components: {\n avatar: eagerComponent(AvatarComponent),\n badge: eagerComponent(BadgeComponent),\n button: eagerComponent(ButtonComponent),\n card: eagerComponent(CardComponent),\n chart: eagerComponent(ChartComponent),\n \"code-block\": eagerComponent(CodeBlockComponent),\n collapsible: eagerComponent(CollapsibleComponent),\n \"floating-panel\": eagerComponent(FloatingPanelComponent),\n fragment: eagerComponent(FragmentComponent),\n grid: eagerComponent(GridComponent),\n heading: eagerComponent(HeadingComponent),\n icon: eagerComponent(IconComponent),\n image: eagerComponent(ImageComponent),\n link: eagerComponent(LinkComponent),\n modal: eagerComponent(ModalComponent),\n progress: eagerComponent(ProgressComponent),\n \"raw-block\": eagerComponent(RawBlockComponent),\n section: eagerComponent(SectionComponent),\n \"segmented-control\": eagerComponent(SegmentedControlComponent),\n separator: eagerComponent(SeparatorComponent),\n stack: eagerComponent(StackComponent),\n tab: eagerComponent(TabComponent),\n tabs: eagerComponent(TabsComponent),\n text: eagerComponent(TextComponent),\n tooltip: eagerComponent(TooltipComponent),\n } satisfies ComponentRegistryFor<UiComponentType>,\n name: \"lattice/ui\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAa,eAAe,aAAa;CACvC,YAAY;EACV,QAAQ,eAAe,eAAe;EACtC,OAAO,eAAe,cAAc;EACpC,QAAQ,eAAe,eAAe;EACtC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,cAAc,eAAe,kBAAkB;EAC/C,aAAa,eAAe,oBAAoB;EAChD,kBAAkB,eAAe,sBAAsB;EACvD,UAAU,eAAe,iBAAiB;EAC1C,MAAM,eAAe,aAAa;EAClC,SAAS,eAAe,gBAAgB;EACxC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,MAAM,eAAe,aAAa;EAClC,OAAO,eAAe,cAAc;EACpC,UAAU,eAAe,iBAAiB;EAC1C,aAAa,eAAe,iBAAiB;EAC7C,SAAS,eAAe,gBAAgB;EACxC,qBAAqB,eAAe,yBAAyB;EAC7D,WAAW,eAAe,kBAAkB;EAC5C,OAAO,eAAe,cAAc;EACpC,KAAK,eAAe,YAAY;EAChC,MAAM,eAAe,aAAa;EAClC,MAAM,eAAe,aAAa;EAClC,SAAS,eAAe,gBAAgB;CAC1C;CACA,MAAM;AACR,CAAC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { ComponentProps } from 'react';
|
|
1
2
|
import { Option } from '../core/types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Presentational segmented pill group. Used by the form choice field (bound to a
|
|
4
5
|
* form value) and the core segmented control (standalone, emits an event).
|
|
5
6
|
*/
|
|
6
|
-
export declare function SegmentedPills({ ariaLabel, autoFocus, disabled, name, onSelect, options, tabIndex, value, }: {
|
|
7
|
+
export declare function SegmentedPills({ ariaLabel, autoFocus, className, disabled, name, onSelect, options, tabIndex, value, ...props }: Omit<ComponentProps<"div">, "children" | "onSelect" | "ref"> & {
|
|
7
8
|
ariaLabel?: string;
|
|
8
9
|
autoFocus?: boolean;
|
|
9
10
|
disabled?: boolean;
|
|
@@ -7,7 +7,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
7
7
|
* Presentational segmented pill group. Used by the form choice field (bound to a
|
|
8
8
|
* form value) and the core segmented control (standalone, emits an event).
|
|
9
9
|
*/
|
|
10
|
-
function SegmentedPills({ ariaLabel, autoFocus = false, disabled = false, name, onSelect, options, tabIndex, value }) {
|
|
10
|
+
function SegmentedPills({ ariaLabel, autoFocus = false, className, disabled = false, name, onSelect, options, tabIndex, value, ...props }) {
|
|
11
11
|
const groupRef = useRef(null);
|
|
12
12
|
useEffect(() => {
|
|
13
13
|
if (!autoFocus) return;
|
|
@@ -15,8 +15,9 @@ function SegmentedPills({ ariaLabel, autoFocus = false, disabled = false, name,
|
|
|
15
15
|
(group?.querySelector("button[aria-checked=\"true\"]") ?? group?.querySelector("button"))?.focus();
|
|
16
16
|
}, [autoFocus]);
|
|
17
17
|
return /* @__PURE__ */ jsx("div", {
|
|
18
|
+
...props,
|
|
18
19
|
"aria-label": ariaLabel,
|
|
19
|
-
className: "inline-flex w-fit max-w-full gap-1 overflow-x-auto rounded-lt bg-lt-muted p-1",
|
|
20
|
+
className: cn("inline-flex w-fit max-w-full gap-1 overflow-x-auto rounded-lt bg-lt-muted p-1", className),
|
|
20
21
|
ref: groupRef,
|
|
21
22
|
role: "radiogroup",
|
|
22
23
|
children: options.map((option) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"segmented-pills.js","names":[],"sources":["../../resources/js/ui/segmented-pills.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport type { Option } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { pillClassName } from \"./pill\";\n\n/**\n * Presentational segmented pill group. Used by the form choice field (bound to a\n * form value) and the core segmented control (standalone, emits an event).\n */\nexport function SegmentedPills({\n ariaLabel,\n autoFocus = false,\n disabled = false,\n name,\n onSelect,\n options,\n tabIndex,\n value,\n}: {\n ariaLabel?: string;\n autoFocus?: boolean;\n disabled?: boolean;\n name: string;\n onSelect: (value: string) => void;\n options: Option[];\n tabIndex?: number;\n value: string;\n}) {\n const groupRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!autoFocus) {\n return;\n }\n\n const group = groupRef.current;\n const target =\n group?.querySelector<HTMLButtonElement>('button[aria-checked=\"true\"]') ??\n group?.querySelector<HTMLButtonElement>(\"button\");\n\n target?.focus();\n }, [autoFocus]);\n\n return (\n <div\n aria-label={ariaLabel}\n className
|
|
1
|
+
{"version":3,"file":"segmented-pills.js","names":[],"sources":["../../resources/js/ui/segmented-pills.tsx"],"sourcesContent":["import { type ComponentProps, useEffect, useRef } from \"react\";\nimport type { Option } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { pillClassName } from \"./pill\";\n\n/**\n * Presentational segmented pill group. Used by the form choice field (bound to a\n * form value) and the core segmented control (standalone, emits an event).\n */\nexport function SegmentedPills({\n ariaLabel,\n autoFocus = false,\n className,\n disabled = false,\n name,\n onSelect,\n options,\n tabIndex,\n value,\n ...props\n}: Omit<ComponentProps<\"div\">, \"children\" | \"onSelect\" | \"ref\"> & {\n ariaLabel?: string;\n autoFocus?: boolean;\n disabled?: boolean;\n name: string;\n onSelect: (value: string) => void;\n options: Option[];\n tabIndex?: number;\n value: string;\n}) {\n const groupRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!autoFocus) {\n return;\n }\n\n const group = groupRef.current;\n const target =\n group?.querySelector<HTMLButtonElement>('button[aria-checked=\"true\"]') ??\n group?.querySelector<HTMLButtonElement>(\"button\");\n\n target?.focus();\n }, [autoFocus]);\n\n return (\n <div\n {...props}\n aria-label={ariaLabel}\n className={cn(\n \"inline-flex w-fit max-w-full gap-1 overflow-x-auto rounded-lt bg-lt-muted p-1\",\n className,\n )}\n ref={groupRef}\n role=\"radiogroup\"\n >\n {options.map((option) => {\n const isSelected = value === option.value;\n\n return (\n <button\n aria-checked={isSelected}\n data-test={`${name}-${option.value}`}\n className={cn(pillClassName(isSelected), disabled && \"cursor-not-allowed opacity-60\")}\n disabled={disabled}\n key={option.value}\n onClick={() => onSelect(option.value)}\n role=\"radio\"\n tabIndex={tabIndex}\n type=\"button\"\n >\n {option.label}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,eAAe,EAC7B,WACA,YAAY,OACZ,WACA,WAAW,OACX,MACA,UACA,SACA,UACA,OACA,GAAG,SAUF;CACD,MAAM,WAAW,OAAuB,IAAI;CAE5C,gBAAgB;EACd,IAAI,CAAC,WACH;EAGF,MAAM,QAAQ,SAAS;EAKvB,CAHE,OAAO,cAAiC,+BAA6B,KACrE,OAAO,cAAiC,QAAQ,EAAA,EAE1C,MAAM;CAChB,GAAG,CAAC,SAAS,CAAC;CAEd,OACE,oBAAC,OAAD;EACE,GAAI;EACJ,cAAY;EACZ,WAAW,GACT,iFACA,SACF;EACA,KAAK;EACL,MAAK;EAEJ,UAAA,QAAQ,KAAK,WAAW;GACvB,MAAM,aAAa,UAAU,OAAO;GAEpC,OACE,oBAAC,UAAD;IACE,gBAAc;IACd,aAAW,GAAG,KAAK,GAAG,OAAO;IAC7B,WAAW,GAAG,cAAc,UAAU,GAAG,YAAY,+BAA+B;IAC1E;IAEV,eAAe,SAAS,OAAO,KAAK;IACpC,MAAK;IACK;IACV,MAAK;IAEJ,UAAA,OAAO;GACF,GAPD,OAAO,KAON;EAEZ,CAAC;CACE,CAAA;AAET"}
|