@surflane/ui 0.8.0 → 0.10.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/README.md +5 -5
- package/dist/accordion.d.ts +33 -17
- package/dist/accordion.js +10 -11
- package/dist/alert-dialog.d.ts +8 -4
- package/dist/alert-dialog.js +3 -4
- package/dist/app-shell.d.ts +81 -44
- package/dist/app-shell.js +67 -29
- package/dist/autocomplete.d.ts +117 -61
- package/dist/autocomplete.js +8 -8
- package/dist/avatar.d.ts +34 -17
- package/dist/avatar.js +8 -9
- package/dist/badge.d.ts +6 -10
- package/dist/badge.js +5 -6
- package/dist/bottom-navigation.d.ts +11 -41
- package/dist/bottom-navigation.js +9 -10
- package/dist/breadcrumbs.d.ts +21 -5
- package/dist/breadcrumbs.js +12 -14
- package/dist/button.d.ts +15 -8
- package/dist/button.js +8 -9
- package/dist/card.d.ts +610 -35
- package/dist/card.js +12 -12
- package/dist/chart.d.ts +9 -32
- package/dist/chart.js +65 -26
- package/dist/checkbox-group.d.ts +4 -1
- package/dist/checkbox-group.js +3 -4
- package/dist/checkbox.d.ts +13 -4
- package/dist/checkbox.js +6 -6
- package/dist/choice-label.d.ts +3 -11
- package/dist/choice-label.js +4 -4
- package/dist/class-names.d.ts +6 -0
- package/dist/class-names.js +18 -0
- package/dist/code.d.ts +13 -10
- package/dist/code.js +8 -5
- package/dist/collapsible.d.ts +25 -12
- package/dist/collapsible.js +7 -8
- package/dist/combobox.d.ts +145 -73
- package/dist/combobox.js +27 -27
- package/dist/context-menu.d.ts +106 -54
- package/dist/context-menu.js +4 -5
- package/dist/css/accordion.css +1 -1
- package/dist/css/alert-dialog.css +2 -2
- package/dist/css/all.css +7 -5
- package/dist/css/app-shell.css +1 -1
- package/dist/css/autocomplete.css +1 -1
- package/dist/css/avatar.css +2 -2
- package/dist/css/badge.css +2 -2
- package/dist/css/bottom-navigation.css +1 -1
- package/dist/css/breadcrumbs.css +3 -3
- package/dist/css/button.css +1 -1
- package/dist/css/card.css +3 -3
- package/dist/css/chart.css +2 -2
- package/dist/css/checkbox-group.css +1 -1
- package/dist/css/checkbox.css +1 -1
- package/dist/css/choice-label.css +1 -1
- package/dist/css/code.css +2 -2
- package/dist/css/collapsible.css +1 -1
- package/dist/css/combobox.css +1 -1
- package/dist/css/context-menu.css +1 -1
- package/dist/css/date-picker.css +1 -1
- package/dist/css/dialog.css +2 -2
- package/dist/css/drawer.css +2 -2
- package/dist/css/empty-state.css +2 -2
- package/dist/css/field.css +1 -1
- package/dist/css/fieldset.css +1 -1
- package/dist/css/file-upload.css +2 -2
- package/dist/css/filter-bar.css +1 -1
- package/dist/css/form.css +1 -1
- package/dist/css/icon.css +1 -1
- package/dist/css/inline-message.css +2 -2
- package/dist/css/input.css +1 -1
- package/dist/css/layout.css +2 -2
- package/dist/css/link.css +2 -2
- package/dist/css/list.css +2 -2
- package/dist/css/markdown.css +4 -4
- package/dist/css/menu.css +1 -1
- package/dist/css/menubar.css +1 -1
- package/dist/css/meter.css +1 -1
- package/dist/css/native-select.css +1 -1
- package/dist/css/navigation-menu.css +1 -1
- package/dist/css/number-field.css +1 -1
- package/dist/css/otp-field.css +1 -1
- package/dist/css/page-header.css +3 -3
- package/dist/css/pagination.css +2 -2
- package/dist/css/popover.css +1 -1
- package/dist/css/preview-card.css +2 -2
- package/dist/css/progress.css +1 -1
- package/dist/css/radio-group.css +1 -1
- package/dist/css/rules/accordion.css +5 -5
- package/dist/css/rules/alert-dialog.css +9 -9
- package/dist/css/rules/app-shell.css +274 -82
- package/dist/css/rules/avatar.css +7 -7
- package/dist/css/rules/badge.css +6 -6
- package/dist/css/rules/bar.css +11 -11
- package/dist/css/rules/base.css +30 -30
- package/dist/css/rules/bottom-navigation.css +57 -56
- package/dist/css/rules/breadcrumbs.css +2 -2
- package/dist/css/rules/button.css +66 -65
- package/dist/css/rules/card.css +3 -3
- package/dist/css/rules/chart.css +134 -124
- package/dist/css/rules/checkbox.css +5 -5
- package/dist/css/rules/choice-label.css +19 -18
- package/dist/css/rules/choice.css +65 -60
- package/dist/css/rules/code.css +2 -2
- package/dist/css/rules/collapsible.css +2 -2
- package/dist/css/rules/combobox.css +87 -83
- package/dist/css/rules/context-menu.css +1 -1
- package/dist/css/rules/date-picker.css +150 -144
- package/dist/css/rules/dialog.css +85 -79
- package/dist/css/rules/disclosure.css +49 -47
- package/dist/css/rules/drawer.css +6 -6
- package/dist/css/rules/empty-state.css +2 -2
- package/dist/css/rules/field-control.css +51 -42
- package/dist/css/rules/field.css +26 -24
- package/dist/css/rules/fieldset.css +8 -8
- package/dist/css/rules/file-upload.css +73 -72
- package/dist/css/rules/filter-bar.css +19 -18
- package/dist/css/rules/form.css +3 -3
- package/dist/css/rules/gauge.css +22 -22
- package/dist/css/rules/icon.css +16 -11
- package/dist/css/rules/inline-message.css +3 -3
- package/dist/css/rules/input-group.css +44 -43
- package/dist/css/rules/input.css +11 -11
- package/dist/css/rules/item.css +7 -7
- package/dist/css/rules/link.css +2 -2
- package/dist/css/rules/list.css +3 -3
- package/dist/css/rules/markdown.css +27 -27
- package/dist/css/rules/menu.css +45 -42
- package/dist/css/rules/menubar.css +6 -6
- package/dist/css/rules/navigation-menu.css +69 -66
- package/dist/css/rules/number-field.css +22 -21
- package/dist/css/rules/otp-field.css +28 -27
- package/dist/css/rules/panel.css +105 -100
- package/dist/css/rules/popover.css +23 -20
- package/dist/css/rules/popup.css +15 -14
- package/dist/css/rules/preview-card.css +20 -19
- package/dist/css/rules/progress.css +3 -3
- package/dist/css/rules/radio-group.css +8 -8
- package/dist/css/rules/scroll-area.css +50 -39
- package/dist/css/rules/select.css +39 -36
- package/dist/css/rules/sizing.css +119 -116
- package/dist/css/rules/skeleton.css +11 -11
- package/dist/css/rules/slider.css +43 -43
- package/dist/css/rules/sparkline.css +21 -21
- package/dist/css/rules/spinner.css +9 -9
- package/dist/css/rules/stat.css +32 -32
- package/dist/css/rules/switch.css +28 -21
- package/dist/css/rules/syntax-highlighter.css +37 -36
- package/dist/css/rules/table.css +10 -10
- package/dist/css/rules/tabs.css +53 -52
- package/dist/css/rules/textarea.css +9 -7
- package/dist/css/rules/toast.css +94 -90
- package/dist/css/rules/toggle.css +12 -12
- package/dist/css/rules/toolbar.css +27 -27
- package/dist/css/rules/tooltip.css +14 -14
- package/dist/css/rules/tree.css +38 -38
- package/dist/css/scroll-area.css +1 -1
- package/dist/css/select.css +1 -1
- package/dist/css/separator.css +2 -2
- package/dist/css/sheet.css +2 -2
- package/dist/css/sizing.css +1 -1
- package/dist/css/skeleton.css +1 -1
- package/dist/css/slider.css +1 -1
- package/dist/css/sparkline.css +1 -1
- package/dist/css/spinner.css +1 -1
- package/dist/css/stat.css +1 -1
- package/dist/css/styles.css +17 -17
- package/dist/css/switch.css +1 -1
- package/dist/css/syntax-highlighter.css +1 -1
- package/dist/css/table.css +2 -2
- package/dist/css/tabs.css +1 -1
- package/dist/css/textarea.css +1 -1
- package/dist/css/toast.css +1 -1
- package/dist/css/toggle-group.css +1 -1
- package/dist/css/toggle.css +1 -1
- package/dist/css/toolbar.css +1 -1
- package/dist/css/tooltip.css +1 -1
- package/dist/css/tree.css +1 -1
- package/dist/css/typography.css +2 -2
- package/dist/date-picker.js +28 -8
- package/dist/dialog.d.ts +20 -8
- package/dist/dialog.js +11 -12
- package/dist/drawer.d.ts +12 -4
- package/dist/drawer.js +5 -5
- package/dist/element-part.d.ts +8 -0
- package/dist/element-part.js +21 -0
- package/dist/empty-state.d.ts +602 -24
- package/dist/empty-state.js +9 -9
- package/dist/field.d.ts +60 -36
- package/dist/field.js +6 -6
- package/dist/fieldset.d.ts +19 -11
- package/dist/fieldset.js +2 -2
- package/dist/file-upload.d.ts +8 -23
- package/dist/file-upload.js +21 -16
- package/dist/filter-bar.d.ts +30 -10
- package/dist/filter-bar.js +16 -18
- package/dist/form.js +1 -1
- package/dist/icon.d.ts +9 -2
- package/dist/icon.js +6 -6
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -3
- package/dist/inline-message.d.ts +51 -27
- package/dist/inline-message.js +10 -11
- package/dist/input.d.ts +4 -2
- package/dist/input.js +3 -4
- package/dist/layout.d.ts +24 -12
- package/dist/layout.js +27 -27
- package/dist/link.d.ts +3 -1
- package/dist/link.js +3 -4
- package/dist/list.d.ts +43 -27
- package/dist/list.js +12 -13
- package/dist/markdown.d.ts +14 -6
- package/dist/markdown.js +28 -11
- package/dist/menu.d.ts +113 -57
- package/dist/menu.js +24 -25
- package/dist/menubar.d.ts +5 -3
- package/dist/menubar.js +1 -1
- package/dist/meter.d.ts +46 -26
- package/dist/meter.js +5 -5
- package/dist/native-select.d.ts +4 -2
- package/dist/native-select.js +2 -2
- package/dist/navigation-menu.d.ts +76 -40
- package/dist/navigation-menu.js +15 -16
- package/dist/number-field.d.ts +44 -19
- package/dist/number-field.js +12 -13
- package/dist/otp-field.d.ts +26 -9
- package/dist/otp-field.js +10 -11
- package/dist/page-header.js +3 -4
- package/dist/pagination.d.ts +11 -8
- package/dist/pagination.js +4 -5
- package/dist/panel.d.ts +3 -1
- package/dist/panel.js +5 -6
- package/dist/parts.d.ts +5 -7
- package/dist/parts.js +8 -16
- package/dist/popover.d.ts +20 -12
- package/dist/popover.js +3 -3
- package/dist/preview-card.d.ts +22 -10
- package/dist/preview-card.js +7 -8
- package/dist/progress.d.ts +46 -26
- package/dist/progress.js +5 -5
- package/dist/radio-group.d.ts +16 -8
- package/dist/radio-group.js +5 -5
- package/dist/scroll-area.d.ts +4 -2
- package/dist/scroll-area.js +6 -6
- package/dist/select.d.ts +3 -1
- package/dist/select.js +5 -5
- package/dist/separator.d.ts +5 -2
- package/dist/separator.js +3 -4
- package/dist/sheet.d.ts +12 -4
- package/dist/sheet.js +5 -5
- package/dist/size.d.ts +7 -0
- package/dist/size.js +9 -0
- package/dist/sizing.d.ts +8 -3
- package/dist/sizing.js +8 -10
- package/dist/skeleton.d.ts +3 -3
- package/dist/skeleton.js +4 -6
- package/dist/slider.d.ts +55 -31
- package/dist/slider.js +7 -7
- package/dist/sparkline.d.ts +5 -20
- package/dist/sparkline.js +5 -7
- package/dist/spinner.d.ts +4 -6
- package/dist/spinner.js +7 -8
- package/dist/stat.d.ts +34 -42
- package/dist/stat.js +9 -10
- package/dist/status.js +0 -1
- package/dist/switch.d.ts +17 -8
- package/dist/switch.js +4 -5
- package/dist/syntax-context.d.ts +11 -2
- package/dist/syntax-context.js +34 -1
- package/dist/syntax-engine.js +9 -1
- package/dist/syntax-highlighter.d.ts +3 -7
- package/dist/syntax-highlighter.js +37 -26
- package/dist/syntax-highlighting.d.ts +14 -1
- package/dist/syntax-highlighting.js +54 -3
- package/dist/table.d.ts +111 -75
- package/dist/table.js +32 -33
- package/dist/tabs.d.ts +46 -26
- package/dist/tabs.js +5 -5
- package/dist/textarea.d.ts +4 -2
- package/dist/textarea.js +3 -4
- package/dist/toast.d.ts +76 -42
- package/dist/toast.js +28 -28
- package/dist/toggle-group.js +1 -1
- package/dist/toggle.js +1 -1
- package/dist/tone-transition.d.ts +1 -1
- package/dist/tone-transition.js +9 -8
- package/dist/toolbar.d.ts +55 -31
- package/dist/toolbar.js +6 -6
- package/dist/tooltip.d.ts +4 -1
- package/dist/tooltip.js +2 -2
- package/dist/tree.d.ts +12 -1
- package/dist/tree.js +13 -9
- package/dist/typography.d.ts +3 -1
- package/dist/typography.js +6 -8
- package/package.json +6 -5
package/dist/slider.js
CHANGED
|
@@ -6,7 +6,7 @@ import { useResetValue, useRootRef } from './form-state.js';
|
|
|
6
6
|
function SliderRoot({ value, defaultValue, onValueChange, ref, className, form, thumbAlignment = 'edge', ...props }) {
|
|
7
7
|
const [element, attach] = useRootRef(ref);
|
|
8
8
|
const [selected, setSelected] = useResetValue(value, (defaultValue ?? props.min ?? 0), element, form);
|
|
9
|
-
return (_jsx(BaseSlider.Root, { ...props, form: form, ref: attach, value: selected, thumbAlignment: thumbAlignment, className: skin('
|
|
9
|
+
return (_jsx(BaseSlider.Root, { ...props, form: form, ref: attach, value: selected, thumbAlignment: thumbAlignment, className: skin('surf-slider', className), onValueChange: (next, event) => {
|
|
10
10
|
onValueChange?.(next, event);
|
|
11
11
|
if (value === undefined && !event.isCanceled)
|
|
12
12
|
setSelected(next);
|
|
@@ -15,12 +15,12 @@ function SliderRoot({ value, defaultValue, onValueChange, ref, className, form,
|
|
|
15
15
|
export const Slider = {
|
|
16
16
|
...BaseSlider,
|
|
17
17
|
Root: SliderRoot,
|
|
18
|
-
Label: /* @__PURE__ */ part(BaseSlider.Label, '
|
|
19
|
-
Value: /* @__PURE__ */ part(BaseSlider.Value, '
|
|
20
|
-
Control: /* @__PURE__ */ part(BaseSlider.Control, '
|
|
21
|
-
Track: /* @__PURE__ */ part(BaseSlider.Track, '
|
|
22
|
-
Indicator: /* @__PURE__ */ part(BaseSlider.Indicator, '
|
|
23
|
-
Thumb: /* @__PURE__ */ part(BaseSlider.Thumb, '
|
|
18
|
+
Label: /* @__PURE__ */ part(BaseSlider.Label, 'surf-slider-label'),
|
|
19
|
+
Value: /* @__PURE__ */ part(BaseSlider.Value, 'surf-slider-value'),
|
|
20
|
+
Control: /* @__PURE__ */ part(BaseSlider.Control, 'surf-slider-control'),
|
|
21
|
+
Track: /* @__PURE__ */ part(BaseSlider.Track, 'surf-slider-track'),
|
|
22
|
+
Indicator: /* @__PURE__ */ part(BaseSlider.Indicator, 'surf-slider-indicator'),
|
|
23
|
+
Thumb: /* @__PURE__ */ part(BaseSlider.Thumb, 'surf-slider-thumb'),
|
|
24
24
|
};
|
|
25
25
|
// Flat names for React Server Components, which cannot dot into client exports.
|
|
26
26
|
export { SliderRoot };
|
package/dist/sparkline.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type { ComponentSizeAlias } from './
|
|
2
|
+
import type { ComponentSizeAlias } from './size.js';
|
|
3
3
|
export type SparklineTrend = 'auto' | 'up' | 'down' | 'flat' | 'neutral';
|
|
4
4
|
export type SparklineProps = Omit<React.ComponentPropsWithoutRef<'svg'>, 'children' | 'role' | 'aria-label'> & {
|
|
5
5
|
/** Values in order. A missing value leaves a gap. */
|
|
@@ -7,7 +7,7 @@ export type SparklineProps = Omit<React.ComponentPropsWithoutRef<'svg'>, 'childr
|
|
|
7
7
|
/** What the line shows and how it moved, such as "KOSPI, 30 days, up 2.3%". The
|
|
8
8
|
* line is an image; this is its name. */
|
|
9
9
|
label: string;
|
|
10
|
-
/** 24, 32 or 48px tall. Set --sparkline-height for another height. */
|
|
10
|
+
/** 24, 32 or 48px tall. Set --surf-sparkline-height for another height. */
|
|
11
11
|
size?: ComponentSizeAlias;
|
|
12
12
|
/** A reference line: a value, the first value, or none. */
|
|
13
13
|
baseline?: number | 'first' | 'none';
|
|
@@ -21,21 +21,6 @@ export type SparklineProps = Omit<React.ComponentPropsWithoutRef<'svg'>, 'childr
|
|
|
21
21
|
};
|
|
22
22
|
/** A small trend line for cards and table cells: no axes, legend or tooltip. Use Chart
|
|
23
23
|
* when people need to read values. */
|
|
24
|
-
export declare const Sparkline:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/** What the line shows and how it moved, such as "KOSPI, 30 days, up 2.3%". The
|
|
28
|
-
* line is an image; this is its name. */
|
|
29
|
-
label: string;
|
|
30
|
-
/** 24, 32 or 48px tall. Set --sparkline-height for another height. */
|
|
31
|
-
size?: ComponentSizeAlias;
|
|
32
|
-
/** A reference line: a value, the first value, or none. */
|
|
33
|
-
baseline?: number | "first" | "none";
|
|
34
|
-
/** Marks the last value with a dot. */
|
|
35
|
-
marker?: boolean;
|
|
36
|
-
/** The line's color. auto compares the last value with the baseline, or with the
|
|
37
|
-
* first value when there is none. */
|
|
38
|
-
trend?: SparklineTrend;
|
|
39
|
-
/** For measures where a fall is good: the colors swap. */
|
|
40
|
-
lowerIsBetter?: boolean;
|
|
41
|
-
} & React.RefAttributes<SVGSVGElement>>;
|
|
24
|
+
export declare const Sparkline: ({ data, label, size, baseline, marker, trend, lowerIsBetter, className, ref, ...props }: SparklineProps & {
|
|
25
|
+
ref?: React.Ref<SVGSVGElement>;
|
|
26
|
+
}) => React.JSX.Element;
|
package/dist/sparkline.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import
|
|
4
|
-
import { cx, sizeName } from './parts.js';
|
|
2
|
+
import { cx, sizeName } from './class-names.js';
|
|
5
3
|
const finite = (value) => typeof value === 'number' && Number.isFinite(value);
|
|
6
4
|
/** A small trend line for cards and table cells: no axes, legend or tooltip. Use Chart
|
|
7
5
|
* when people need to read values. */
|
|
8
|
-
export const Sparkline =
|
|
6
|
+
export const Sparkline = function Sparkline({ data, label, size, baseline = 'none', marker = true, trend = 'auto', lowerIsBetter = false, className, ref, ...props }) {
|
|
9
7
|
const values = data.filter(finite);
|
|
10
8
|
const first = values[0];
|
|
11
9
|
const last = values[values.length - 1];
|
|
@@ -43,8 +41,8 @@ export const Sparkline = /* @__PURE__ */ React.forwardRef(function Sparkline({ d
|
|
|
43
41
|
let end = data.length - 1;
|
|
44
42
|
while (end >= 0 && !finite(data[end]))
|
|
45
43
|
end--;
|
|
46
|
-
return (_jsxs("svg", { ...props, ref: ref, role: 'img', "aria-label": label, className: cx('
|
|
44
|
+
return (_jsxs("svg", { ...props, ref: ref, role: 'img', "aria-label": label, className: cx('surf-sparkline', className), "data-size": sizeName(size), "data-tone": tone, viewBox: '0 0 100 100', preserveAspectRatio: 'none', focusable: 'false', children: [finite(reference) && (_jsx("path", { className: 'surf-sparkline-baseline', d: `M0 ${y(reference)}H100`, vectorEffect: 'non-scaling-stroke' })), _jsx("path", { className: 'surf-sparkline-line', d: path, vectorEffect: 'non-scaling-stroke' }), marker && end >= 0 && (
|
|
47
45
|
// A zero-length segment with a round cap stays a circle however the
|
|
48
46
|
// line stretches.
|
|
49
|
-
_jsx("path", { className: '
|
|
50
|
-
}
|
|
47
|
+
_jsx("path", { className: 'surf-sparkline-marker', d: `M${x(end)} ${y(data[end])}h0`, vectorEffect: 'non-scaling-stroke' }))] }));
|
|
48
|
+
};
|
package/dist/spinner.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type ComponentSizeAlias } from './
|
|
2
|
+
import { type ComponentSizeAlias } from './size.js';
|
|
3
3
|
export type SpinnerProps = Omit<React.ComponentPropsWithoutRef<'span'>, 'children'> & {
|
|
4
4
|
label?: string;
|
|
5
5
|
decorative?: boolean;
|
|
6
6
|
size?: ComponentSizeAlias;
|
|
7
7
|
};
|
|
8
8
|
/** Use decorative within a loading region that already owns the announcement. */
|
|
9
|
-
export declare const Spinner:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
size?: ComponentSizeAlias;
|
|
13
|
-
} & React.RefAttributes<HTMLSpanElement>>;
|
|
9
|
+
export declare const Spinner: ({ label, decorative, size, className, ref, ...props }: SpinnerProps & {
|
|
10
|
+
ref?: React.Ref<HTMLSpanElement>;
|
|
11
|
+
}) => React.JSX.Element;
|
package/dist/spinner.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { sizeName, cx } from './parts.js';
|
|
2
|
+
import { normalizeSize } from './size.js';
|
|
3
|
+
import { sizeName, cx } from './class-names.js';
|
|
6
4
|
/** Use decorative within a loading region that already owns the announcement. */
|
|
7
|
-
export const Spinner =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
});
|
|
5
|
+
export const Spinner = function Spinner({ label = 'Loading', decorative = false, size, className, ref, ...props }) {
|
|
6
|
+
// An omitted size follows the surrounding size scope through CSS.
|
|
7
|
+
const scheme = normalizeSize(size);
|
|
8
|
+
return (_jsxs("span", { ...props, ref: ref, className: cx('surf-spinner', className), "data-size": sizeName(size), "data-ui-size": scheme, role: decorative ? undefined : 'status', "aria-hidden": decorative || undefined, children: [_jsxs("svg", { viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', "aria-hidden": 'true', children: [_jsx("circle", { cx: '12', cy: '12', r: '9', opacity: '.2' }), _jsx("path", { d: 'M12 3a9 9 0 0 1 9 9', strokeLinecap: 'round' })] }), !decorative && _jsx("span", { className: 'surf-feedback-sr', children: label })] }));
|
|
9
|
+
};
|
package/dist/stat.d.ts
CHANGED
|
@@ -14,46 +14,38 @@ export type StatChangeProps = Omit<React.ComponentPropsWithoutRef<'dd'>, 'childr
|
|
|
14
14
|
/** A key figure: what it measures, its value, how it moved and what it covers. Renders
|
|
15
15
|
* a description list, so the label names the value for assistive technology. */
|
|
16
16
|
export declare const Stat: {
|
|
17
|
-
Root:
|
|
18
|
-
size?: import("./
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} & React.RefAttributes<HTMLElement>>;
|
|
37
|
-
Caption: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "size"> & {
|
|
38
|
-
size?: import("./sizing.js").ComponentSize;
|
|
39
|
-
} & React.RefAttributes<HTMLElement>>;
|
|
17
|
+
Root: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDListElement>, HTMLDListElement>, "ref">, "size"> & {
|
|
18
|
+
size?: import("./size.js").ComponentSize;
|
|
19
|
+
ref?: React.Ref<HTMLDListElement> | undefined;
|
|
20
|
+
}) => React.JSX.Element;
|
|
21
|
+
Label: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "size"> & {
|
|
22
|
+
size?: import("./size.js").ComponentSize;
|
|
23
|
+
ref?: React.Ref<HTMLElement> | undefined;
|
|
24
|
+
}) => React.JSX.Element;
|
|
25
|
+
Value: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "size"> & {
|
|
26
|
+
size?: import("./size.js").ComponentSize;
|
|
27
|
+
ref?: React.Ref<HTMLElement> | undefined;
|
|
28
|
+
}) => React.JSX.Element;
|
|
29
|
+
Change: ({ value, format, direction, lowerIsBetter, className, ref, ...props }: StatChangeProps & {
|
|
30
|
+
ref?: React.Ref<HTMLElement>;
|
|
31
|
+
}) => React.JSX.Element;
|
|
32
|
+
Caption: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "size"> & {
|
|
33
|
+
size?: import("./size.js").ComponentSize;
|
|
34
|
+
ref?: React.Ref<HTMLElement> | undefined;
|
|
35
|
+
}) => React.JSX.Element;
|
|
40
36
|
};
|
|
41
|
-
export declare const StatRoot:
|
|
42
|
-
size?: import("./
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
lowerIsBetter?: boolean;
|
|
57
|
-
} & React.RefAttributes<HTMLElement>>, StatCaption: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "size"> & {
|
|
58
|
-
size?: import("./sizing.js").ComponentSize;
|
|
59
|
-
} & React.RefAttributes<HTMLElement>>;
|
|
37
|
+
export declare const StatRoot: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDListElement>, HTMLDListElement>, "ref">, "size"> & {
|
|
38
|
+
size?: import("./size.js").ComponentSize;
|
|
39
|
+
ref?: React.Ref<HTMLDListElement> | undefined;
|
|
40
|
+
}) => React.JSX.Element, StatLabel: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "size"> & {
|
|
41
|
+
size?: import("./size.js").ComponentSize;
|
|
42
|
+
ref?: React.Ref<HTMLElement> | undefined;
|
|
43
|
+
}) => React.JSX.Element, StatValue: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "size"> & {
|
|
44
|
+
size?: import("./size.js").ComponentSize;
|
|
45
|
+
ref?: React.Ref<HTMLElement> | undefined;
|
|
46
|
+
}) => React.JSX.Element, StatChange: ({ value, format, direction, lowerIsBetter, className, ref, ...props }: StatChangeProps & {
|
|
47
|
+
ref?: React.Ref<HTMLElement>;
|
|
48
|
+
}) => React.JSX.Element, StatCaption: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "size"> & {
|
|
49
|
+
size?: import("./size.js").ComponentSize;
|
|
50
|
+
ref?: React.Ref<HTMLElement> | undefined;
|
|
51
|
+
}) => React.JSX.Element;
|
package/dist/stat.js
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import * as React from 'react';
|
|
4
2
|
import { Icon } from './icon.js';
|
|
5
|
-
import {
|
|
3
|
+
import { cx } from './class-names.js';
|
|
4
|
+
import { elementPart } from './element-part.js';
|
|
6
5
|
import { formatPercent } from './format.js';
|
|
7
6
|
const signed = (value) => formatPercent(value, { signed: true });
|
|
8
7
|
const arrows = { up: 'arrow-up', down: 'arrow-down', flat: 'minus' };
|
|
9
8
|
/** A change against an earlier value: an arrow, a signed figure and its direction's
|
|
10
9
|
* color. The sign and the arrow carry the direction, so color is never alone. */
|
|
11
|
-
const ChangePart =
|
|
10
|
+
const ChangePart = function StatChange({ value, format = signed, direction, lowerIsBetter, className, ref, ...props }) {
|
|
12
11
|
const way = direction ?? (value > 0 ? 'up' : value < 0 ? 'down' : 'flat');
|
|
13
12
|
const tone = way === 'flat' || !lowerIsBetter ? way : way === 'up' ? 'down' : 'up';
|
|
14
|
-
return (_jsxs("dd", { ...props, ref: ref, "data-direction": way, "data-tone": tone, className: cx('
|
|
15
|
-
}
|
|
13
|
+
return (_jsxs("dd", { ...props, ref: ref, "data-direction": way, "data-tone": tone, className: cx('surf-stat-change', className), children: [_jsx(Icon, { name: arrows[way] }), format(value)] }));
|
|
14
|
+
};
|
|
16
15
|
/** A key figure: what it measures, its value, how it moved and what it covers. Renders
|
|
17
16
|
* a description list, so the label names the value for assistive technology. */
|
|
18
17
|
export const Stat = {
|
|
19
|
-
Root: /* @__PURE__ */
|
|
20
|
-
Label: /* @__PURE__ */
|
|
21
|
-
Value: /* @__PURE__ */
|
|
18
|
+
Root: /* @__PURE__ */ elementPart('dl', 'surf-stat'),
|
|
19
|
+
Label: /* @__PURE__ */ elementPart('dt', 'surf-stat-label'),
|
|
20
|
+
Value: /* @__PURE__ */ elementPart('dd', 'surf-stat-value'),
|
|
22
21
|
Change: ChangePart,
|
|
23
|
-
Caption: /* @__PURE__ */
|
|
22
|
+
Caption: /* @__PURE__ */ elementPart('dd', 'surf-stat-caption'),
|
|
24
23
|
};
|
|
25
24
|
// Flat names for React Server Components, which cannot dot into client exports.
|
|
26
25
|
export const { Root: StatRoot, Label: StatLabel, Value: StatValue, Change: StatChange, Caption: StatCaption, } = Stat;
|
package/dist/status.js
CHANGED
package/dist/switch.d.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { Switch as BaseSwitch } from '@base-ui/react/switch';
|
|
2
3
|
import { type ChoiceSize } from './choice.js';
|
|
3
|
-
declare const SwitchRoot:
|
|
4
|
+
declare const SwitchRoot: ({ size, checked, defaultChecked, onCheckedChange, className, form, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseSwitch.Root> & ChoiceSize & {
|
|
5
|
+
ref?: React.Ref<React.ComponentRef<typeof BaseSwitch.Root>>;
|
|
6
|
+
}) => React.JSX.Element;
|
|
4
7
|
export declare const Switch: {
|
|
5
|
-
Root:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} & React.RefAttributes<HTMLSpanElement
|
|
8
|
+
Root: ({ size, checked, defaultChecked, onCheckedChange, className, form, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseSwitch.Root> & ChoiceSize & {
|
|
9
|
+
ref?: React.Ref<React.ComponentRef<typeof BaseSwitch.Root>>;
|
|
10
|
+
}) => React.JSX.Element;
|
|
11
|
+
Thumb: ({ ref, ...props }: Omit<Omit<Omit<import("@base-ui/react/switch").SwitchThumbProps, "ref"> & React.RefAttributes<HTMLSpanElement>, "ref">, "size"> & {
|
|
12
|
+
size?: import("./size.js").ComponentSize;
|
|
13
|
+
} & {
|
|
14
|
+
ref?: React.Ref<HTMLSpanElement> | undefined;
|
|
15
|
+
}) => React.JSX.Element;
|
|
9
16
|
};
|
|
10
17
|
export { SwitchRoot };
|
|
11
|
-
export declare const SwitchThumb:
|
|
12
|
-
size?: import("./
|
|
13
|
-
} &
|
|
18
|
+
export declare const SwitchThumb: ({ ref, ...props }: Omit<Omit<Omit<import("@base-ui/react/switch").SwitchThumbProps, "ref"> & React.RefAttributes<HTMLSpanElement>, "ref">, "size"> & {
|
|
19
|
+
size?: import("./size.js").ComponentSize;
|
|
20
|
+
} & {
|
|
21
|
+
ref?: React.Ref<HTMLSpanElement> | undefined;
|
|
22
|
+
}) => React.JSX.Element;
|
package/dist/switch.js
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import * as React from 'react';
|
|
4
3
|
import { useComponentSize } from './sizing.js';
|
|
5
4
|
import { Switch as BaseSwitch } from '@base-ui/react/switch';
|
|
6
5
|
import { skin, part } from './parts.js';
|
|
7
6
|
import { useRootRef, useResetValue } from './form-state.js';
|
|
8
|
-
const SwitchRoot =
|
|
7
|
+
const SwitchRoot = function SwitchRoot({ size, checked, defaultChecked = false, onCheckedChange, className, form, ref, ...props }) {
|
|
9
8
|
const scheme = useComponentSize(size);
|
|
10
9
|
const [element, attach] = useRootRef(ref);
|
|
11
10
|
const [selected, setSelected] = useResetValue(checked, defaultChecked, element, form);
|
|
12
|
-
return (_jsx(BaseSwitch.Root, { ...props, "data-ui-size": size ? scheme : undefined, form: form, ref: attach, checked: selected, className: skin('
|
|
11
|
+
return (_jsx(BaseSwitch.Root, { ...props, "data-ui-size": size ? scheme : undefined, form: form, ref: attach, checked: selected, className: skin('surf-switch', className), onCheckedChange: (next, event) => {
|
|
13
12
|
onCheckedChange?.(next, event);
|
|
14
13
|
if (checked === undefined && !event.isCanceled)
|
|
15
14
|
setSelected(next);
|
|
16
15
|
} }));
|
|
17
|
-
}
|
|
16
|
+
};
|
|
18
17
|
export const Switch = {
|
|
19
18
|
...BaseSwitch,
|
|
20
19
|
Root: SwitchRoot,
|
|
21
|
-
Thumb: /* @__PURE__ */ part(BaseSwitch.Thumb, '
|
|
20
|
+
Thumb: /* @__PURE__ */ part(BaseSwitch.Thumb, 'surf-switch-thumb'),
|
|
22
21
|
};
|
|
23
22
|
// Flat names for React Server Components, which cannot dot into client exports.
|
|
24
23
|
export { SwitchRoot };
|
package/dist/syntax-context.d.ts
CHANGED
|
@@ -3,5 +3,14 @@ import type { SyntaxToken } from './syntax-engine.js';
|
|
|
3
3
|
export type SyntaxEngine = {
|
|
4
4
|
highlightCode(code: string, language: string): Promise<SyntaxToken[][] | null>;
|
|
5
5
|
};
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Loads the highlighting engine, and says whether code waits for its tokens inside
|
|
8
|
+
* Suspense. Without a provider, code renders as plain text.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SyntaxEngineContext: React.Context<{
|
|
11
|
+
load: () => Promise<SyntaxEngine>;
|
|
12
|
+
suspense: boolean;
|
|
13
|
+
} | null>;
|
|
14
|
+
export declare function requestTokens(load: () => Promise<SyntaxEngine>, code: string, language: string): Promise<SyntaxToken[][] | null>;
|
|
15
|
+
/** Records tokens already known, such as those a server render wrote. */
|
|
16
|
+
export declare function provideTokens(code: string, language: string, tokens: SyntaxToken[][]): void;
|
package/dist/syntax-context.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Loads the highlighting engine, and says whether code waits for its tokens inside
|
|
5
|
+
* Suspense. Without a provider, code renders as plain text.
|
|
6
|
+
*/
|
|
4
7
|
export const SyntaxEngineContext = /* @__PURE__ */ React.createContext(null);
|
|
8
|
+
// One request per language and code, so a render that suspended finds its tokens again.
|
|
9
|
+
// A settled request carries React's status and value, so use() reads it at once.
|
|
10
|
+
const requests = new Map();
|
|
11
|
+
export function requestTokens(load, code, language) {
|
|
12
|
+
const key = `${language}\n${code}`;
|
|
13
|
+
let pending = requests.get(key);
|
|
14
|
+
if (!pending) {
|
|
15
|
+
const request = load()
|
|
16
|
+
.then((engine) => engine.highlightCode(code, language))
|
|
17
|
+
.catch(() => null);
|
|
18
|
+
void request.then((tokens) => Object.assign(request, { status: 'fulfilled', value: tokens }));
|
|
19
|
+
pending = request;
|
|
20
|
+
remember(key, pending);
|
|
21
|
+
}
|
|
22
|
+
return pending;
|
|
23
|
+
}
|
|
24
|
+
/** Records tokens already known, such as those a server render wrote. */
|
|
25
|
+
export function provideTokens(code, language, tokens) {
|
|
26
|
+
const key = `${language}\n${code}`;
|
|
27
|
+
if (!requests.has(key))
|
|
28
|
+
remember(key, Object.assign(Promise.resolve(tokens), {
|
|
29
|
+
status: 'fulfilled',
|
|
30
|
+
value: tokens,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
function remember(key, request) {
|
|
34
|
+
requests.set(key, request);
|
|
35
|
+
if (requests.size > 500)
|
|
36
|
+
requests.delete(requests.keys().next().value);
|
|
37
|
+
}
|
package/dist/syntax-engine.js
CHANGED
|
@@ -115,7 +115,15 @@ export async function highlightCode(code, language) {
|
|
|
115
115
|
}));
|
|
116
116
|
await languages.get(language);
|
|
117
117
|
const tokens = instance
|
|
118
|
-
.codeToTokensBase(code, {
|
|
118
|
+
.codeToTokensBase(code, {
|
|
119
|
+
lang: language,
|
|
120
|
+
theme: 'platform-quiet',
|
|
121
|
+
// Shiki leaves the rest of a line plain once it has spent this long on it, and the
|
|
122
|
+
// lines after it too. Its 500ms default cut the first line short on a slow device,
|
|
123
|
+
// while the regular expressions were still compiling; the result was then cached.
|
|
124
|
+
// The limit stays only as a guard against runaway patterns.
|
|
125
|
+
tokenizeTimeLimit: 5000,
|
|
126
|
+
})
|
|
119
127
|
.map((line) => line.map((token) => ({
|
|
120
128
|
content: token.content,
|
|
121
129
|
role: colors[token.color?.toLowerCase() ?? ''] ?? 'plain',
|
|
@@ -11,10 +11,6 @@ export type SyntaxHighlighterProps = Omit<React.ComponentPropsWithoutRef<'code'>
|
|
|
11
11
|
* Rendering remains selectable React text; it never inserts executable HTML. Tokens are
|
|
12
12
|
* colored inside a SyntaxHighlighting provider; without one, the code renders plain.
|
|
13
13
|
*/
|
|
14
|
-
export declare const SyntaxHighlighter:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
inline?: boolean;
|
|
18
|
-
label?: string;
|
|
19
|
-
wrap?: boolean;
|
|
20
|
-
} & React.RefAttributes<HTMLElement>>;
|
|
14
|
+
export declare const SyntaxHighlighter: ({ code, language, inline, label, wrap, className, ref, ...props }: SyntaxHighlighterProps & {
|
|
15
|
+
ref?: React.Ref<HTMLElement>;
|
|
16
|
+
}) => React.JSX.Element;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { ScrollArea } from './scroll-area.js';
|
|
5
|
-
import { SyntaxEngineContext } from './syntax-context.js';
|
|
5
|
+
import { SyntaxEngineContext, requestTokens } from './syntax-context.js';
|
|
6
6
|
import { cx } from './parts.js';
|
|
7
7
|
export const syntaxLanguages = [
|
|
8
8
|
'tsx',
|
|
@@ -31,41 +31,52 @@ const aliases = {
|
|
|
31
31
|
plaintext: 'text',
|
|
32
32
|
txt: 'text',
|
|
33
33
|
};
|
|
34
|
+
function Highlighted({ tokens, render, }) {
|
|
35
|
+
return render(React.use(tokens));
|
|
36
|
+
}
|
|
34
37
|
/**
|
|
35
38
|
* Rendering remains selectable React text; it never inserts executable HTML. Tokens are
|
|
36
39
|
* colored inside a SyntaxHighlighting provider; without one, the code renders plain.
|
|
37
40
|
*/
|
|
38
|
-
export const SyntaxHighlighter =
|
|
39
|
-
const
|
|
41
|
+
export const SyntaxHighlighter = function SyntaxHighlighter({ code, language = 'text', inline = false, label = 'Highlighted source', wrap = false, className, ref, ...props }) {
|
|
42
|
+
const source = React.useContext(SyntaxEngineContext);
|
|
40
43
|
const name = language.trim().toLowerCase();
|
|
41
44
|
const lang = aliases[name] ?? name;
|
|
45
|
+
const highlightable = source !== null &&
|
|
46
|
+
lang !== 'text' &&
|
|
47
|
+
syntaxLanguages.includes(lang) &&
|
|
48
|
+
code.length <= 50000;
|
|
49
|
+
// Without suspense, code shows plain until an effect brings its tokens.
|
|
42
50
|
const [result, setResult] = React.useState();
|
|
51
|
+
// A block waits only for the code it mounted with. Code that changes later is
|
|
52
|
+
// highlighted in place, so a boundary never hides shown code behind its fallback.
|
|
53
|
+
const [first] = React.useState(() => ({ code, lang }));
|
|
54
|
+
const suspense = highlightable &&
|
|
55
|
+
source.suspense &&
|
|
56
|
+
first.code === code &&
|
|
57
|
+
first.lang === lang;
|
|
43
58
|
React.useEffect(() => {
|
|
44
|
-
|
|
45
|
-
if (!load ||
|
|
46
|
-
lang === 'text' ||
|
|
47
|
-
!syntaxLanguages.includes(lang) ||
|
|
48
|
-
code.length > 50000)
|
|
59
|
+
if (!highlightable || suspense)
|
|
49
60
|
return;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (current)
|
|
61
|
+
let live = true;
|
|
62
|
+
void requestTokens(source.load, code, lang).then((tokens) => {
|
|
63
|
+
if (live)
|
|
54
64
|
setResult({ code, language: lang, tokens });
|
|
55
|
-
})
|
|
56
|
-
.catch(() => {
|
|
57
|
-
if (current)
|
|
58
|
-
setResult({ code, language: lang, tokens: null });
|
|
59
65
|
});
|
|
60
66
|
return () => {
|
|
61
|
-
|
|
67
|
+
live = false;
|
|
62
68
|
};
|
|
63
|
-
}, [code, lang,
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
}, [code, lang, source, highlightable, suspense]);
|
|
70
|
+
const render = (tokens) => {
|
|
71
|
+
const eols = code.match(/\r\n|\n|\r/g) ?? [];
|
|
72
|
+
const valid = tokens && tokens.length === eols.length + 1;
|
|
73
|
+
return (_jsx("code", { ...props, ref: ref, className: cx('surf-syntax', className), "data-language": lang, "data-highlighted": valid ? 'true' : 'false', children: valid
|
|
74
|
+
? tokens.map((line, i) => (_jsxs(React.Fragment, { children: [line.map((token, j) => (_jsx("span", { "data-syntax": token.role, children: token.content }, j))), eols[i] ?? ''] }, i)))
|
|
75
|
+
: code }));
|
|
76
|
+
};
|
|
77
|
+
// With suspense, the block waits for its tokens behind its own boundary, plain
|
|
78
|
+
// meanwhile: a server render writes it highlighted and hydration keeps that HTML,
|
|
79
|
+
// while a block that appears later never suspends anything around it.
|
|
80
|
+
const element = suspense ? (_jsx(React.Suspense, { fallback: render(null), children: _jsx(Highlighted, { tokens: requestTokens(source.load, code, lang), render: render }) })) : (render(result?.code === code && result.language === lang ? result.tokens : null));
|
|
81
|
+
return inline ? (element) : (_jsx(ScrollArea, { label: label, orientation: 'horizontal', rootClassName: 'surf-syntax-scroll', children: _jsx("pre", { className: 'surf-syntax-pre', "data-wrap": wrap || undefined, children: element }) }));
|
|
82
|
+
};
|
|
@@ -2,7 +2,20 @@ import * as React from 'react';
|
|
|
2
2
|
/**
|
|
3
3
|
* Turns on syntax highlighting for CodeBlock, SyntaxHighlighter and Markdown inside it.
|
|
4
4
|
* Requires the optional `shiki` package; without this provider, code renders as plain text.
|
|
5
|
+
* Code shows as plain text until its tokens arrive. With `suspense`, each block waits
|
|
6
|
+
* for them behind its own Suspense boundary instead, so a server render writes it
|
|
7
|
+
* highlighted and hydration keeps that HTML. Nothing around a block suspends.
|
|
5
8
|
*/
|
|
6
|
-
export declare function SyntaxHighlighting({ children, }: {
|
|
9
|
+
export declare function SyntaxHighlighting({ children, suspense, }: {
|
|
7
10
|
children?: React.ReactNode;
|
|
11
|
+
suspense?: boolean;
|
|
8
12
|
}): React.JSX.Element;
|
|
13
|
+
/**
|
|
14
|
+
* Reads the tokens of the server-rendered code under `root` from its HTML, so
|
|
15
|
+
* hydration needs neither the engine nor a grammar. With `suspense`, await it before
|
|
16
|
+
* `hydrateRoot`: every block then hydrates at once, so no boundary is left waiting for
|
|
17
|
+
* its tokens, where a context change above it would make React drop the highlighted
|
|
18
|
+
* HTML for the plain fallback. A block whose HTML doesn't read back as its code is
|
|
19
|
+
* highlighted again.
|
|
20
|
+
*/
|
|
21
|
+
export declare function preloadSyntaxHighlighting(root?: ParentNode): Promise<undefined>;
|
|
@@ -1,12 +1,63 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { SyntaxEngineContext } from './syntax-context.js';
|
|
3
|
+
import { SyntaxEngineContext, provideTokens, requestTokens, } from './syntax-context.js';
|
|
4
4
|
// The engine and each grammar load on first use, in their own chunks.
|
|
5
5
|
const load = () => import('./syntax-engine.js');
|
|
6
|
+
const sources = {
|
|
7
|
+
plain: { load, suspense: false },
|
|
8
|
+
suspense: { load, suspense: true },
|
|
9
|
+
};
|
|
6
10
|
/**
|
|
7
11
|
* Turns on syntax highlighting for CodeBlock, SyntaxHighlighter and Markdown inside it.
|
|
8
12
|
* Requires the optional `shiki` package; without this provider, code renders as plain text.
|
|
13
|
+
* Code shows as plain text until its tokens arrive. With `suspense`, each block waits
|
|
14
|
+
* for them behind its own Suspense boundary instead, so a server render writes it
|
|
15
|
+
* highlighted and hydration keeps that HTML. Nothing around a block suspends.
|
|
9
16
|
*/
|
|
10
|
-
export function SyntaxHighlighting({ children, }) {
|
|
11
|
-
return (_jsx(SyntaxEngineContext.Provider, { value:
|
|
17
|
+
export function SyntaxHighlighting({ children, suspense = false, }) {
|
|
18
|
+
return (_jsx(SyntaxEngineContext.Provider, { value: suspense ? sources.suspense : sources.plain, children: children }));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reads the tokens of the server-rendered code under `root` from its HTML, so
|
|
22
|
+
* hydration needs neither the engine nor a grammar. With `suspense`, await it before
|
|
23
|
+
* `hydrateRoot`: every block then hydrates at once, so no boundary is left waiting for
|
|
24
|
+
* its tokens, where a context change above it would make React drop the highlighted
|
|
25
|
+
* HTML for the plain fallback. A block whose HTML doesn't read back as its code is
|
|
26
|
+
* highlighted again.
|
|
27
|
+
*/
|
|
28
|
+
export function preloadSyntaxHighlighting(root = document) {
|
|
29
|
+
return Promise.all(Array.from(root.querySelectorAll('code.surf-syntax[data-highlighted="true"]'), (code) => {
|
|
30
|
+
const text = code.textContent ?? '';
|
|
31
|
+
const language = code.dataset.language;
|
|
32
|
+
const tokens = readTokens(code, text);
|
|
33
|
+
if (tokens)
|
|
34
|
+
provideTokens(text, language, tokens);
|
|
35
|
+
else
|
|
36
|
+
return requestTokens(load, text, language);
|
|
37
|
+
})).then(() => undefined);
|
|
38
|
+
}
|
|
39
|
+
// SyntaxHighlighter writes a line's tokens as spans and ends it with its line break.
|
|
40
|
+
function readTokens(code, text) {
|
|
41
|
+
const lines = [[]];
|
|
42
|
+
let read = '';
|
|
43
|
+
for (const node of code.childNodes) {
|
|
44
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
45
|
+
const content = node.textContent ?? '';
|
|
46
|
+
lines[lines.length - 1].push({
|
|
47
|
+
content,
|
|
48
|
+
role: node.dataset.syntax ?? '',
|
|
49
|
+
});
|
|
50
|
+
read += content;
|
|
51
|
+
}
|
|
52
|
+
else if (node.nodeType === Node.TEXT_NODE) {
|
|
53
|
+
const breaks = node.textContent ?? '';
|
|
54
|
+
if (!/^[\r\n]*$/.test(breaks))
|
|
55
|
+
return null;
|
|
56
|
+
const count = breaks.match(/\r\n|\n|\r/g)?.length ?? 0;
|
|
57
|
+
for (let line = 0; line < count; line++)
|
|
58
|
+
lines.push([]);
|
|
59
|
+
read += breaks;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return read === text ? lines : null;
|
|
12
63
|
}
|