@flowgram-vue/fixed-semi-materials 0.2.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.
@@ -0,0 +1,66 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { ref } from 'vue';
7
+ import { nanoid } from 'nanoid';
8
+ import {
9
+ FlowNodeTransformData,
10
+ FlowOperationBaseService,
11
+ type FlowNodeEntity,
12
+ } from '@flowgram-vue/document';
13
+ import { usePlayground, useService } from '@flowgram-vue/core';
14
+
15
+ import { IconPlus } from '../assets';
16
+ import NodesPicker from './nodes.vue';
17
+
18
+ defineOptions({ name: 'Adder' });
19
+
20
+ const props = defineProps<{
21
+ from: FlowNodeEntity;
22
+ to?: FlowNodeEntity;
23
+ hoverActivated?: boolean;
24
+ }>();
25
+
26
+ const visible = ref(false);
27
+ const playground = usePlayground();
28
+ const flowOperationService = useService(FlowOperationBaseService);
29
+
30
+ const add = (addProps: { type: string; blocks?: () => unknown }) => {
31
+ const block = flowOperationService.addFromNode(props.from, {
32
+ id: addProps.type + nanoid(5),
33
+ type: addProps.type,
34
+ blocks: addProps.blocks ? (addProps.blocks() as never) : undefined,
35
+ });
36
+ setTimeout(() => {
37
+ playground.scrollToView({
38
+ bounds: block.getData<FlowNodeTransformData>(FlowNodeTransformData)!.bounds,
39
+ scrollToCenter: true,
40
+ });
41
+ }, 10);
42
+ visible.value = false;
43
+ };
44
+
45
+ const onMouseDown = (e: MouseEvent) => e.stopPropagation();
46
+ </script>
47
+
48
+ <template>
49
+ <div
50
+ v-if="!playground.config.readonlyOrDisabled"
51
+ class="fg-fixed-chrome"
52
+ style="position: relative"
53
+ >
54
+ <div
55
+ class="fg-adder-wrap"
56
+ :class="{ 'is-hovered': props.hoverActivated }"
57
+ @mousedown="onMouseDown"
58
+ @click.stop="visible = !visible"
59
+ >
60
+ <IconPlus v-if="props.hoverActivated" />
61
+ </div>
62
+ <div v-if="visible" class="fg-popover" @mousedown.stop>
63
+ <NodesPicker :on-select="add" />
64
+ </div>
65
+ </div>
66
+ </template>
@@ -0,0 +1,55 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { nanoid } from 'nanoid';
7
+ import {
8
+ FlowNodeRenderData,
9
+ FlowNodeTransformData,
10
+ FlowOperationBaseService,
11
+ type FlowNodeEntity,
12
+ } from '@flowgram-vue/document';
13
+ import { usePlayground, useService } from '@flowgram-vue/core';
14
+
15
+ import { IconPlus } from '../assets';
16
+
17
+ defineOptions({ name: 'BranchAdder' });
18
+
19
+ const props = defineProps<{
20
+ activated?: boolean;
21
+ node: FlowNodeEntity;
22
+ }>();
23
+
24
+ const nodeData = props.node.firstChild?.getData<FlowNodeRenderData>(FlowNodeRenderData);
25
+ const playground = usePlayground();
26
+ const flowOperationService = useService(FlowOperationBaseService);
27
+ const isVertical = props.node.isVertical;
28
+
29
+ function addBranch() {
30
+ const block = flowOperationService.addBlock(props.node, { id: nanoid(5) });
31
+ setTimeout(() => {
32
+ playground.scrollToView({
33
+ bounds: block.getData<FlowNodeTransformData>(FlowNodeTransformData)!.bounds,
34
+ scrollToCenter: true,
35
+ });
36
+ }, 10);
37
+ }
38
+ </script>
39
+
40
+ <template>
41
+ <div
42
+ v-if="!playground.config.readonlyOrDisabled"
43
+ class="fg-branch-adder"
44
+ :class="{
45
+ 'is-activated': props.activated || nodeData?.hovered,
46
+ 'is-horizontal': !isVertical,
47
+ }"
48
+ @mouseenter="nodeData?.toggleMouseEnter()"
49
+ @mouseleave="nodeData?.toggleMouseLeave()"
50
+ >
51
+ <div class="fg-branch-hit" aria-hidden="true" @click="addBranch">
52
+ <IconPlus />
53
+ </div>
54
+ </div>
55
+ </template>
@@ -0,0 +1,84 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import {
7
+ FlowNodeEntity,
8
+ FlowNodeRenderData,
9
+ FlowNodeTransformData,
10
+ } from '@flowgram-vue/document';
11
+ import { usePlayground } from '@flowgram-vue/core';
12
+ import type { CSSProperties } from 'vue';
13
+
14
+ import { Arrow } from '../assets';
15
+
16
+ defineOptions({ name: 'Collapse' });
17
+
18
+ const props = defineProps<{
19
+ node: FlowNodeEntity;
20
+ collapseNode: FlowNodeEntity;
21
+ activateNode?: FlowNodeEntity;
22
+ forceVisible?: boolean;
23
+ hoverActivated?: boolean;
24
+ style?: CSSProperties;
25
+ }>();
26
+
27
+ const playground = usePlayground();
28
+ const activateData = props.activateNode?.getData(FlowNodeRenderData);
29
+ const transform = props.collapseNode.getData(FlowNodeTransformData);
30
+
31
+ const scrollToActivateNode = () => {
32
+ setTimeout(() => {
33
+ playground.config.scrollToView({
34
+ position: props.activateNode?.getData(FlowNodeTransformData)?.outputPoint,
35
+ scrollToCenter: true,
36
+ });
37
+ }, 100);
38
+ };
39
+
40
+ const collapseBlock = () => {
41
+ if (!transform) {
42
+ return;
43
+ }
44
+ transform.collapsed = true;
45
+ activateData?.toggleMouseLeave();
46
+ scrollToActivateNode();
47
+ };
48
+
49
+ const openBlock = () => {
50
+ if (!transform) {
51
+ return;
52
+ }
53
+ transform.collapsed = false;
54
+ scrollToActivateNode();
55
+ };
56
+
57
+ const childCount = transform
58
+ ? transform.collapsed
59
+ ? props.collapseNode.allCollapsedChildren.filter(
60
+ (child) => !child.hidden && child !== props.activateNode
61
+ ).length
62
+ : 0
63
+ : 0;
64
+
65
+ const circleColor = '#fff';
66
+ const color = props.hoverActivated ? '#82A7FC' : '#BBBFC4';
67
+ </script>
68
+
69
+ <template>
70
+ <div
71
+ v-if="transform"
72
+ class="fg-collapse"
73
+ :class="{
74
+ 'is-hovered': props.hoverActivated,
75
+ 'is-horizontal-collapse': !props.activateNode?.isVertical && !transform.collapsed,
76
+ }"
77
+ aria-hidden="true"
78
+ :style="props.style"
79
+ @click="transform.collapsed ? openBlock() : collapseBlock()"
80
+ >
81
+ <template v-if="transform.collapsed">{{ childCount }}</template>
82
+ <Arrow v-else :color="color" :circle-color="circleColor" />
83
+ </div>
84
+ </template>
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export const primary = 'hsl(252 62% 54.9%)';
7
+ export const primaryOpacity09 = 'hsl(252deg 62% 55% / 9%)';
@@ -0,0 +1,54 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { FlowNodeTransformData, type FlowNodeEntity } from '@flowgram-vue/document';
7
+
8
+ import { Ellipse } from '../assets';
9
+
10
+ defineOptions({ name: 'DragHighlightAdder' });
11
+
12
+ const props = defineProps<{
13
+ node: FlowNodeEntity;
14
+ }>();
15
+
16
+ const getMinSize = (preWidth: number, nextWidth: number): number => {
17
+ if (!preWidth || preWidth < 0) {
18
+ return 0;
19
+ }
20
+ if (!nextWidth || nextWidth < 0) {
21
+ return preWidth;
22
+ }
23
+ return Math.min(preWidth, nextWidth) || 0;
24
+ };
25
+
26
+ const transformBounds = props.node.getData<FlowNodeTransformData>(FlowNodeTransformData)?.bounds;
27
+ const isVertical = props.node.isVertical;
28
+
29
+ let barWidth = 0;
30
+ let barHeight = 0;
31
+ if (isVertical) {
32
+ const preWidth = (transformBounds?.width || 0) - 16;
33
+ const nextNodeBounds =
34
+ props.node?.next?.getData<FlowNodeTransformData>(FlowNodeTransformData)?.bounds?.width;
35
+ const nextWidth = (nextNodeBounds || 0) - 16;
36
+ barWidth = getMinSize(preWidth, nextWidth);
37
+ barHeight = 2;
38
+ } else {
39
+ const preHeight = (transformBounds?.height || 0) - 16;
40
+ const nextNodeBounds =
41
+ props.node?.next?.getData<FlowNodeTransformData>(FlowNodeTransformData)?.bounds?.height;
42
+ const nextHeight = (nextNodeBounds || 0) - 16;
43
+ barWidth = 2;
44
+ barHeight = getMinSize(preHeight, nextHeight);
45
+ }
46
+ </script>
47
+
48
+ <template>
49
+ <div class="fg-highlight-line" :style="{ flexDirection: isVertical ? 'row' : 'column' }">
50
+ <Ellipse />
51
+ <div class="fg-highlight-bar" :style="{ width: `${barWidth}px`, height: `${barHeight}px` }" />
52
+ <Ellipse />
53
+ </div>
54
+ </template>
@@ -0,0 +1,45 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import type { FlowNodeEntity, FlowNodeJSON } from '@flowgram-vue/document';
7
+
8
+ defineOptions({ name: 'DragNode' });
9
+
10
+ const props = defineProps<{
11
+ dragStart?: FlowNodeEntity;
12
+ dragJSON?: FlowNodeJSON;
13
+ dragNodes: FlowNodeEntity[];
14
+ }>();
15
+
16
+ const dragLength = (props.dragNodes || [])
17
+ .map((_node) =>
18
+ _node.allCollapsedChildren.length
19
+ ? _node.allCollapsedChildren.filter((_n) => !_n.hidden).length
20
+ : 1
21
+ )
22
+ .reduce((acm, curr) => acm + curr, 0);
23
+
24
+ const label = props.dragStart?.id || props.dragJSON?.id;
25
+ </script>
26
+
27
+ <template>
28
+ <div class="fg-drag-node">
29
+ {{ label }}
30
+ <template v-if="dragLength > 1">
31
+ <div class="fg-drag-counts">{{ dragLength }}</div>
32
+ <div
33
+ class="fg-drag-node"
34
+ :style="{
35
+ position: 'absolute',
36
+ top: '5px',
37
+ right: '-5px',
38
+ left: '5px',
39
+ bottom: '-5px',
40
+ opacity: 0.5,
41
+ }"
42
+ />
43
+ </template>
44
+ </div>
45
+ </template>
@@ -0,0 +1,40 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { computed } from 'vue';
7
+ import { usePlayground } from '@flowgram-vue/core';
8
+ import { FlowDragLayer } from '@flowgram-vue/renderer';
9
+ import type { FlowNodeEntity } from '@flowgram-vue/document';
10
+
11
+ defineOptions({ name: 'DraggingAdder' });
12
+
13
+ const props = defineProps<{
14
+ from?: FlowNodeEntity;
15
+ }>();
16
+
17
+ const playground = usePlayground();
18
+ const layer = playground.getLayer(FlowDragLayer);
19
+
20
+ const canShow = computed(() => {
21
+ if (!layer) {
22
+ return false;
23
+ }
24
+ if (
25
+ layer.options.canDrop &&
26
+ !layer.options.canDrop({
27
+ dragNodes: layer.dragEntities || [],
28
+ dropNode: props.from as FlowNodeEntity,
29
+ isBranch: false,
30
+ })
31
+ ) {
32
+ return false;
33
+ }
34
+ return true;
35
+ });
36
+ </script>
37
+
38
+ <template>
39
+ <div v-if="canShow" class="fg-dragging-adder" />
40
+ </template>
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { FlowRendererKey } from '@flowgram-vue/renderer';
7
+
8
+ import { Ellipse } from '../assets';
9
+ import Adder from './adder.vue';
10
+ import BranchAdder from './branch-adder.vue';
11
+ import Collapse from './collapse.vue';
12
+ import DragHighlightAdder from './drag-highlight-adder.vue';
13
+ import DragNode from './drag-node.vue';
14
+ import DraggingAdder from './dragging-adder.vue';
15
+ import SlotAdder from './slot-adder.vue';
16
+ import SlotCollapse from './slot-collapse.vue';
17
+ import TryCatchCollapse from './try-catch-collapse.vue';
18
+
19
+ export { default as PlaygroundTools } from './playground-tools.vue';
20
+
21
+ export const defaultFixedSemiMaterials = {
22
+ [FlowRendererKey.ADDER]: Adder,
23
+ [FlowRendererKey.COLLAPSE]: Collapse,
24
+ [FlowRendererKey.TRY_CATCH_COLLAPSE]: TryCatchCollapse,
25
+ [FlowRendererKey.BRANCH_ADDER]: BranchAdder,
26
+ [FlowRendererKey.DRAG_NODE]: DragNode,
27
+ [FlowRendererKey.DRAGGABLE_ADDER]: DraggingAdder,
28
+ [FlowRendererKey.DRAG_HIGHLIGHT_ADDER]: DragHighlightAdder,
29
+ [FlowRendererKey.DRAG_BRANCH_HIGHLIGHT_ADDER]: Ellipse,
30
+ [FlowRendererKey.SLOT_COLLAPSE]: SlotCollapse,
31
+ [FlowRendererKey.SLOT_ADDER]: SlotAdder,
32
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { h, type Component } from 'vue';
7
+ import { nanoid } from 'nanoid';
8
+
9
+ import {
10
+ BiBootstrapReboot,
11
+ BiCloud,
12
+ FeAlignCenter,
13
+ IconParkRightBranch,
14
+ IconStyleBorder,
15
+ PhCircleBold,
16
+ } from '../assets';
17
+
18
+ export interface NodeMeta {
19
+ type: string;
20
+ label: string;
21
+ icon: Component;
22
+ schemaType?: string;
23
+ branchEnd?: boolean;
24
+ blocks?: () => Array<{ id: string }>;
25
+ }
26
+
27
+ const metadata = {
28
+ nodes: [
29
+ {
30
+ type: 'start',
31
+ label: 'Start',
32
+ icon: IconStyleBorder,
33
+ },
34
+ {
35
+ type: 'dynamicSplit',
36
+ label: 'Split Branch',
37
+ icon: IconParkRightBranch,
38
+ blocks() {
39
+ return [{ id: nanoid(5) }, { id: nanoid(5) }];
40
+ },
41
+ },
42
+ {
43
+ type: 'end',
44
+ label: 'Branch End',
45
+ icon: FeAlignCenter,
46
+ branchEnd: true,
47
+ },
48
+ {
49
+ type: 'loop',
50
+ schemaType: 'loop',
51
+ label: 'Loop',
52
+ icon: BiBootstrapReboot,
53
+ },
54
+ {
55
+ type: 'tryCatch',
56
+ schemaType: 'tryCatch',
57
+ label: 'TryCatch',
58
+ icon: IconParkRightBranch,
59
+ blocks() {
60
+ return [
61
+ { id: `try_${nanoid(5)}` },
62
+ { id: `catch_${nanoid(5)}` },
63
+ { id: `catch_${nanoid(5)}` },
64
+ ];
65
+ },
66
+ },
67
+ {
68
+ type: 'noop',
69
+ label: 'Noop Node',
70
+ icon: BiCloud,
71
+ },
72
+ {
73
+ type: 'end',
74
+ label: 'End',
75
+ icon: PhCircleBold,
76
+ },
77
+ ] as NodeMeta[],
78
+ find(type: string) {
79
+ return metadata.nodes.find((m) => m.type === type);
80
+ },
81
+ };
82
+
83
+ export function renderMetaIcon(icon: Component) {
84
+ return h(icon);
85
+ }
86
+
87
+ export default metadata;
@@ -0,0 +1,32 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import type { FlowNodeEntity } from '@flowgram-vue/document';
7
+ import metadata from './metadata';
8
+
9
+ defineOptions({ name: 'NodesPicker' });
10
+
11
+ defineProps<{
12
+ onSelect: (meta: (typeof metadata.nodes)[number]) => void;
13
+ }>();
14
+
15
+ const addings = metadata.nodes.filter((node) => node.type !== 'start');
16
+ </script>
17
+
18
+ <template>
19
+ <div class="fg-nodes-wrap" :style="{ width: `${80 * 2 + 20}px` }">
20
+ <div
21
+ v-for="(n, i) in addings"
22
+ :key="i"
23
+ class="fg-node-wrap"
24
+ @click="onSelect(n)"
25
+ >
26
+ <div style="font-size: 14px">
27
+ <component :is="n.icon" />
28
+ </div>
29
+ <div class="fg-node-label">{{ n.label }}</div>
30
+ </div>
31
+ </div>
32
+ </template>
@@ -0,0 +1,33 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { computed } from 'vue';
7
+
8
+ import { useFixedPlaygroundTools } from '../composables/use-fixed-playground-tools';
9
+
10
+ defineOptions({ name: 'PlaygroundTools' });
11
+
12
+ const props = defineProps<{
13
+ layoutText?: string;
14
+ }>();
15
+
16
+ const tools = useFixedPlaygroundTools();
17
+ const zoomLabel = computed(() => `${Math.floor(tools.zoom * 100)}%`);
18
+ </script>
19
+
20
+ <template>
21
+ <div class="fg-tools">
22
+ <label>
23
+ <input type="checkbox" :checked="!tools.isVertical" @change="tools.changeLayout()" />
24
+ {{ props.layoutText || 'isHorizontal' }}
25
+ </label>
26
+ <button type="button" title="fit view" @click="tools.fitView()">fit</button>
27
+ <button type="button" title="zoom out" @click="tools.zoomout()">-</button>
28
+ <button type="button" title="zoom in" @click="tools.zoomin()">+</button>
29
+ <button type="button" title="undo" :disabled="!tools.canUndo" @click="tools.undo()">undo</button>
30
+ <button type="button" title="redo" :disabled="!tools.canRedo" @click="tools.redo()">redo</button>
31
+ <span>{{ zoomLabel }}</span>
32
+ </div>
33
+ </template>
@@ -0,0 +1,44 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { nanoid } from 'nanoid';
7
+ import {
8
+ FlowDocument,
9
+ FlowNodeRenderData,
10
+ type FlowNodeEntity,
11
+ } from '@flowgram-vue/document';
12
+ import { useService } from '@flowgram-vue/core';
13
+
14
+ import { IconPlus } from '../assets';
15
+
16
+ defineOptions({ name: 'SlotAdder' });
17
+
18
+ const props = defineProps<{
19
+ node: FlowNodeEntity;
20
+ }>();
21
+
22
+ const nodeData = props.node.firstChild?.getData<FlowNodeRenderData>(FlowNodeRenderData);
23
+ const document = useService(FlowDocument) as FlowDocument;
24
+
25
+ function addPort() {
26
+ document.addNode({
27
+ id: nanoid(5),
28
+ type: 'custom',
29
+ parent: props.node,
30
+ });
31
+ }
32
+ </script>
33
+
34
+ <template>
35
+ <div
36
+ class="fg-slot-adder"
37
+ @mouseenter="nodeData?.toggleMouseEnter()"
38
+ @mouseleave="nodeData?.toggleMouseLeave()"
39
+ >
40
+ <button type="button" class="fg-native-btn" @click="addPort">
41
+ <IconPlus />
42
+ </button>
43
+ </div>
44
+ </template>
@@ -0,0 +1,55 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { ref, computed } from 'vue';
7
+ import {
8
+ FlowNodeRenderData,
9
+ FlowNodeTransformData,
10
+ type FlowNodeEntity,
11
+ } from '@flowgram-vue/document';
12
+
13
+ import Collapse from './collapse.vue';
14
+
15
+ defineOptions({ name: 'SlotCollapse' });
16
+
17
+ const props = defineProps<{
18
+ node: FlowNodeEntity;
19
+ }>();
20
+
21
+ const hoverActivated = ref(false);
22
+ const icon = props.node.firstChild!;
23
+ const iconActivated = icon.getData(FlowNodeRenderData).activated;
24
+ const iconHeight = icon.getData(FlowNodeTransformData).size.height;
25
+ const isChildVisible = computed(
26
+ () => props.node.collapsed || hoverActivated.value || iconActivated
27
+ );
28
+ </script>
29
+
30
+ <template>
31
+ <div
32
+ :style="{
33
+ width: '30px',
34
+ height: `${iconHeight || 100}px`,
35
+ display: 'flex',
36
+ alignItems: 'center',
37
+ justifyContent: 'center',
38
+ }"
39
+ @mouseenter="hoverActivated = true"
40
+ @mouseleave="hoverActivated = false"
41
+ >
42
+ <Collapse
43
+ v-if="isChildVisible"
44
+ :style="
45
+ !props.node.collapsed
46
+ ? { transform: props.node.isVertical ? 'rotate(-90deg)' : 'rotate(90deg)' }
47
+ : {}
48
+ "
49
+ :node="props.node"
50
+ :activate-node="icon"
51
+ :collapse-node="props.node"
52
+ :hover-activated="hoverActivated"
53
+ />
54
+ </div>
55
+ </template>