@orcestr/ui 0.0.1 → 0.0.3

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 CHANGED
@@ -10,6 +10,10 @@
10
10
 
11
11
  # Orcestr UI
12
12
 
13
+ ## [Demo](https://orcestr.com/ui)
14
+
15
+ Open the live example page to inspect Orcestr UI components and patterns in context.
16
+
13
17
  Shared React UI foundation for Orcestr products.
14
18
 
15
19
  Orcestr UI is a public component library extracted from real Orcestr product work. It collects the interface primitives we reuse across product surfaces: app shell patterns, dense operational controls, workflow states, overlays, form fields, data views, design tokens and theme infrastructure.
package/README.ru.md CHANGED
@@ -10,6 +10,10 @@
10
10
 
11
11
  # Orcestr UI
12
12
 
13
+ ## [Demo](https://orcestr.com/ui)
14
+
15
+ Открой live example page, чтобы посмотреть компоненты и паттерны Orcestr UI в контексте.
16
+
13
17
  Общая React UI-основа для продуктов Orcestr.
14
18
 
15
19
  Orcestr UI - публичная библиотека компонентов, выделенная из реальной продуктовой разработки Orcestr. Здесь собираются интерфейсные примитивы, которые переиспользуются в product surfaces: application shell, плотные операционные контролы, workflow-состояния, overlays, поля, data views, design tokens и инфраструктура темы.
@@ -165,25 +165,25 @@ export declare const themePlaygroundPresets: readonly [{
165
165
  };
166
166
  readonly mode: "dark";
167
167
  readonly surface: "media";
168
- readonly accent: "#fb7185";
169
- readonly previewBg: "#120d0e";
170
- readonly previewPanel: "#2a1222";
171
- readonly previewText: "#fff0f6";
168
+ readonly accent: "#f0a6d8";
169
+ readonly previewBg: "#141116";
170
+ readonly previewPanel: "#1d1820";
171
+ readonly previewText: "#f7eef7";
172
172
  readonly group: "dark";
173
173
  readonly overrides: {
174
174
  readonly colors: {
175
- readonly bg: "#120d0e";
176
- readonly panel: "#2a1222";
177
- readonly panelSoft: "#3a1830";
178
- readonly floating: "#2a1222";
179
- readonly brand: "#fb7185";
180
- readonly brandStrong: "#fda4af";
181
- readonly brandSolid: "#be123c";
182
- readonly brandSolidHover: "#e11d48";
183
- readonly brandSoft: "rgb(251 113 133 / 18%)";
184
- readonly selected: "rgb(251 113 133 / 15%)";
175
+ readonly bg: "#141116";
176
+ readonly panel: "#1d1820";
177
+ readonly panelSoft: "#29212d";
178
+ readonly floating: "#1d1820";
179
+ readonly brand: "#f0a6d8";
180
+ readonly brandStrong: "#ffc1e7";
181
+ readonly brandSolid: "#a84486";
182
+ readonly brandSolidHover: "#bc5399";
183
+ readonly brandSoft: "rgb(240 166 216 / 17%)";
184
+ readonly selected: "rgb(240 166 216 / 16%)";
185
185
  readonly warning: "#fbbf24";
186
- readonly warningSoft: "#4a2f0b";
186
+ readonly warningSoft: "#49371c";
187
187
  };
188
188
  };
189
189
  }, {
@@ -215,23 +215,23 @@ export declare const themePlaygroundPresets: readonly [{
215
215
  };
216
216
  readonly mode: "dark";
217
217
  readonly surface: "orcestr";
218
- readonly accent: "#a78bfa";
219
- readonly previewBg: "#09091f";
220
- readonly previewPanel: "#121234";
221
- readonly previewText: "#edf5ff";
218
+ readonly accent: "#7dd3fc";
219
+ readonly previewBg: "#080f1c";
220
+ readonly previewPanel: "#101a2b";
221
+ readonly previewText: "#e9f3ff";
222
222
  readonly group: "dark";
223
223
  readonly overrides: {
224
224
  readonly colors: {
225
- readonly bg: "#09091f";
226
- readonly panel: "#121234";
227
- readonly panelSoft: "#1b1b4a";
228
- readonly floating: "#121234";
229
- readonly brand: "#a78bfa";
230
- readonly brandStrong: "#c4b5fd";
231
- readonly brandSolid: "#7c3aed";
232
- readonly brandSolidHover: "#8b5cf6";
233
- readonly brandSoft: "rgb(167 139 250 / 17%)";
234
- readonly selected: "rgb(167 139 250 / 14%)";
225
+ readonly bg: "#080f1c";
226
+ readonly panel: "#101a2b";
227
+ readonly panelSoft: "#172338";
228
+ readonly floating: "#101a2b";
229
+ readonly brand: "#7dd3fc";
230
+ readonly brandStrong: "#bae6fd";
231
+ readonly brandSolid: "#0369a1";
232
+ readonly brandSolidHover: "#0284c7";
233
+ readonly brandSoft: "rgb(125 211 252 / 16%)";
234
+ readonly selected: "rgb(125 211 252 / 13%)";
235
235
  readonly info: "#22d3ee";
236
236
  readonly infoSoft: "#083344";
237
237
  };
@@ -158,25 +158,25 @@ export const themePlaygroundPresets = [
158
158
  },
159
159
  mode: 'dark',
160
160
  surface: 'media',
161
- accent: '#fb7185',
162
- previewBg: '#120d0e',
163
- previewPanel: '#2a1222',
164
- previewText: '#fff0f6',
161
+ accent: '#f0a6d8',
162
+ previewBg: '#141116',
163
+ previewPanel: '#1d1820',
164
+ previewText: '#f7eef7',
165
165
  group: 'dark',
166
166
  overrides: {
167
167
  colors: {
168
- bg: '#120d0e',
169
- panel: '#2a1222',
170
- panelSoft: '#3a1830',
171
- floating: '#2a1222',
172
- brand: '#fb7185',
173
- brandStrong: '#fda4af',
174
- brandSolid: '#be123c',
175
- brandSolidHover: '#e11d48',
176
- brandSoft: 'rgb(251 113 133 / 18%)',
177
- selected: 'rgb(251 113 133 / 15%)',
168
+ bg: '#141116',
169
+ panel: '#1d1820',
170
+ panelSoft: '#29212d',
171
+ floating: '#1d1820',
172
+ brand: '#f0a6d8',
173
+ brandStrong: '#ffc1e7',
174
+ brandSolid: '#a84486',
175
+ brandSolidHover: '#bc5399',
176
+ brandSoft: 'rgb(240 166 216 / 17%)',
177
+ selected: 'rgb(240 166 216 / 16%)',
178
178
  warning: '#fbbf24',
179
- warningSoft: '#4a2f0b',
179
+ warningSoft: '#49371c',
180
180
  },
181
181
  },
182
182
  },
@@ -210,23 +210,23 @@ export const themePlaygroundPresets = [
210
210
  },
211
211
  mode: 'dark',
212
212
  surface: 'orcestr',
213
- accent: '#a78bfa',
214
- previewBg: '#09091f',
215
- previewPanel: '#121234',
216
- previewText: '#edf5ff',
213
+ accent: '#7dd3fc',
214
+ previewBg: '#080f1c',
215
+ previewPanel: '#101a2b',
216
+ previewText: '#e9f3ff',
217
217
  group: 'dark',
218
218
  overrides: {
219
219
  colors: {
220
- bg: '#09091f',
221
- panel: '#121234',
222
- panelSoft: '#1b1b4a',
223
- floating: '#121234',
224
- brand: '#a78bfa',
225
- brandStrong: '#c4b5fd',
226
- brandSolid: '#7c3aed',
227
- brandSolidHover: '#8b5cf6',
228
- brandSoft: 'rgb(167 139 250 / 17%)',
229
- selected: 'rgb(167 139 250 / 14%)',
220
+ bg: '#080f1c',
221
+ panel: '#101a2b',
222
+ panelSoft: '#172338',
223
+ floating: '#101a2b',
224
+ brand: '#7dd3fc',
225
+ brandStrong: '#bae6fd',
226
+ brandSolid: '#0369a1',
227
+ brandSolidHover: '#0284c7',
228
+ brandSoft: 'rgb(125 211 252 / 16%)',
229
+ selected: 'rgb(125 211 252 / 13%)',
230
230
  info: '#22d3ee',
231
231
  infoSoft: '#083344',
232
232
  },
@@ -1,2 +1,8 @@
1
- export declare function UiExamplePage(): import("react/jsx-runtime").JSX.Element;
1
+ import { type OrcestrUiLocale } from '..';
2
+ export type UiExamplePageProps = {
3
+ locale?: OrcestrUiLocale;
4
+ defaultLocale?: OrcestrUiLocale;
5
+ onLocaleChange?: (locale: OrcestrUiLocale) => void;
6
+ };
7
+ export declare function UiExamplePage({ locale: controlledLocale, defaultLocale, onLocaleChange, }?: UiExamplePageProps): import("react/jsx-runtime").JSX.Element;
2
8
  //# sourceMappingURL=UiExamplePage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UiExamplePage.d.ts","sourceRoot":"","sources":["../../src/example/UiExamplePage.tsx"],"names":[],"mappings":"AAwmBA,wBAAgB,aAAa,4CAmC5B"}
1
+ {"version":3,"file":"UiExamplePage.d.ts","sourceRoot":"","sources":["../../src/example/UiExamplePage.tsx"],"names":[],"mappings":"AAWA,OAAO,EAYH,KAAK,eAAe,EACvB,MAAM,IAAI,CAAC;AA4lBZ,MAAM,MAAM,kBAAkB,GAAG;IAC7B,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,aAAa,CAAC,EAAE,eAAe,CAAC;IAChC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;CACtD,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAC1B,MAAM,EAAE,gBAAgB,EACxB,aAAoB,EACpB,cAAc,GACjB,GAAE,kBAAuB,2CA8CzB"}
@@ -18,23 +18,34 @@ import {} from './codeSamples';
18
18
  import { navGroups, navItems } from './exampleData';
19
19
  const ORCESTR_LOGO_SRC = '/assets/orcestr/logo.png';
20
20
  const UI_EXAMPLE_SCROLL_LEAD = 50;
21
- function scrollUiExampleSection(id) {
21
+ const UI_EXAMPLE_ACTIVE_PROBE_OFFSET = UI_EXAMPLE_SCROLL_LEAD + 220;
22
+ const UI_EXAMPLE_CLICK_TARGET_TOP_TOLERANCE = 180;
23
+ const UI_EXAMPLE_SCROLL_LOCK_TIMEOUT_MS = 1600;
24
+ function scrollUiExampleSection(id, behavior = 'auto') {
22
25
  const node = document.getElementById(id);
23
26
  const scrollRoot = document.querySelector('.oui-app-shell-content-scroll .oui-scroll-area-viewport');
24
27
  if (!node || !scrollRoot) {
25
- node?.scrollIntoView({ block: 'start', behavior: 'smooth' });
28
+ node?.scrollIntoView({ block: 'start', behavior });
26
29
  return;
27
30
  }
28
31
  scrollRoot.scrollTo({
29
32
  top: uiExampleSectionScrollTop(node, scrollRoot),
30
- behavior: 'smooth',
33
+ behavior,
31
34
  });
32
35
  }
33
36
  function uiExampleSectionScrollTop(node, scrollRoot) {
34
37
  const maxTop = scrollRoot
35
38
  ? Math.max(0, scrollRoot.scrollHeight - scrollRoot.clientHeight)
36
39
  : Number.POSITIVE_INFINITY;
37
- return Math.min(maxTop, Math.max(0, node.offsetTop - UI_EXAMPLE_SCROLL_LEAD));
40
+ const targetTop = scrollRoot
41
+ ? uiExampleSectionAbsoluteTop(node, scrollRoot)
42
+ : node.offsetTop;
43
+ return Math.min(maxTop, Math.max(0, targetTop - UI_EXAMPLE_SCROLL_LEAD));
44
+ }
45
+ function uiExampleSectionAbsoluteTop(node, scrollRoot) {
46
+ const nodeTop = node.getBoundingClientRect().top;
47
+ const scrollRootTop = scrollRoot.getBoundingClientRect().top;
48
+ return scrollRoot.scrollTop + nodeTop - scrollRootTop;
38
49
  }
39
50
  function UiExampleContent({ activePresetId, onThemePresetChange, onThemeOverridesChange, locale, onLocaleChange, }) {
40
51
  const { theme } = useOrcestrTheme();
@@ -70,20 +81,14 @@ function UiExampleContent({ activePresetId, onThemePresetChange, onThemeOverride
70
81
  document.documentElement.classList.add('oui-ui-document-lock');
71
82
  document.body.classList.add('oui-ui-document-lock');
72
83
  const hash = window.location.hash.replace('#', '');
73
- let hashSettleTimer = null;
74
84
  const hashFrame = hash
75
85
  ? window.requestAnimationFrame(() => {
76
86
  scrollUiExampleSection(hash);
77
- hashSettleTimer = window.setTimeout(() => {
78
- scrollUiExampleSection(hash);
79
- }, 450);
80
87
  })
81
88
  : null;
82
89
  return () => {
83
90
  if (hashFrame !== null)
84
91
  window.cancelAnimationFrame(hashFrame);
85
- if (hashSettleTimer !== null)
86
- window.clearTimeout(hashSettleTimer);
87
92
  document.documentElement.classList.remove('oui-ui-document-lock');
88
93
  document.body.classList.remove('oui-ui-document-lock');
89
94
  };
@@ -209,7 +214,7 @@ function UiExampleSidebar({ onNavigate }) {
209
214
  scrollNavigationReleaseTimerRef.current = window.setTimeout(() => {
210
215
  if (scrollNavigationTargetRef.current === id)
211
216
  scrollNavigationTargetRef.current = null;
212
- }, 900);
217
+ }, UI_EXAMPLE_SCROLL_LOCK_TIMEOUT_MS);
213
218
  }, []);
214
219
  useEffect(() => {
215
220
  const hash = window.location.hash.replace('#', '');
@@ -233,7 +238,8 @@ function UiExampleSidebar({ onNavigate }) {
233
238
  return;
234
239
  }
235
240
  const targetTop = uiExampleSectionScrollTop(targetNode, scrollRoot);
236
- if (Math.abs(scrollRoot.scrollTop - targetTop) <= 2) {
241
+ if (Math.abs(scrollRoot.scrollTop - targetTop) <=
242
+ UI_EXAMPLE_CLICK_TARGET_TOP_TOLERANCE) {
237
243
  scrollNavigationTargetRef.current = null;
238
244
  setActiveSectionValue(lockedTarget);
239
245
  }
@@ -242,10 +248,12 @@ function UiExampleSidebar({ onNavigate }) {
242
248
  const bottomDistance = scrollRoot.scrollHeight
243
249
  - scrollRoot.clientHeight
244
250
  - scrollRoot.scrollTop;
245
- const top = scrollRoot.scrollTop + 24;
251
+ const top = scrollRoot.scrollTop + UI_EXAMPLE_ACTIVE_PROBE_OFFSET;
246
252
  const next = bottomDistance <= 2
247
253
  ? nodes.at(-1)?.id
248
- : nodes.filter((node) => node.offsetTop <= top).at(-1)?.id ?? nodes[0]?.id;
254
+ : nodes
255
+ .filter((node) => uiExampleSectionAbsoluteTop(node, scrollRoot) <= top)
256
+ .at(-1)?.id ?? nodes[0]?.id;
249
257
  if (next && next !== activeSectionRef.current)
250
258
  setActiveSectionValue(next);
251
259
  };
@@ -298,18 +306,26 @@ function UiExampleBrand({ compact = false }) {
298
306
  function UiExampleHeaderActions({ compact = false, locale, onLocaleChange, }) {
299
307
  return (_jsxs("div", { className: 'oui-ui-topbar-actions', "data-compact": compact ? 'true' : undefined, children: [_jsxs("span", { className: 'oui-ui-language-switch', "aria-label": 'UI example language', children: [_jsx("span", { className: 'oui-ui-language-label', children: locale === 'ru' ? 'Язык' : 'Language' }), ['ru', 'en'].map((item) => (_jsx("button", { type: 'button', className: 'oui-ui-language-option', "data-active": locale === item ? 'true' : undefined, "aria-pressed": locale === item, onClick: () => onLocaleChange(item), children: item.toUpperCase() }, item)))] }), _jsxs("a", { className: 'oui-ui-topbar-link', href: 'https://github.com/Artasov/orcestr', target: '_blank', rel: 'noreferrer', children: [_jsx(LuExternalLink, { size: 14, "aria-hidden": true }), _jsx("span", { children: "GitHub" })] })] }));
300
308
  }
301
- export function UiExamplePage() {
309
+ export function UiExamplePage({ locale: controlledLocale, defaultLocale = 'ru', onLocaleChange, } = {}) {
302
310
  const firstPreset = getThemePlaygroundPreset('orcestr-dark');
303
311
  const [activePresetId, setActivePresetId] = useState(firstPreset.id);
304
312
  const [mode, setMode] = useState(firstPreset.mode);
305
313
  const [surface, setSurface] = useState(firstPreset.surface);
306
- const [locale, setLocale] = useState('ru');
314
+ const [internalLocale, setInternalLocale] = useState(defaultLocale);
307
315
  const [themeOverrides, setThemeOverrides] = useState(firstPreset.overrides ?? {});
316
+ const locale = controlledLocale ?? internalLocale;
308
317
  const handleThemePresetChange = useCallback((preset) => {
309
318
  setActivePresetId(preset.id);
310
319
  setMode(preset.mode);
311
320
  setSurface(preset.surface);
312
321
  setThemeOverrides(preset.overrides ?? {});
313
322
  }, []);
314
- return (_jsx(OrcestrUiProvider, { mode: mode, surface: surface, onModeChange: setMode, onSurfaceChange: setSurface, themeOverrides: themeOverrides, locale: locale, children: _jsx(UiExampleContent, { activePresetId: activePresetId, onThemePresetChange: handleThemePresetChange, onThemeOverridesChange: setThemeOverrides, locale: locale, onLocaleChange: setLocale }) }));
323
+ const handleLocaleChange = useCallback((nextLocale) => {
324
+ if (onLocaleChange) {
325
+ onLocaleChange(nextLocale);
326
+ return;
327
+ }
328
+ setInternalLocale(nextLocale);
329
+ }, [onLocaleChange]);
330
+ return (_jsx(OrcestrUiProvider, { mode: mode, surface: surface, onModeChange: setMode, onSurfaceChange: setSurface, themeOverrides: themeOverrides, locale: locale, children: _jsx(UiExampleContent, { activePresetId: activePresetId, onThemePresetChange: handleThemePresetChange, onThemeOverridesChange: setThemeOverrides, locale: locale, onLocaleChange: handleLocaleChange }) }));
315
331
  }
@@ -1527,7 +1527,7 @@
1527
1527
  height: 1em;
1528
1528
  align-items: center;
1529
1529
  justify-content: center;
1530
- margin: 0.12em 0.4em 0 0;
1530
+ margin: 0.17em 0.4em 0 0;
1531
1531
  color: var(--oui-icon-text-icon-color, currentColor);
1532
1532
  vertical-align: top;
1533
1533
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orcestr/ui",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Shared React UI package for Orcestr products.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -51,7 +51,7 @@
51
51
  "release:minor": "node scripts/release.mjs minor",
52
52
  "release:major": "node scripts/release.mjs major",
53
53
  "typecheck": "tsc -p tsconfig.json --noEmit",
54
- "test": "node --test \"src/**/*.test.mts\""
54
+ "test": "node scripts/run-tests.mjs"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@tanstack/react-query": "^5.99.0",