@neo-ui-kit/react 1.3.7 → 1.3.9
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 +18 -1
- package/dist/chunks/{chunk-347P6ZE4.js → chunk-IFEK5PRT.js} +10 -1
- package/dist/chunks/{chunk-FRALT7ER.js → chunk-L6VCC7AS.js} +72 -47
- package/dist/chunks/{chunk-YVRQTI7B.js → chunk-OCH27SNY.js} +31 -7
- package/dist/components/artwork/NeonWaves/styles.css +15 -2
- package/dist/components/controls/InputBase/styles.css +29 -47
- package/dist/components/media/MelodyRoll/styles.css +7 -0
- package/dist/core.js +1 -1
- package/dist/forms.js +2 -2
- package/dist/index.js +143 -151
- package/dist/router.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,13 @@ Migration: replace `@ad-voice/ui` with `@neo-ui-kit/react` in dependencies and i
|
|
|
4
4
|
|
|
5
5
|
NEO UI — React + TypeScript UI kit: glass/ruby surfaces, animated neon borders, fields, tabs, dialogs, audio controls and a melody editor.
|
|
6
6
|
|
|
7
|
+
## 1.3.9
|
|
8
|
+
|
|
9
|
+
- Rounded field variants share the existing neon outline; outlined and underlined fields have transparent backgrounds, and filled fields have no animated border.
|
|
10
|
+
- Faster DataTable sorting, cached playground runtime builds and shared editor sources.
|
|
11
|
+
- The shared motion clock defaults to 60 FPS; NeonWaves stars render in the existing worker where supported, with hidden effects paused.
|
|
12
|
+
- MelodyRoll indexes note blocks and limits style invalidation while retaining its effects. Stable 60 FPS under CPU ×8/×16 throttling is **not achieved**; the latest full diagnostic measured about 35.4/6.3 FPS. See the [performance report](https://github.com/Pro100Dimka/adui/blob/main/evidence/performance-report.md) for scenarios and limitations.
|
|
13
|
+
|
|
7
14
|
## Install
|
|
8
15
|
|
|
9
16
|
Live docs: https://pro100dimka.github.io/adui/#/components/overview
|
|
@@ -25,7 +32,7 @@ import "@neo-ui-kit/react/styles.css";
|
|
|
25
32
|
To test repository changes before publishing, run `npm run package` from the repository root and install the resulting archive in a separate application:
|
|
26
33
|
|
|
27
34
|
```bash
|
|
28
|
-
npm install /path/to/adui/release/neo-ui-kit-react-1.3.
|
|
35
|
+
npm install /path/to/adui/release/neo-ui-kit-react-1.3.9.tgz
|
|
29
36
|
```
|
|
30
37
|
|
|
31
38
|
Maintainers publish new versions from `Pro100Dimka/adui` using the `release.yml` GitHub Actions workflow, a matching `v<version>` tag, and the `npm-publication` environment. npm trusts its OIDC identity; no `NPM_TOKEN` is required. The workflow checks the project and packed consumer before publishing the tested archive. Published `1.0.0` cannot be overwritten: code and README changes in npm require a new package version. See the [repository release instructions](https://github.com/Pro100Dimka/adui#npm-пакет).
|
|
@@ -68,6 +75,10 @@ export function SettingsCard() {
|
|
|
68
75
|
|
|
69
76
|
Fields that declare `value` / `defaultValue` support controlled and uncontrolled values; checkboxes and switches use `checked` / `defaultChecked`. Dialog supports `open` / `defaultOpen` and `onOpenChange`; Popover requires controlled `open`. See each component's API for its supported state and event names; a change callback reports a user action, while the owner applies the new controlled value.
|
|
70
77
|
|
|
78
|
+
`InputBase` provides the shared field appearance: `variant="outlined"` has a rounded, glowing outline and a transparent background; `filled` has a glass-like fill with a soft accent highlight and a static thin border; `underlined` has only a glowing bottom line and no background. Outlined and underlined use the shared `AnimatedBorder`, including its motion and offscreen controls; filled has no animated border. Colours follow the active theme. Outlined and underlined fields remain transparent on hover, focus and when read-only. TextField, Select, Autocomplete, NumberField, TextArea, TagInput and PeoplePicker share the same radius at each size, including fields with wrapped chips. Existing adornments and floating labels are supported.
|
|
79
|
+
|
|
80
|
+
Rounded field variants use `--ad-radius-pill`, so floating labels and taller chip rows keep the capsule shape instead of retaining a radius calculated from the shorter base height.
|
|
81
|
+
|
|
71
82
|
The package ships ESM only. Import styles once and wrap the UI in `ThemeProvider`; no playground CSS or router is required. `@neo-ui-kit/react/core`, `/forms`, `/editor`, and `/router` share the same provider contexts with the root entry.
|
|
72
83
|
|
|
73
84
|
The library code is licensed under [MIT](./LICENSE). Bundled third-party materials retain their respective licenses and notices: `dist/fonts/melodix/OFL-PlayfairDisplay.txt` for the font and `dist/components/artwork/QuantumField/UPSTREAM-LICENSE.txt` for Quantum Fields.
|
|
@@ -164,6 +175,10 @@ For an unpaginated large dataset use `virtualize maxHeight={480}`. The table mou
|
|
|
164
175
|
|
|
165
176
|
## Responsiveness
|
|
166
177
|
|
|
178
|
+
The shared animation clock defaults to 60 fps, including border and wave workers. At full rate it follows browser animation frames without an extra timer; `setMotionFrameRate(30)` remains available when an application explicitly prefers less decorative work. Hidden/offscreen work still pauses. The rate is a scheduling target, not a minimum guaranteed on every device or screen.
|
|
179
|
+
|
|
180
|
+
NeonWaves draws stars in its existing wave worker, preserving their positions, sizes and easing. Browsers without worker canvas support share nine CSS twinkle phases instead; the entire decoration pauses offscreen. QuantumField's WebGL renderer targets 60 fps; its CPU canvas fallback retains the 24/30 fps quality limits to protect the UI thread. The separate QuantumBackground visualizer keeps its own adaptive-quality controls. CPU ×8/×16 measurements and remaining limits are recorded in [the performance report](../../evidence/performance-report.md).
|
|
181
|
+
|
|
167
182
|
The package pauses decorative work outside the viewport and in hidden tabs. Its shared motion clock stops when no visible work remains; consumers do not need a separate animation scheduler. Playback updates are isolated from transport and volume controls. Audio analysis and loader image processing yield between short CPU work slices, and unmounting cancels stale results. Processed loader images are capped at 2048px on the longest side; turning background removal off restores the original upload.
|
|
168
183
|
|
|
169
184
|
Router updates are immediate by default. Opt into native snapshot transitions with `transition` only when their temporary interruption of pointer interaction suits your screen; normal CSS entrances do not require it.
|
|
@@ -172,6 +187,8 @@ Animated border geometry is initialized and resized in small batches on the shar
|
|
|
172
187
|
|
|
173
188
|
Keep `rows`, `columns`, `options`, and `initialValues` immutable: replace them when their contents change. Stable references allow expensive derived results to be reused across selection, navigation, and other local UI changes. Do not memoize every prop automatically.
|
|
174
189
|
|
|
190
|
+
MelodyRoll also expects an immutable `notes` array: keep its reference stable during playback and replace it after editing notes. It caches pitch bounds and time bounds for blocks of 128 notes, skipping blocks outside the visible window. Input order and overlapping notes are preserved; heavily interleaved unsorted data can still require a full scan. The moving strip uses nearby time coordinates instead of ever-growing song offsets. Beat, voice level and accuracy updates are scoped to the elements that use them, avoiding restyling the whole keyboard and note strip. These changes preserve the existing glow, transitions, lyrics, seeking and wheel zoom; CPU ×8/×16 stress results are in the performance report.
|
|
191
|
+
|
|
175
192
|
For large tables, use `pageSize` or opt into `virtualize` to bound mounted rows. Virtualization bounds rendering, not the cost of client-side sorting/filtering: use the manual server contracts for datasets that are too large to process locally. Open Select/Autocomplete lists are not virtualized: filter very large datasets before supplying options. `PeoplePicker.onSearch(query, signal)` receives an optional `AbortSignal`; forward it to your data client to cancel superseded requests. Expensive custom renderers, validators, and aggregates still need their own work budget.
|
|
176
193
|
|
|
177
194
|
## Entry points
|
|
@@ -18,7 +18,16 @@ var clean = (v) => {
|
|
|
18
18
|
return x === "/" ? x : x.replace(/\/$/, "");
|
|
19
19
|
};
|
|
20
20
|
var current = (m) => clean(m === "hash" ? location.hash.slice(1) || "/" : location.pathname);
|
|
21
|
-
var morph = (update, transition) =>
|
|
21
|
+
var morph = (update, transition) => {
|
|
22
|
+
if (!transition || !motionEnabled() || !("startViewTransition" in document)) {
|
|
23
|
+
update();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
void document.startViewTransition(() => flushSync(update)).ready.catch((error) => {
|
|
27
|
+
const skipped = error instanceof DOMException && ["AbortError", "TimeoutError"].includes(error.name);
|
|
28
|
+
console[skipped ? "debug" : "error"](skipped ? "Route animation skipped" : "Route animation failed", error);
|
|
29
|
+
});
|
|
30
|
+
};
|
|
22
31
|
function matchPath(pattern, pathname) {
|
|
23
32
|
const p = clean(pattern).split("/").filter(Boolean), v = clean(pathname).split("/").filter(Boolean), params = /* @__PURE__ */ Object.create(null);
|
|
24
33
|
for (let i = 0, j = 0; i < p.length; i++, j++) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
themes,
|
|
3
|
+
useBorder,
|
|
3
4
|
usePauseOffscreen
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OCH27SNY.js";
|
|
5
6
|
import {
|
|
6
7
|
assignRef,
|
|
7
8
|
clamp,
|
|
@@ -351,6 +352,28 @@ var Popover = (p) => {
|
|
|
351
352
|
);
|
|
352
353
|
};
|
|
353
354
|
|
|
355
|
+
// src/components/effects/AnimatedBorder/AnimatedBorder.tsx
|
|
356
|
+
import { createElement as createElement3, useRef as useRef3 } from "react";
|
|
357
|
+
function AnimatedBorder({
|
|
358
|
+
as = "div",
|
|
359
|
+
shell = false,
|
|
360
|
+
round = false,
|
|
361
|
+
children,
|
|
362
|
+
...props
|
|
363
|
+
}) {
|
|
364
|
+
const ref = useRef3(null);
|
|
365
|
+
useBorder(ref, true, shell, round);
|
|
366
|
+
usePauseOffscreen(ref);
|
|
367
|
+
return createElement3(
|
|
368
|
+
as,
|
|
369
|
+
{
|
|
370
|
+
...mark("AnimatedBorder", props),
|
|
371
|
+
ref
|
|
372
|
+
},
|
|
373
|
+
children
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
354
377
|
// src/components/controls/InputBase/InputBase.tsx
|
|
355
378
|
import { forwardRef } from "react";
|
|
356
379
|
import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
@@ -383,6 +406,7 @@ var InputBase = /* @__PURE__ */ forwardRef(function InputBase2({
|
|
|
383
406
|
"data-filled": filled || void 0,
|
|
384
407
|
onClick,
|
|
385
408
|
children: [
|
|
409
|
+
variant !== "filled" && !disabled && !readOnly && /* @__PURE__ */ jsx5(AnimatedBorder, { className: "ad-input-border" }),
|
|
386
410
|
startAdornment && /* @__PURE__ */ jsx5("span", { className: "ad-input-adornment", "data-position": "start", children: startAdornment }),
|
|
387
411
|
/* @__PURE__ */ jsxs2("span", { className: "ad-input-base-content", children: [
|
|
388
412
|
label && /* @__PURE__ */ jsx5("span", { className: "ad-input-label", id: labelId, children: label }),
|
|
@@ -418,7 +442,7 @@ var hsvToHex = ({ h, s, v }) => {
|
|
|
418
442
|
};
|
|
419
443
|
|
|
420
444
|
// src/components/controls/ColorPicker/ColorPicker.tsx
|
|
421
|
-
import { useEffect, useRef as
|
|
445
|
+
import { useEffect, useRef as useRef4, useState } from "react";
|
|
422
446
|
|
|
423
447
|
// src/components/controls/internal.tsx
|
|
424
448
|
import { Fragment, useId as useId2, useLayoutEffect as useLayoutEffect2 } from "react";
|
|
@@ -664,7 +688,7 @@ function ColorPicker({ value: controlled, defaultValue = "#ff244c", onValueChang
|
|
|
664
688
|
useEffect(() => {
|
|
665
689
|
if (p.disabled) setOpen(false);
|
|
666
690
|
}, [p.disabled]);
|
|
667
|
-
const box =
|
|
691
|
+
const box = useRef4(null);
|
|
668
692
|
const ids = useFieldIds(p.label, p.description, void 0, p);
|
|
669
693
|
const hex = normalizeHex(value) ?? "#ff244c";
|
|
670
694
|
if (inline)
|
|
@@ -690,7 +714,7 @@ function ColorPicker({ value: controlled, defaultValue = "#ff244c", onValueChang
|
|
|
690
714
|
}
|
|
691
715
|
|
|
692
716
|
// src/components/controls/shared.tsx
|
|
693
|
-
import { useEffect as useEffect2, useRef as
|
|
717
|
+
import { useEffect as useEffect2, useRef as useRef5 } from "react";
|
|
694
718
|
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
695
719
|
function buttonView(p, name = "Button") {
|
|
696
720
|
const {
|
|
@@ -764,7 +788,7 @@ function BooleanControl({
|
|
|
764
788
|
p.defaultChecked ?? false,
|
|
765
789
|
p.onValueChange
|
|
766
790
|
);
|
|
767
|
-
const input =
|
|
791
|
+
const input = useRef5(null);
|
|
768
792
|
useEffect2(() => {
|
|
769
793
|
if (input.current) input.current.indeterminate = !!p.indeterminate;
|
|
770
794
|
}, [p.indeterminate]);
|
|
@@ -797,11 +821,11 @@ var Button = /* @__PURE__ */ forwardRef2(function Button2(p, ref) {
|
|
|
797
821
|
});
|
|
798
822
|
|
|
799
823
|
// src/components/controls/FilePicker/FilePicker.tsx
|
|
800
|
-
import { useEffect as useEffect3, useRef as
|
|
824
|
+
import { useEffect as useEffect3, useRef as useRef6, useState as useState2 } from "react";
|
|
801
825
|
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
802
826
|
var FilePicker = (p) => {
|
|
803
827
|
const tr = useTr();
|
|
804
|
-
const input =
|
|
828
|
+
const input = useRef6(null), [names, setNames] = useState2(""), [preview, setPreview] = useState2(), [over, setOver] = useState2(false);
|
|
805
829
|
useEffect3(() => () => {
|
|
806
830
|
if (preview) URL.revokeObjectURL(preview);
|
|
807
831
|
}, [preview]);
|
|
@@ -949,7 +973,7 @@ var Tab = /* @__PURE__ */ forwardRef3(function Tab2({
|
|
|
949
973
|
});
|
|
950
974
|
|
|
951
975
|
// src/components/controls/Tabs/Tabs.tsx
|
|
952
|
-
import { useLayoutEffect as useLayoutEffect3, useRef as
|
|
976
|
+
import { useLayoutEffect as useLayoutEffect3, useRef as useRef7, useState as useState3 } from "react";
|
|
953
977
|
import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
954
978
|
function Tabs(p) {
|
|
955
979
|
const tr = useTr();
|
|
@@ -963,7 +987,7 @@ function Tabs(p) {
|
|
|
963
987
|
p.defaultValue ?? items.find((item) => !item.disabled)?.value ?? "",
|
|
964
988
|
p.onValueChange
|
|
965
989
|
);
|
|
966
|
-
const buttons =
|
|
990
|
+
const buttons = useRef7([]);
|
|
967
991
|
const selected = items.findIndex((item) => item.value === value && !item.disabled);
|
|
968
992
|
const firstEnabled = items.findIndex((item) => !item.disabled);
|
|
969
993
|
const fallback = items[firstEnabled]?.value;
|
|
@@ -972,7 +996,7 @@ function Tabs(p) {
|
|
|
972
996
|
}, [p.value, selected, fallback, setValue]);
|
|
973
997
|
const [place2, setPlace] = useState3();
|
|
974
998
|
const [moving, setMoving] = useState3(false);
|
|
975
|
-
const first =
|
|
999
|
+
const first = useRef7(true);
|
|
976
1000
|
useLayoutEffect3(() => {
|
|
977
1001
|
const tab = buttons.current[selected];
|
|
978
1002
|
if (!tab) return setPlace(void 0);
|
|
@@ -1401,7 +1425,7 @@ function Chip({ label, icon, avatar, onRemove, removeLabel, onClick, selected, d
|
|
|
1401
1425
|
import { useState as useState4 } from "react";
|
|
1402
1426
|
|
|
1403
1427
|
// src/components/controls/ChipField.tsx
|
|
1404
|
-
import { useRef as
|
|
1428
|
+
import { useRef as useRef8 } from "react";
|
|
1405
1429
|
import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1406
1430
|
function ChipField({
|
|
1407
1431
|
className,
|
|
@@ -1425,7 +1449,7 @@ function ChipField({
|
|
|
1425
1449
|
inputProps
|
|
1426
1450
|
}) {
|
|
1427
1451
|
const ids = useFieldIds(label, description || error, inputProps?.id, inputProps);
|
|
1428
|
-
const input =
|
|
1452
|
+
const input = useRef8(null);
|
|
1429
1453
|
const floating = labelPlacement === "floating" && !!label;
|
|
1430
1454
|
const hasChips = Array.isArray(chips) ? chips.length > 0 : !!chips;
|
|
1431
1455
|
return /* @__PURE__ */ jsx19(FieldFrame, { ids, className: `ad-chip-field ${className}`, label: floating ? void 0 : label, required, description, error, children: /* @__PURE__ */ jsx19(
|
|
@@ -1552,7 +1576,7 @@ function TagInput({
|
|
|
1552
1576
|
}
|
|
1553
1577
|
|
|
1554
1578
|
// src/components/controls/PeoplePicker/PeoplePicker.tsx
|
|
1555
|
-
import { useEffect as useEffect4, useId as useId3, useRef as
|
|
1579
|
+
import { useEffect as useEffect4, useId as useId3, useRef as useRef9, useState as useState5 } from "react";
|
|
1556
1580
|
import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1557
1581
|
var searchDelay = 220;
|
|
1558
1582
|
function PeoplePicker({
|
|
@@ -1574,9 +1598,9 @@ function PeoplePicker({
|
|
|
1574
1598
|
const [found, setFound] = useState5(null);
|
|
1575
1599
|
const [loading, setLoading] = useState5(false);
|
|
1576
1600
|
const [searchError, setSearchError] = useState5();
|
|
1577
|
-
const box =
|
|
1601
|
+
const box = useRef9(null);
|
|
1578
1602
|
const listId = `${useId3()}-people`;
|
|
1579
|
-
const search =
|
|
1603
|
+
const search = useRef9(onSearch);
|
|
1580
1604
|
search.current = onSearch;
|
|
1581
1605
|
const remote = !!onSearch;
|
|
1582
1606
|
const needle = query.trim().toLowerCase();
|
|
@@ -1741,7 +1765,7 @@ function PeoplePicker({
|
|
|
1741
1765
|
}
|
|
1742
1766
|
|
|
1743
1767
|
// src/components/controls/DatePicker/DatePicker.tsx
|
|
1744
|
-
import { useEffect as useEffect5, useMemo as useMemo3, useRef as
|
|
1768
|
+
import { useEffect as useEffect5, useMemo as useMemo3, useRef as useRef10, useState as useState6 } from "react";
|
|
1745
1769
|
import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1746
1770
|
var iso = (date) => `${String(date.getFullYear()).padStart(4, "0")}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
1747
1771
|
var calendarDate = (year, month, day) => {
|
|
@@ -1771,7 +1795,7 @@ function DatePicker({ value: controlled, defaultValue = "", onValueChange, min,
|
|
|
1771
1795
|
}, [p.disabled]);
|
|
1772
1796
|
const [text, setText] = useState6(null);
|
|
1773
1797
|
const [month, setMonth] = useState6(() => selected ?? /* @__PURE__ */ new Date());
|
|
1774
|
-
const box =
|
|
1798
|
+
const box = useRef10(null);
|
|
1775
1799
|
const ids = useFieldIds(p.label, p.description || p.error, void 0, p);
|
|
1776
1800
|
const format = useMemo3(() => new Intl.DateTimeFormat(locale, { day: "2-digit", month: "2-digit", year: "numeric" }), [locale]);
|
|
1777
1801
|
const weekdays = useMemo3(
|
|
@@ -1881,7 +1905,7 @@ function DatePicker({ value: controlled, defaultValue = "", onValueChange, min,
|
|
|
1881
1905
|
}
|
|
1882
1906
|
|
|
1883
1907
|
// src/components/controls/Autocomplete/Autocomplete.tsx
|
|
1884
|
-
import { useEffect as useEffect6, useMemo as useMemo4, useRef as
|
|
1908
|
+
import { useEffect as useEffect6, useMemo as useMemo4, useRef as useRef11, useState as useState7 } from "react";
|
|
1885
1909
|
import { Fragment as Fragment6, jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1886
1910
|
var defaultOptions = ["WASAPI Shared", "WASAPI Exclusive", "ASIO"];
|
|
1887
1911
|
var Autocomplete = ({
|
|
@@ -1915,8 +1939,8 @@ var Autocomplete = ({
|
|
|
1915
1939
|
);
|
|
1916
1940
|
const [open, setOpen] = useState7(false);
|
|
1917
1941
|
const [active, setActive] = useState7(0);
|
|
1918
|
-
const inputNode =
|
|
1919
|
-
const box =
|
|
1942
|
+
const inputNode = useRef11(null);
|
|
1943
|
+
const box = useRef11(null);
|
|
1920
1944
|
const ids = useFieldIds(label, description || error, input.id, input);
|
|
1921
1945
|
const locked = !!(input.disabled || input.readOnly);
|
|
1922
1946
|
useEffect6(() => {
|
|
@@ -2155,7 +2179,7 @@ var NumberField = ({
|
|
|
2155
2179
|
};
|
|
2156
2180
|
|
|
2157
2181
|
// src/components/controls/Select/Select.tsx
|
|
2158
|
-
import { forwardRef as forwardRef6, useEffect as useEffect7, useMemo as useMemo5, useRef as
|
|
2182
|
+
import { forwardRef as forwardRef6, useEffect as useEffect7, useMemo as useMemo5, useRef as useRef12, useState as useState8 } from "react";
|
|
2159
2183
|
import { Fragment as Fragment8, jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2160
2184
|
var defaultKey = (value) => typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
2161
2185
|
var textOf = (option) => option.text ?? (typeof option.label === "string" || typeof option.label === "number" ? String(option.label) : "");
|
|
@@ -2183,8 +2207,8 @@ var Select = /* @__PURE__ */ forwardRef6(function Select2(p, ref) {
|
|
|
2183
2207
|
setQuery("");
|
|
2184
2208
|
}
|
|
2185
2209
|
}, [p.disabled]);
|
|
2186
|
-
const anchor =
|
|
2187
|
-
const box =
|
|
2210
|
+
const anchor = useRef12(null);
|
|
2211
|
+
const box = useRef12(null);
|
|
2188
2212
|
const keyedOptions = useMemo5(() => options.map((option) => ({ option, key: keyOf(option.value) })), [options, keyOf]);
|
|
2189
2213
|
const selectedKey = value === void 0 ? void 0 : keyOf(value);
|
|
2190
2214
|
const selected = keyedOptions.find(({ key }) => key === selectedKey)?.option;
|
|
@@ -2354,10 +2378,10 @@ var Select = /* @__PURE__ */ forwardRef6(function Select2(p, ref) {
|
|
|
2354
2378
|
});
|
|
2355
2379
|
|
|
2356
2380
|
// src/components/effects/ImageShine/ImageShine.tsx
|
|
2357
|
-
import { useRef as
|
|
2381
|
+
import { useRef as useRef13 } from "react";
|
|
2358
2382
|
import { jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2359
2383
|
function ImageShine({ src, glow = true, label, style, ...p }) {
|
|
2360
|
-
const pauseRef =
|
|
2384
|
+
const pauseRef = useRef13(null);
|
|
2361
2385
|
usePauseOffscreen(pauseRef);
|
|
2362
2386
|
return /* @__PURE__ */ jsxs18(
|
|
2363
2387
|
"span",
|
|
@@ -2482,7 +2506,7 @@ import {
|
|
|
2482
2506
|
useEffect as useEffect8,
|
|
2483
2507
|
useLayoutEffect as useLayoutEffect4,
|
|
2484
2508
|
useMemo as useMemo6,
|
|
2485
|
-
useRef as
|
|
2509
|
+
useRef as useRef14,
|
|
2486
2510
|
useState as useState9
|
|
2487
2511
|
} from "react";
|
|
2488
2512
|
|
|
@@ -2562,17 +2586,17 @@ function paintCanvas(key, width, height, painting) {
|
|
|
2562
2586
|
import { jsx as jsx29, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2563
2587
|
var RotaryKnob = (p) => {
|
|
2564
2588
|
const tr = useTr();
|
|
2565
|
-
const rootRef =
|
|
2566
|
-
const baseRef =
|
|
2567
|
-
const feedbackRef =
|
|
2568
|
-
const rotorRef =
|
|
2569
|
-
const controlRef =
|
|
2570
|
-
const readoutRef =
|
|
2571
|
-
const controllerRef =
|
|
2572
|
-
const onChangeRef =
|
|
2573
|
-
const onCommitRef =
|
|
2574
|
-
const disabledRef =
|
|
2575
|
-
const readOnlyRef =
|
|
2589
|
+
const rootRef = useRef14(null);
|
|
2590
|
+
const baseRef = useRef14(null);
|
|
2591
|
+
const feedbackRef = useRef14(null);
|
|
2592
|
+
const rotorRef = useRef14(null);
|
|
2593
|
+
const controlRef = useRef14(null);
|
|
2594
|
+
const readoutRef = useRef14(null);
|
|
2595
|
+
const controllerRef = useRef14(null);
|
|
2596
|
+
const onChangeRef = useRef14(p.onValueChange);
|
|
2597
|
+
const onCommitRef = useRef14(p.onValueCommit);
|
|
2598
|
+
const disabledRef = useRef14(!!p.disabled);
|
|
2599
|
+
const readOnlyRef = useRef14(!!p.readOnly);
|
|
2576
2600
|
const numberFormat = useMemo6(
|
|
2577
2601
|
() => new Intl.NumberFormat("ru-RU", { maximumFractionDigits: 1 }),
|
|
2578
2602
|
[]
|
|
@@ -2581,14 +2605,14 @@ var RotaryKnob = (p) => {
|
|
|
2581
2605
|
const span = (p.max ?? 100) - min || 1;
|
|
2582
2606
|
const toPosition = (value) => clamp((value - min) / span * 100);
|
|
2583
2607
|
const toValue = (position) => Math.round((min + position / 100 * span) * 1e6) / 1e6;
|
|
2584
|
-
const controlledPosition =
|
|
2608
|
+
const controlledPosition = useRef14(void 0);
|
|
2585
2609
|
controlledPosition.current = p.value === void 0 ? void 0 : toPosition(p.value);
|
|
2586
2610
|
const displayScale = p.displayScale ?? 1;
|
|
2587
2611
|
const format = (value) => `${numberFormat.format(value * displayScale)}${p.suffix ?? "%"}`;
|
|
2588
|
-
const scale =
|
|
2612
|
+
const scale = useRef14({ toValue, format });
|
|
2589
2613
|
scale.current = { toValue, format };
|
|
2590
|
-
const stepRef =
|
|
2591
|
-
const fineStepRef =
|
|
2614
|
+
const stepRef = useRef14(1);
|
|
2615
|
+
const fineStepRef = useRef14(0.1);
|
|
2592
2616
|
onChangeRef.current = p.onValueChange;
|
|
2593
2617
|
onCommitRef.current = p.onValueCommit;
|
|
2594
2618
|
disabledRef.current = !!p.disabled;
|
|
@@ -2596,8 +2620,8 @@ var RotaryKnob = (p) => {
|
|
|
2596
2620
|
stepRef.current = Math.max(1e-3, (p.step ?? span / 100) / span * 100);
|
|
2597
2621
|
fineStepRef.current = Math.max(1e-3, (p.fineStep ?? span / 1e3) / span * 100);
|
|
2598
2622
|
const initial = toPosition(p.value ?? p.defaultValue ?? min + span * 0.67);
|
|
2599
|
-
const initialRef =
|
|
2600
|
-
const resetRef =
|
|
2623
|
+
const initialRef = useRef14(initial);
|
|
2624
|
+
const resetRef = useRef14(void 0);
|
|
2601
2625
|
resetRef.current = p.resetValue === void 0 ? void 0 : toPosition(p.resetValue);
|
|
2602
2626
|
const diameter = p.diameter ?? { xs: 84, sm: 124, md: 220, lg: 320 }[normalizeSize(p.size) ?? "md"];
|
|
2603
2627
|
useLayoutEffect4(() => {
|
|
@@ -3277,7 +3301,7 @@ var RotaryKnob = (p) => {
|
|
|
3277
3301
|
controllerRef.current?.setValue(toPosition(p.value), false);
|
|
3278
3302
|
}, [p.value, min, span]);
|
|
3279
3303
|
const [draft, setDraft] = useState9(null);
|
|
3280
|
-
const cancelled =
|
|
3304
|
+
const cancelled = useRef14(false);
|
|
3281
3305
|
const editable = !p.disabled && !p.readOnly;
|
|
3282
3306
|
const applyDraft = () => {
|
|
3283
3307
|
if (cancelled.current) return;
|
|
@@ -3396,7 +3420,7 @@ import {
|
|
|
3396
3420
|
useContext,
|
|
3397
3421
|
useEffect as useEffect9,
|
|
3398
3422
|
useMemo as useMemo7,
|
|
3399
|
-
useRef as
|
|
3423
|
+
useRef as useRef15,
|
|
3400
3424
|
useState as useState10
|
|
3401
3425
|
} from "react";
|
|
3402
3426
|
import { jsx as jsx30 } from "react/jsx-runtime";
|
|
@@ -3416,8 +3440,8 @@ var setPath = (value, path, next) => {
|
|
|
3416
3440
|
return root;
|
|
3417
3441
|
};
|
|
3418
3442
|
function useForm(options) {
|
|
3419
|
-
const [values, setValues] = useState10(options.initialValues), [errors, setErrors] = useState10({}), [touched, setTouchedState] = useState10({}), [submitting, setSubmitting] = useState10(false), latest =
|
|
3420
|
-
const previousInitialKey =
|
|
3443
|
+
const [values, setValues] = useState10(options.initialValues), [errors, setErrors] = useState10({}), [touched, setTouchedState] = useState10({}), [submitting, setSubmitting] = useState10(false), latest = useRef15(values), touchedRef = useRef15(touched), validationId = useRef15(0), submittingRef = useRef15(false), settings = useRef15(options), initialKey = useMemo7(() => JSON.stringify(options.initialValues), [options.initialValues]);
|
|
3444
|
+
const previousInitialKey = useRef15(initialKey);
|
|
3421
3445
|
settings.current = options;
|
|
3422
3446
|
useEffect9(() => {
|
|
3423
3447
|
if (previousInitialKey.current === initialKey) return;
|
|
@@ -3623,6 +3647,7 @@ export {
|
|
|
3623
3647
|
Avatar,
|
|
3624
3648
|
Popover,
|
|
3625
3649
|
variantMaterial,
|
|
3650
|
+
AnimatedBorder,
|
|
3626
3651
|
InputBase,
|
|
3627
3652
|
normalizeHex,
|
|
3628
3653
|
hexToHsv,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
// src/core/border-worker.js
|
|
13
13
|
function renderBorders() {
|
|
14
14
|
const scenes = /* @__PURE__ */ new Map();
|
|
15
|
-
let frame = null, fps =
|
|
15
|
+
let frame = null, fps = 60, last2 = -Infinity;
|
|
16
16
|
const release = (scene) => {
|
|
17
17
|
for (const layer of scene.layers) {
|
|
18
18
|
layer.mask.close();
|
|
@@ -91,7 +91,7 @@ var worker = null;
|
|
|
91
91
|
var workerUrl = null;
|
|
92
92
|
var serial = 0;
|
|
93
93
|
var unavailable = false;
|
|
94
|
-
var frameRate =
|
|
94
|
+
var frameRate = 60;
|
|
95
95
|
var clients = /* @__PURE__ */ new Map();
|
|
96
96
|
var visibilityChanged = () => {
|
|
97
97
|
for (const client of clients.values()) client.visibility();
|
|
@@ -466,7 +466,7 @@ function createNeonGeometry() {
|
|
|
466
466
|
var neonGeometry = createNeonGeometry();
|
|
467
467
|
function renderNeon(createGeometry) {
|
|
468
468
|
const geometry = createGeometry(), scenes = /* @__PURE__ */ new Map();
|
|
469
|
-
let frame = null, fps =
|
|
469
|
+
let frame = null, fps = 60, last2 = -Infinity;
|
|
470
470
|
function draw(scene) {
|
|
471
471
|
const { ctx, width, height, padX, padY, sx, sy, options, time, gradient } = scene;
|
|
472
472
|
ctx.clearRect(-padX, -padY, width + padX * 2, height + padY * 2);
|
|
@@ -551,6 +551,27 @@ function renderNeon(createGeometry) {
|
|
|
551
551
|
);
|
|
552
552
|
ctx.restore();
|
|
553
553
|
}
|
|
554
|
+
const phases = scene.twinkle ??= new Float32Array(9);
|
|
555
|
+
for (let phase = 0; phase < (options.dots?.length ? 9 : 0); phase++) {
|
|
556
|
+
const cycle = (time + phase * 0.45) % 4;
|
|
557
|
+
const x = cycle < 2 ? cycle / 2 : (4 - cycle) / 2;
|
|
558
|
+
let low = 0, high = 1;
|
|
559
|
+
for (let step = 0; step < 20; step++) {
|
|
560
|
+
const t2 = (low + high) / 2;
|
|
561
|
+
const at = 3 * (1 - t2) ** 2 * t2 * 0.42 + 3 * (1 - t2) * t2 * t2 * 0.58 + t2 ** 3;
|
|
562
|
+
if (at < x) low = t2;
|
|
563
|
+
else high = t2;
|
|
564
|
+
}
|
|
565
|
+
const t = (low + high) / 2;
|
|
566
|
+
phases[phase] = 1 - (3 * t * t - 2 * t ** 3);
|
|
567
|
+
}
|
|
568
|
+
ctx.fillStyle = scene.starColor;
|
|
569
|
+
for (const [i, dot] of (options.dots ?? []).entries()) {
|
|
570
|
+
ctx.globalAlpha = 0.05 + (dot.o - 0.05) * phases[i % 9];
|
|
571
|
+
ctx.beginPath();
|
|
572
|
+
ctx.arc(dot.x * sx, dot.y * sy, Math.max(1, dot.r * 3) / 2, 0, Math.PI * 2);
|
|
573
|
+
ctx.fill();
|
|
574
|
+
}
|
|
554
575
|
ctx.globalAlpha = 1;
|
|
555
576
|
}
|
|
556
577
|
function schedule2() {
|
|
@@ -624,7 +645,7 @@ var thread = null;
|
|
|
624
645
|
var url = null;
|
|
625
646
|
var serial2 = 0;
|
|
626
647
|
var unavailable2 = false;
|
|
627
|
-
var frameRate2 =
|
|
648
|
+
var frameRate2 = 60;
|
|
628
649
|
var clients2 = /* @__PURE__ */ new Map();
|
|
629
650
|
var visibilityChanged2 = () => {
|
|
630
651
|
for (const client of clients2.values()) client.visibility();
|
|
@@ -687,6 +708,7 @@ function createNeonRenderer(root, svg, initial) {
|
|
|
687
708
|
};
|
|
688
709
|
const hideOriginal = () => {
|
|
689
710
|
if (!renderer.ready) return;
|
|
711
|
+
root.setAttribute("data-ad-neon-rendered", "");
|
|
690
712
|
svg.style.visibility = "hidden";
|
|
691
713
|
const node = root.querySelector(".ad-neon-waves-comets");
|
|
692
714
|
if (node !== comets) {
|
|
@@ -750,6 +772,7 @@ function createNeonRenderer(root, svg, initial) {
|
|
|
750
772
|
intersection.disconnect();
|
|
751
773
|
tokens.disconnect();
|
|
752
774
|
svg.style.visibility = originalVisibility;
|
|
775
|
+
root.removeAttribute("data-ad-neon-rendered");
|
|
753
776
|
if (comets) comets.style.visibility = cometVisibility;
|
|
754
777
|
canvas.remove();
|
|
755
778
|
clients2.delete(id);
|
|
@@ -795,6 +818,7 @@ function createNeonRenderer(root, svg, initial) {
|
|
|
795
818
|
stops,
|
|
796
819
|
shadows,
|
|
797
820
|
color: style?.backgroundColor,
|
|
821
|
+
starColor: getComputedStyle(root.querySelector(".ad-neon-waves-star") ?? root).backgroundColor,
|
|
798
822
|
options: { ...options, comets: comet ? options.comets : 0 }
|
|
799
823
|
};
|
|
800
824
|
const key = JSON.stringify(config);
|
|
@@ -841,8 +865,7 @@ var measurements = /* @__PURE__ */ new Set();
|
|
|
841
865
|
var raf = null;
|
|
842
866
|
var timer = null;
|
|
843
867
|
var last = 0;
|
|
844
|
-
var
|
|
845
|
-
var FRAME = 1e3 / (weakDevice ? 20 : 30);
|
|
868
|
+
var FRAME = 1e3 / 60;
|
|
846
869
|
setBorderFrameRate(1e3 / FRAME);
|
|
847
870
|
setNeonFrameRate(1e3 / FRAME);
|
|
848
871
|
U.setMotionFrameRate = (fps) => {
|
|
@@ -946,7 +969,8 @@ function tick(now) {
|
|
|
946
969
|
last = now;
|
|
947
970
|
}
|
|
948
971
|
if (hasVisibleWork() && !document.hidden) {
|
|
949
|
-
|
|
972
|
+
if (FRAME <= 1e3 / 60) schedule();
|
|
973
|
+
else timer = setTimeout(() => {
|
|
950
974
|
timer = null;
|
|
951
975
|
schedule();
|
|
952
976
|
}, Math.max(0, FRAME - (performance.now() - now) - 4));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* Worker canvas draws the decoration; these elements also provide the main-thread fallback. */
|
|
2
2
|
.ad-neon-waves {
|
|
3
3
|
position: relative;
|
|
4
4
|
display: block;
|
|
@@ -22,11 +22,24 @@
|
|
|
22
22
|
border-radius: 50%;
|
|
23
23
|
background: var(--ad-pink);
|
|
24
24
|
translate: -50% -50%;
|
|
25
|
+
}
|
|
26
|
+
.ad-neon-waves-stars,
|
|
27
|
+
.ad-neon-waves-stars-floor {
|
|
28
|
+
position: absolute;
|
|
29
|
+
inset: 0;
|
|
30
|
+
}
|
|
31
|
+
.ad-neon-waves-stars {
|
|
25
32
|
animation: ad-neon-twinkle 4s ease-in-out infinite;
|
|
26
33
|
}
|
|
34
|
+
.ad-neon-waves[data-ad-neon-rendered] :is(.ad-neon-waves-stars, .ad-neon-waves-stars-floor) {
|
|
35
|
+
display: none;
|
|
36
|
+
}
|
|
27
37
|
@keyframes ad-neon-twinkle {
|
|
38
|
+
0%, 100% {
|
|
39
|
+
opacity: 1;
|
|
40
|
+
}
|
|
28
41
|
50% {
|
|
29
|
-
opacity: 0
|
|
42
|
+
opacity: 0;
|
|
30
43
|
}
|
|
31
44
|
}
|
|
32
45
|
/* A comet is a short bright dash with a glow. Its glow is drawn once on its own layer; the
|