@dskripchenko/flow 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/style.css CHANGED
@@ -1057,17 +1057,6 @@
1057
1057
  margin-top: 8px;
1058
1058
  }
1059
1059
 
1060
- .fl-context-anchor {
1061
- position: fixed;
1062
- width: 1px;
1063
- height: 1px;
1064
- z-index: 50;
1065
- }
1066
- .fl-context-anchor__trigger {
1067
- display: block;
1068
- width: 1px;
1069
- height: 1px;
1070
- }
1071
1060
  .fl-menu-label {
1072
1061
  flex: 1 1 auto;
1073
1062
  }
@@ -1092,8 +1081,7 @@
1092
1081
  display: none;
1093
1082
  }
1094
1083
 
1095
- /* Toggle buttons keep one kit variant (switching variants re-renders the
1096
- trigger and detaches its tooltip); the pressed state is drawn here. */
1084
+ /* A pressed toggle (minimap, snap): the kit's secondary variant, tinted with the accent. */
1097
1085
  .fl-toggle.uid-button.is-active {
1098
1086
  background: var(--fl-accent-subtle);
1099
1087
  color: var(--uid-accent-text, var(--fl-accent));
package/dist/vue.cjs CHANGED
@@ -664,13 +664,14 @@ var FlowControls_default = /* @__PURE__ */ (0, vue.defineComponent)({
664
664
  }, {
665
665
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_dskripchenko_ui.UidButton), {
666
666
  size: "sm",
667
- variant: "ghost",
667
+ variant: __props.minimap ? "secondary" : "ghost",
668
668
  icon: (0, vue.unref)(_dskripchenko_ui_icons.Map),
669
669
  "aria-label": __props.labels.minimap,
670
670
  "aria-pressed": __props.minimap,
671
671
  class: (0, vue.normalizeClass)(["fl-controls__minimap fl-toggle", { "is-active": __props.minimap }]),
672
672
  onClick: _cache[4] || (_cache[4] = ($event) => emit("update:minimap", !__props.minimap))
673
673
  }, null, 8, [
674
+ "variant",
674
675
  "icon",
675
676
  "aria-label",
676
677
  "aria-pressed",
@@ -2600,6 +2601,26 @@ var FlowDetails_default = /* @__PURE__ */ (0, vue.defineComponent)({
2600
2601
  };
2601
2602
  }
2602
2603
  });
2604
+ // src/vue/localeScope.ts
2605
+ /**
2606
+ * Locale handling shared by FlowView and FlowEditor. The kit's locale comes
2607
+ * from the host (a parent UidLocaleProvider) and is never overridden unless
2608
+ * the component's own `locale` prop is set; the package's strings follow the
2609
+ * explicit prop, else the host's kit locale (ru-* → Russian, else English).
2610
+ */
2611
+ /** Renders its slot as is: stands in for UidLocaleProvider when the host's locale should apply. */
2612
+ var LocalePassthrough = (0, vue.defineComponent)({
2613
+ name: "FlowLocalePassthrough",
2614
+ setup: (_, { slots }) => () => slots.default?.()
2615
+ });
2616
+ function useLocaleScope(explicit) {
2617
+ const host = (0, _dskripchenko_ui.useLocale)();
2618
+ return {
2619
+ lang: (0, vue.computed)(() => explicit() ?? (host.value.code?.toLowerCase().startsWith("ru") ? "ru" : "en")),
2620
+ wrapper: (0, vue.computed)(() => explicit() ? _dskripchenko_ui.UidLocaleProvider : LocalePassthrough),
2621
+ wrapperProps: (0, vue.computed)(() => explicit() ? { locale: explicit() === "ru" ? _dskripchenko_ui.ru : _dskripchenko_ui.en } : {})
2622
+ };
2623
+ }
2603
2624
  // src/vue/labels.ts
2604
2625
  var en = {
2605
2626
  graph: "Process graph",
@@ -3056,7 +3077,7 @@ var FlowView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3056
3077
  nodeRenderers: { default: () => ({}) },
3057
3078
  nodeIcons: { default: () => ({}) },
3058
3079
  statusTones: { default: () => ({}) },
3059
- locale: { default: "en" },
3080
+ locale: { default: void 0 },
3060
3081
  labels: { default: void 0 },
3061
3082
  wheel: { default: "zoom" },
3062
3083
  minZoom: { default: .15 },
@@ -3086,8 +3107,8 @@ var FlowView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3086
3107
  */
3087
3108
  const props = __props;
3088
3109
  const emit = __emit;
3089
- const L = (0, vue.computed)(() => resolveLabels(props.locale, props.labels));
3090
- const kitLocale = (0, vue.computed)(() => props.locale === "ru" ? _dskripchenko_ui.ru : _dskripchenko_ui.en);
3110
+ const { lang, wrapper, wrapperProps } = useLocaleScope(() => props.locale);
3111
+ const L = (0, vue.computed)(() => resolveLabels(lang.value, props.labels));
3091
3112
  const toast = (0, _dskripchenko_ui.useToast)();
3092
3113
  const stack = (0, vue.shallowRef)([]);
3093
3114
  const loadingSub = (0, vue.ref)(false);
@@ -3189,7 +3210,7 @@ var FlowView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3189
3210
  });
3190
3211
  const updatedExact = (0, vue.computed)(() => {
3191
3212
  const t = monitor.state.value?.updatedAt ?? monitor.state.value?.receivedAt;
3192
- return t ? new Date(t).toLocaleString(props.locale) : "";
3213
+ return t ? new Date(t).toLocaleString(lang.value) : "";
3193
3214
  });
3194
3215
  const canvas = (0, vue.ref)(null);
3195
3216
  const selection = (0, vue.ref)([]);
@@ -3303,10 +3324,10 @@ var FlowView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3303
3324
  stack
3304
3325
  });
3305
3326
  return (_ctx, _cache) => {
3306
- return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_dskripchenko_ui.UidLocaleProvider), { locale: kitLocale.value }, {
3327
+ return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(wrapper)), (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(wrapperProps))), {
3307
3328
  default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", (0, vue.mergeProps)(_ctx.$attrs, {
3308
3329
  class: ["fl-view", { "is-monitoring": monitoring.value }],
3309
- lang: __props.locale
3330
+ lang: (0, vue.unref)(lang)
3310
3331
  }), [
3311
3332
  (0, vue.createVNode)(FlowCanvas_default, {
3312
3333
  ref_key: "canvas",
@@ -3491,7 +3512,7 @@ var FlowView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3491
3512
  toggleable: __props.toggleable,
3492
3513
  drillable: selectedRef.value.kind === "node" && canEnter(selectedElement.value),
3493
3514
  "status-tones": __props.statusTones,
3494
- locale: __props.locale,
3515
+ locale: (0, vue.unref)(lang),
3495
3516
  onToggle: _cache[2] || (_cache[2] = (v) => toggle({
3496
3517
  kind: selectedRef.value.kind,
3497
3518
  id: selectedRef.value.id,
@@ -3530,7 +3551,7 @@ var FlowView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3530
3551
  __props.toastProvider ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_dskripchenko_ui.UidToastProvider), { key: 1 })) : (0, vue.createCommentVNode)("", true)
3531
3552
  ], 16, _hoisted_1$6)]),
3532
3553
  _: 3
3533
- }, 8, ["locale"]);
3554
+ }, 16);
3534
3555
  };
3535
3556
  }
3536
3557
  });
@@ -3544,43 +3565,21 @@ var _hoisted_2$4 = {
3544
3565
  var FlowContextMenu_default = /* @__PURE__ */ (0, vue.defineComponent)({
3545
3566
  __name: "FlowContextMenu",
3546
3567
  setup(__props, { expose: __expose }) {
3547
- /**
3548
- * A context menu at a screen point, on the kit's UidMenu. The kit menu opens
3549
- * from a trigger, so the trigger here is an invisible 1×1 button placed at
3550
- * the point and clicked programmatically (see docs/internal/design.md: the
3551
- * kit could grow a controlled/context mode).
3552
- */
3568
+ /** A context menu at a screen point: the kit's UidMenu, opened with `openAt`. */
3553
3569
  const items = (0, vue.ref)([]);
3554
- const pos = (0, vue.ref)({
3555
- x: 0,
3556
- y: 0
3557
- });
3558
- const visible = (0, vue.ref)(false);
3559
- const anchor = (0, vue.ref)(null);
3570
+ const menu = (0, vue.ref)(null);
3560
3571
  async function open(point, entries) {
3561
3572
  items.value = entries;
3562
- pos.value = point;
3563
- visible.value = true;
3564
3573
  await (0, vue.nextTick)();
3565
- setTimeout(() => anchor.value?.querySelector(".uid-menu-trigger")?.click(), 0);
3574
+ menu.value?.openAt(point.x, point.y);
3566
3575
  }
3567
3576
  __expose({ open });
3568
3577
  return (_ctx, _cache) => {
3569
- return visible.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
3570
- key: 0,
3571
- ref_key: "anchor",
3572
- ref: anchor,
3573
- class: "fl-context-anchor",
3574
- "data-fl-ui": "",
3575
- style: (0, vue.normalizeStyle)({
3576
- left: `${pos.value.x}px`,
3577
- top: `${pos.value.y}px`
3578
- })
3579
- }, [(0, vue.createVNode)((0, vue.unref)(_dskripchenko_ui.UidMenu), null, {
3580
- trigger: (0, vue.withCtx)(() => [..._cache[0] || (_cache[0] = [(0, vue.createElementVNode)("span", {
3581
- class: "fl-context-anchor__trigger",
3582
- "aria-hidden": "true"
3583
- }, null, -1)])]),
3578
+ return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_dskripchenko_ui.UidMenu), {
3579
+ ref_key: "menu",
3580
+ ref: menu,
3581
+ class: "fl-context-menu"
3582
+ }, {
3584
3583
  default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(items.value, (item, i) => {
3585
3584
  return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: i }, [item.separator ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_dskripchenko_ui.UidMenuSeparator), { key: 0 })) : item.children ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_dskripchenko_ui.UidSubMenu), {
3586
3585
  key: 1,
@@ -3620,7 +3619,7 @@ var FlowContextMenu_default = /* @__PURE__ */ (0, vue.defineComponent)({
3620
3619
  ]))], 64);
3621
3620
  }), 128))]),
3622
3621
  _: 1
3623
- })], 4)) : (0, vue.createCommentVNode)("", true);
3622
+ }, 512);
3624
3623
  };
3625
3624
  }
3626
3625
  });
@@ -4593,7 +4592,7 @@ var FlowEditor_default = /* @__PURE__ */ (0, vue.defineComponent)({
4593
4592
  edgeStyle: { default: "orthogonal" },
4594
4593
  nodeRenderers: { default: () => ({}) },
4595
4594
  nodeIcons: { default: () => ({}) },
4596
- locale: { default: "en" },
4595
+ locale: { default: void 0 },
4597
4596
  labels: { default: void 0 },
4598
4597
  minimap: {
4599
4598
  type: Boolean,
@@ -4625,8 +4624,8 @@ var FlowEditor_default = /* @__PURE__ */ (0, vue.defineComponent)({
4625
4624
  */
4626
4625
  const props = __props;
4627
4626
  const emit = __emit;
4628
- const L = (0, vue.computed)(() => resolveLabels(props.locale, props.labels));
4629
- const kitLocale = (0, vue.computed)(() => props.locale === "ru" ? _dskripchenko_ui.ru : _dskripchenko_ui.en);
4627
+ const { lang, wrapper, wrapperProps } = useLocaleScope(() => props.locale);
4628
+ const L = (0, vue.computed)(() => resolveLabels(lang.value, props.labels));
4630
4629
  const history = new require_core.History((0, vue.toRaw)(props.modelValue));
4631
4630
  const root = (0, vue.shallowRef)((0, vue.toRaw)(props.modelValue));
4632
4631
  const revision = (0, vue.ref)(0);
@@ -5290,7 +5289,7 @@ var FlowEditor_default = /* @__PURE__ */ (0, vue.defineComponent)({
5290
5289
  issues
5291
5290
  });
5292
5291
  return (_ctx, _cache) => {
5293
- return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_dskripchenko_ui.UidLocaleProvider), { locale: kitLocale.value }, {
5292
+ return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(wrapper)), (0, vue.normalizeProps)((0, vue.guardReactiveProps)((0, vue.unref)(wrapperProps))), {
5294
5293
  default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", (0, vue.mergeProps)({
5295
5294
  ref_key: "rootEl",
5296
5295
  ref: rootEl
@@ -5299,7 +5298,7 @@ var FlowEditor_default = /* @__PURE__ */ (0, vue.defineComponent)({
5299
5298
  "is-narrow": narrow.value,
5300
5299
  "is-readonly": __props.readonly
5301
5300
  }],
5302
- lang: __props.locale,
5301
+ lang: (0, vue.unref)(lang),
5303
5302
  role: "application",
5304
5303
  "aria-label": L.value.editor,
5305
5304
  onKeydown: onKeyDown
@@ -5430,13 +5429,14 @@ var FlowEditor_default = /* @__PURE__ */ (0, vue.defineComponent)({
5430
5429
  }, {
5431
5430
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_dskripchenko_ui.UidButton), {
5432
5431
  size: "sm",
5433
- variant: "ghost",
5432
+ variant: snapOn.value ? "secondary" : "ghost",
5434
5433
  icon: (0, vue.unref)(_dskripchenko_ui_icons.Magnet),
5435
5434
  "aria-label": L.value.snapToGrid,
5436
5435
  "aria-pressed": snapOn.value,
5437
5436
  class: (0, vue.normalizeClass)(["fl-toolbar__snap fl-toggle", { "is-active": snapOn.value }]),
5438
5437
  onClick: _cache[1] || (_cache[1] = ($event) => snapOn.value = !snapOn.value)
5439
5438
  }, null, 8, [
5439
+ "variant",
5440
5440
  "icon",
5441
5441
  "aria-label",
5442
5442
  "aria-pressed",
@@ -5763,7 +5763,7 @@ var FlowEditor_default = /* @__PURE__ */ (0, vue.defineComponent)({
5763
5763
  __props.toastProvider ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_dskripchenko_ui.UidToastProvider), { key: 1 })) : (0, vue.createCommentVNode)("", true)
5764
5764
  ], 16, _hoisted_1)]),
5765
5765
  _: 3
5766
- }, 8, ["locale"]);
5766
+ }, 16);
5767
5767
  };
5768
5768
  }
5769
5769
  });
package/dist/vue.d.ts CHANGED
@@ -228,6 +228,7 @@ declare type __VLS_Props = {
228
228
  nodeRenderers?: Record<string, Component>;
229
229
  nodeIcons?: Record<string, Component>;
230
230
  statusTones?: Record<string, string>;
231
+ /** Interface language; by default the host's kit locale (a parent UidLocaleProvider), else English. Setting it also sets the kit's locale inside. */
231
232
  locale?: 'en' | 'ru' | string;
232
233
  labels?: Partial<FlowLabels>;
233
234
  wheel?: 'zoom' | 'pan';
@@ -310,6 +311,7 @@ declare type __VLS_Props_4 = {
310
311
  edgeStyle?: 'orthogonal' | 'smooth';
311
312
  nodeRenderers?: Record<string, Component>;
312
313
  nodeIcons?: Record<string, Component>;
314
+ /** Interface language; by default the host's kit locale (a parent UidLocaleProvider), else English. Setting it also sets the kit's locale inside. */
313
315
  locale?: 'en' | 'ru' | string;
314
316
  labels?: Partial<FlowLabels>;
315
317
  minimap?: boolean;
package/dist/vue.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { A as reduceMonitor, B as measureNode, D as isStale, E as emptyMonitorState, I as boundsOf, J as sidePoint, M as toEpoch, R as layoutGraph, S as snap, T as STATUS_TONES, V as routeEdges, Y as approximateMeasurer, _ as History, a as isVisible, at as isLocked, b as describeCommand, d as copyFragment, dt as stringifyGraph, f as distributeNodes, g as pasteFragment, h as layoutCommand, i as fitView, it as defaultRegistry, j as statusSummary, k as pathEdges, l as zoomAt, n as centreOn, o as nearestInDirection, ot as portsOf, p as findFreeSpot, pt as isEnabled, r as clampZoom, s as toGraph, st as typeOf, tt as validateGraph, u as alignNodes, ut as parseGraph, v as CommandError, w as createPoller, x as nextId, y as applyCommand } from "./core-DfJMfClV.js";
2
2
  import { Fragment, Teleport, computed, createApp, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, guardReactiveProps, h, mergeProps, nextTick, normalizeClass, normalizeProps, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, render, renderList, renderSlot, resolveDynamicComponent, shallowReactive, shallowRef, toDisplayString, toRaw, unref, useId, watch, withCtx, withKeys, withModifiers } from "vue";
3
- import { UidAlert, UidBadge, UidBreadcrumb, UidBreadcrumbItem, UidButton, UidDescriptionItem, UidDescriptionList, UidDrawer, UidFormField, UidInput, UidLocaleProvider, UidMenu, UidMenuItem, UidMenuSeparator, UidModal, UidSelect, UidSpinner, UidSubMenu, UidSwitch, UidTab, UidTabPanel, UidTabs, UidTag, UidTextarea, UidToastProvider, UidTooltip, en as en$1, ru as ru$1, useToast } from "@dskripchenko/ui";
3
+ import { UidAlert, UidBadge, UidBreadcrumb, UidBreadcrumbItem, UidButton, UidDescriptionItem, UidDescriptionList, UidDrawer, UidFormField, UidInput, UidLocaleProvider, UidMenu, UidMenuItem, UidMenuSeparator, UidModal, UidSelect, UidSpinner, UidSubMenu, UidSwitch, UidTab, UidTabPanel, UidTabs, UidTag, UidTextarea, UidToastProvider, UidTooltip, en as en$1, ru as ru$1, useLocale, useToast } from "@dskripchenko/ui";
4
4
  import { Activity, AlignCenterHorizontal, AlignCenterVertical, AlignEndHorizontal, AlignEndVertical, AlignHorizontalSpaceAround, AlignStartHorizontal, AlignStartVertical, AlignVerticalSpaceAround, ArrowRight, Check, ChevronLeft, CircleAlert, CircleCheck, ClipboardPaste, Clock, Cog, Copy, CopyPlus, Diamond, Download, Ellipsis, FileCode, Layers, LayoutGrid, Magnet, Map as Map$1, Maximize, MessageSquare, Minus, PanelRight, Play, Plus, Power, Redo2, RefreshCw, Scissors, Shapes, SkipForward, Square, SquareArrowOutUpRight, StickyNote, Trash2, TriangleAlert, Undo2, Upload, Workflow, X } from "@dskripchenko/ui/icons";
5
5
  // src/vue/icons.ts
6
6
  var NODE_ICONS = {
@@ -663,13 +663,14 @@ var FlowControls_default = /* @__PURE__ */ defineComponent({
663
663
  }, {
664
664
  default: withCtx(() => [createVNode(unref(UidButton), {
665
665
  size: "sm",
666
- variant: "ghost",
666
+ variant: __props.minimap ? "secondary" : "ghost",
667
667
  icon: unref(Map$1),
668
668
  "aria-label": __props.labels.minimap,
669
669
  "aria-pressed": __props.minimap,
670
670
  class: normalizeClass(["fl-controls__minimap fl-toggle", { "is-active": __props.minimap }]),
671
671
  onClick: _cache[4] || (_cache[4] = ($event) => emit("update:minimap", !__props.minimap))
672
672
  }, null, 8, [
673
+ "variant",
673
674
  "icon",
674
675
  "aria-label",
675
676
  "aria-pressed",
@@ -2599,6 +2600,26 @@ var FlowDetails_default = /* @__PURE__ */ defineComponent({
2599
2600
  };
2600
2601
  }
2601
2602
  });
2603
+ // src/vue/localeScope.ts
2604
+ /**
2605
+ * Locale handling shared by FlowView and FlowEditor. The kit's locale comes
2606
+ * from the host (a parent UidLocaleProvider) and is never overridden unless
2607
+ * the component's own `locale` prop is set; the package's strings follow the
2608
+ * explicit prop, else the host's kit locale (ru-* → Russian, else English).
2609
+ */
2610
+ /** Renders its slot as is: stands in for UidLocaleProvider when the host's locale should apply. */
2611
+ var LocalePassthrough = defineComponent({
2612
+ name: "FlowLocalePassthrough",
2613
+ setup: (_, { slots }) => () => slots.default?.()
2614
+ });
2615
+ function useLocaleScope(explicit) {
2616
+ const host = useLocale();
2617
+ return {
2618
+ lang: computed(() => explicit() ?? (host.value.code?.toLowerCase().startsWith("ru") ? "ru" : "en")),
2619
+ wrapper: computed(() => explicit() ? UidLocaleProvider : LocalePassthrough),
2620
+ wrapperProps: computed(() => explicit() ? { locale: explicit() === "ru" ? ru$1 : en$1 } : {})
2621
+ };
2622
+ }
2602
2623
  // src/vue/labels.ts
2603
2624
  var en = {
2604
2625
  graph: "Process graph",
@@ -3055,7 +3076,7 @@ var FlowView_default = /* @__PURE__ */ defineComponent({
3055
3076
  nodeRenderers: { default: () => ({}) },
3056
3077
  nodeIcons: { default: () => ({}) },
3057
3078
  statusTones: { default: () => ({}) },
3058
- locale: { default: "en" },
3079
+ locale: { default: void 0 },
3059
3080
  labels: { default: void 0 },
3060
3081
  wheel: { default: "zoom" },
3061
3082
  minZoom: { default: .15 },
@@ -3085,8 +3106,8 @@ var FlowView_default = /* @__PURE__ */ defineComponent({
3085
3106
  */
3086
3107
  const props = __props;
3087
3108
  const emit = __emit;
3088
- const L = computed(() => resolveLabels(props.locale, props.labels));
3089
- const kitLocale = computed(() => props.locale === "ru" ? ru$1 : en$1);
3109
+ const { lang, wrapper, wrapperProps } = useLocaleScope(() => props.locale);
3110
+ const L = computed(() => resolveLabels(lang.value, props.labels));
3090
3111
  const toast = useToast();
3091
3112
  const stack = shallowRef([]);
3092
3113
  const loadingSub = ref(false);
@@ -3188,7 +3209,7 @@ var FlowView_default = /* @__PURE__ */ defineComponent({
3188
3209
  });
3189
3210
  const updatedExact = computed(() => {
3190
3211
  const t = monitor.state.value?.updatedAt ?? monitor.state.value?.receivedAt;
3191
- return t ? new Date(t).toLocaleString(props.locale) : "";
3212
+ return t ? new Date(t).toLocaleString(lang.value) : "";
3192
3213
  });
3193
3214
  const canvas = ref(null);
3194
3215
  const selection = ref([]);
@@ -3302,10 +3323,10 @@ var FlowView_default = /* @__PURE__ */ defineComponent({
3302
3323
  stack
3303
3324
  });
3304
3325
  return (_ctx, _cache) => {
3305
- return openBlock(), createBlock(unref(UidLocaleProvider), { locale: kitLocale.value }, {
3326
+ return openBlock(), createBlock(resolveDynamicComponent(unref(wrapper)), normalizeProps(guardReactiveProps(unref(wrapperProps))), {
3306
3327
  default: withCtx(() => [createElementVNode("div", mergeProps(_ctx.$attrs, {
3307
3328
  class: ["fl-view", { "is-monitoring": monitoring.value }],
3308
- lang: __props.locale
3329
+ lang: unref(lang)
3309
3330
  }), [
3310
3331
  createVNode(FlowCanvas_default, {
3311
3332
  ref_key: "canvas",
@@ -3490,7 +3511,7 @@ var FlowView_default = /* @__PURE__ */ defineComponent({
3490
3511
  toggleable: __props.toggleable,
3491
3512
  drillable: selectedRef.value.kind === "node" && canEnter(selectedElement.value),
3492
3513
  "status-tones": __props.statusTones,
3493
- locale: __props.locale,
3514
+ locale: unref(lang),
3494
3515
  onToggle: _cache[2] || (_cache[2] = (v) => toggle({
3495
3516
  kind: selectedRef.value.kind,
3496
3517
  id: selectedRef.value.id,
@@ -3529,7 +3550,7 @@ var FlowView_default = /* @__PURE__ */ defineComponent({
3529
3550
  __props.toastProvider ? (openBlock(), createBlock(unref(UidToastProvider), { key: 1 })) : createCommentVNode("", true)
3530
3551
  ], 16, _hoisted_1$6)]),
3531
3552
  _: 3
3532
- }, 8, ["locale"]);
3553
+ }, 16);
3533
3554
  };
3534
3555
  }
3535
3556
  });
@@ -3543,43 +3564,21 @@ var _hoisted_2$4 = {
3543
3564
  var FlowContextMenu_default = /* @__PURE__ */ defineComponent({
3544
3565
  __name: "FlowContextMenu",
3545
3566
  setup(__props, { expose: __expose }) {
3546
- /**
3547
- * A context menu at a screen point, on the kit's UidMenu. The kit menu opens
3548
- * from a trigger, so the trigger here is an invisible 1×1 button placed at
3549
- * the point and clicked programmatically (see docs/internal/design.md: the
3550
- * kit could grow a controlled/context mode).
3551
- */
3567
+ /** A context menu at a screen point: the kit's UidMenu, opened with `openAt`. */
3552
3568
  const items = ref([]);
3553
- const pos = ref({
3554
- x: 0,
3555
- y: 0
3556
- });
3557
- const visible = ref(false);
3558
- const anchor = ref(null);
3569
+ const menu = ref(null);
3559
3570
  async function open(point, entries) {
3560
3571
  items.value = entries;
3561
- pos.value = point;
3562
- visible.value = true;
3563
3572
  await nextTick();
3564
- setTimeout(() => anchor.value?.querySelector(".uid-menu-trigger")?.click(), 0);
3573
+ menu.value?.openAt(point.x, point.y);
3565
3574
  }
3566
3575
  __expose({ open });
3567
3576
  return (_ctx, _cache) => {
3568
- return visible.value ? (openBlock(), createElementBlock("div", {
3569
- key: 0,
3570
- ref_key: "anchor",
3571
- ref: anchor,
3572
- class: "fl-context-anchor",
3573
- "data-fl-ui": "",
3574
- style: normalizeStyle({
3575
- left: `${pos.value.x}px`,
3576
- top: `${pos.value.y}px`
3577
- })
3578
- }, [createVNode(unref(UidMenu), null, {
3579
- trigger: withCtx(() => [..._cache[0] || (_cache[0] = [createElementVNode("span", {
3580
- class: "fl-context-anchor__trigger",
3581
- "aria-hidden": "true"
3582
- }, null, -1)])]),
3577
+ return openBlock(), createBlock(unref(UidMenu), {
3578
+ ref_key: "menu",
3579
+ ref: menu,
3580
+ class: "fl-context-menu"
3581
+ }, {
3583
3582
  default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(items.value, (item, i) => {
3584
3583
  return openBlock(), createElementBlock(Fragment, { key: i }, [item.separator ? (openBlock(), createBlock(unref(UidMenuSeparator), { key: 0 })) : item.children ? (openBlock(), createBlock(unref(UidSubMenu), {
3585
3584
  key: 1,
@@ -3619,7 +3618,7 @@ var FlowContextMenu_default = /* @__PURE__ */ defineComponent({
3619
3618
  ]))], 64);
3620
3619
  }), 128))]),
3621
3620
  _: 1
3622
- })], 4)) : createCommentVNode("", true);
3621
+ }, 512);
3623
3622
  };
3624
3623
  }
3625
3624
  });
@@ -4592,7 +4591,7 @@ var FlowEditor_default = /* @__PURE__ */ defineComponent({
4592
4591
  edgeStyle: { default: "orthogonal" },
4593
4592
  nodeRenderers: { default: () => ({}) },
4594
4593
  nodeIcons: { default: () => ({}) },
4595
- locale: { default: "en" },
4594
+ locale: { default: void 0 },
4596
4595
  labels: { default: void 0 },
4597
4596
  minimap: {
4598
4597
  type: Boolean,
@@ -4624,8 +4623,8 @@ var FlowEditor_default = /* @__PURE__ */ defineComponent({
4624
4623
  */
4625
4624
  const props = __props;
4626
4625
  const emit = __emit;
4627
- const L = computed(() => resolveLabels(props.locale, props.labels));
4628
- const kitLocale = computed(() => props.locale === "ru" ? ru$1 : en$1);
4626
+ const { lang, wrapper, wrapperProps } = useLocaleScope(() => props.locale);
4627
+ const L = computed(() => resolveLabels(lang.value, props.labels));
4629
4628
  const history = new History(toRaw(props.modelValue));
4630
4629
  const root = shallowRef(toRaw(props.modelValue));
4631
4630
  const revision = ref(0);
@@ -5289,7 +5288,7 @@ var FlowEditor_default = /* @__PURE__ */ defineComponent({
5289
5288
  issues
5290
5289
  });
5291
5290
  return (_ctx, _cache) => {
5292
- return openBlock(), createBlock(unref(UidLocaleProvider), { locale: kitLocale.value }, {
5291
+ return openBlock(), createBlock(resolveDynamicComponent(unref(wrapper)), normalizeProps(guardReactiveProps(unref(wrapperProps))), {
5293
5292
  default: withCtx(() => [createElementVNode("div", mergeProps({
5294
5293
  ref_key: "rootEl",
5295
5294
  ref: rootEl
@@ -5298,7 +5297,7 @@ var FlowEditor_default = /* @__PURE__ */ defineComponent({
5298
5297
  "is-narrow": narrow.value,
5299
5298
  "is-readonly": __props.readonly
5300
5299
  }],
5301
- lang: __props.locale,
5300
+ lang: unref(lang),
5302
5301
  role: "application",
5303
5302
  "aria-label": L.value.editor,
5304
5303
  onKeydown: onKeyDown
@@ -5429,13 +5428,14 @@ var FlowEditor_default = /* @__PURE__ */ defineComponent({
5429
5428
  }, {
5430
5429
  default: withCtx(() => [createVNode(unref(UidButton), {
5431
5430
  size: "sm",
5432
- variant: "ghost",
5431
+ variant: snapOn.value ? "secondary" : "ghost",
5433
5432
  icon: unref(Magnet),
5434
5433
  "aria-label": L.value.snapToGrid,
5435
5434
  "aria-pressed": snapOn.value,
5436
5435
  class: normalizeClass(["fl-toolbar__snap fl-toggle", { "is-active": snapOn.value }]),
5437
5436
  onClick: _cache[1] || (_cache[1] = ($event) => snapOn.value = !snapOn.value)
5438
5437
  }, null, 8, [
5438
+ "variant",
5439
5439
  "icon",
5440
5440
  "aria-label",
5441
5441
  "aria-pressed",
@@ -5762,7 +5762,7 @@ var FlowEditor_default = /* @__PURE__ */ defineComponent({
5762
5762
  __props.toastProvider ? (openBlock(), createBlock(unref(UidToastProvider), { key: 1 })) : createCommentVNode("", true)
5763
5763
  ], 16, _hoisted_1)]),
5764
5764
  _: 3
5765
- }, 8, ["locale"]);
5765
+ }, 16);
5766
5766
  };
5767
5767
  }
5768
5768
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dskripchenko/flow",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Process and state-machine graphs: a zero-dependency TypeScript core (model, validation, layered layout, monitoring state, polling, undoable editing commands) and Vue components on @dskripchenko/ui — viewer, live monitoring overlay, drill-down and a visual editor",
5
5
  "keywords": [
6
6
  "flow",
@@ -85,7 +85,7 @@
85
85
  },
86
86
  "peerDependencies": {
87
87
  "@dskripchenko/diagrams": "^1.1.0",
88
- "@dskripchenko/ui": "^1.14.0",
88
+ "@dskripchenko/ui": "^1.15.0",
89
89
  "vue": "^3.4.0"
90
90
  },
91
91
  "peerDependenciesMeta": {
@@ -102,7 +102,7 @@
102
102
  "devDependencies": {
103
103
  "@changesets/cli": "^3.0.3",
104
104
  "@dskripchenko/diagrams": "^1.1.0",
105
- "@dskripchenko/ui": "^1.14.0",
105
+ "@dskripchenko/ui": "^1.15.0",
106
106
  "@eslint/js": "^10.0.1",
107
107
  "@microsoft/api-extractor": "^7.59.4",
108
108
  "@types/node": "^22.20.5",