@likec4/diagram 1.32.0 → 1.32.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/bundle/{chunk-BvTQaUYl.js → chunk-DlHyKoeo.js} +236 -210
- package/bundle/custom.js +50 -49
- package/bundle/index.js +144 -121
- package/dist/LikeC4Diagram.js +1 -1
- package/dist/LikeC4Diagram.props.d.ts +14 -3
- package/dist/bundle/LikeC4View.js +16 -15
- package/dist/bundle/ReactLikeC4.js +7 -6
- package/dist/bundle/index.js +1 -1
- package/dist/custom/customNodes.d.ts +5 -0
- package/dist/custom/hooks.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/likec4diagram/DiagramXYFlow.js +3 -3
- package/dist/likec4diagram/custom/nodes/toolbar/_shared.d.ts +5 -5
- package/dist/likec4model/index.d.ts +1 -1
- package/dist/likec4model/useLikeC4Model.d.ts +2 -1
- package/dist/likec4model/useLikeC4Model.js +13 -2
- package/dist/overlays/overlay/Overlay.js +24 -4
- package/dist/search/Search.d.ts +2 -3
- package/dist/search/Search.js +27 -46
- package/dist/search/components/ElementsColumn.js +12 -13
- package/dist/search/components/PickView.js +6 -6
- package/dist/search/components/SearchByTags.js +5 -2
- package/dist/search/components/SearchInput.js +16 -14
- package/dist/search/components/ViewsColum.js +16 -9
- package/dist/search/components/utils.d.ts +3 -2
- package/dist/search/components/utils.js +37 -5
- package/dist/search/hooks.d.ts +2 -8
- package/dist/search/hooks.js +4 -4
- package/dist/state/DiagramActorProvider.js +1 -1
- package/dist/state/diagram-machine.d.ts +216 -8
- package/dist/state/diagram-machine.js +8 -6
- package/package.json +9 -8
- package/src/LikeC4Diagram.props.ts +14 -3
- package/src/LikeC4Diagram.tsx +1 -1
- package/src/bundle/LikeC4View.tsx +12 -19
- package/src/bundle/ReactLikeC4.tsx +2 -10
- package/src/custom/customNodes.tsx +5 -0
- package/src/custom/hooks.ts +1 -0
- package/src/likec4diagram/DiagramXYFlow.tsx +3 -3
- package/src/likec4model/index.ts +2 -0
- package/src/likec4model/useLikeC4Model.ts +16 -2
- package/src/overlays/overlay/Overlay.tsx +20 -5
- package/src/search/Search.tsx +34 -51
- package/src/search/components/ElementsColumn.tsx +12 -15
- package/src/search/components/PickView.tsx +41 -47
- package/src/search/components/SearchByTags.tsx +5 -2
- package/src/search/components/SearchInput.tsx +17 -14
- package/src/search/components/ViewsColum.tsx +18 -10
- package/src/search/components/utils.ts +41 -4
- package/src/search/hooks.ts +5 -8
- package/src/state/DiagramActorProvider.tsx +2 -1
- package/src/state/diagram-machine.ts +8 -6
|
@@ -134,7 +134,15 @@ export interface LikeC4DiagramProperties<A extends aux.Any> {
|
|
|
134
134
|
*/
|
|
135
135
|
nodesDraggable?: boolean | undefined
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Initial width of the diagram
|
|
139
|
+
* (supposed to be used during SSR)
|
|
140
|
+
*/
|
|
137
141
|
initialWidth?: number | undefined
|
|
142
|
+
/**
|
|
143
|
+
* Initial height of the diagram
|
|
144
|
+
* (supposed to be used during SSR)
|
|
145
|
+
*/
|
|
138
146
|
initialHeight?: number | undefined
|
|
139
147
|
|
|
140
148
|
/**
|
|
@@ -156,7 +164,9 @@ export interface LikeC4DiagramProperties<A extends aux.Any> {
|
|
|
156
164
|
showNavigationButtons?: undefined | boolean
|
|
157
165
|
|
|
158
166
|
/**
|
|
159
|
-
* Display notations
|
|
167
|
+
* Display notations in the bottom right corner
|
|
168
|
+
* (Active if only notations are present)
|
|
169
|
+
*
|
|
160
170
|
* @default true
|
|
161
171
|
*/
|
|
162
172
|
showNotations?: boolean | undefined
|
|
@@ -200,7 +210,7 @@ export interface LikeC4DiagramProperties<A extends aux.Any> {
|
|
|
200
210
|
|
|
201
211
|
/**
|
|
202
212
|
* Display element tags in the bottom left corner
|
|
203
|
-
* @default
|
|
213
|
+
* @default false
|
|
204
214
|
*/
|
|
205
215
|
enableElementTags?: boolean | undefined
|
|
206
216
|
|
|
@@ -212,8 +222,9 @@ export interface LikeC4DiagramProperties<A extends aux.Any> {
|
|
|
212
222
|
|
|
213
223
|
/**
|
|
214
224
|
* Improve performance by hiding certain elements and reducing visual effects (disable mix-blend, shadows, animations)
|
|
225
|
+
* Enable it if you have a large or static view
|
|
215
226
|
*
|
|
216
|
-
* @default 'auto' - will be
|
|
227
|
+
* @default 'auto' - will be `true` if view has more then 3000 * 2000 pixels
|
|
217
228
|
*/
|
|
218
229
|
reduceGraphics?: 'auto' | boolean | undefined
|
|
219
230
|
|
package/src/LikeC4Diagram.tsx
CHANGED
|
@@ -42,7 +42,7 @@ export function LikeC4Diagram<A extends Any = Any>({
|
|
|
42
42
|
pannable = true,
|
|
43
43
|
zoomable = true,
|
|
44
44
|
background = 'dots',
|
|
45
|
-
enableElementTags =
|
|
45
|
+
enableElementTags = false,
|
|
46
46
|
enableFocusMode = false,
|
|
47
47
|
enableElementDetails = false,
|
|
48
48
|
enableRelationshipDetails = enableElementDetails,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Any, DiagramView
|
|
1
|
+
import type { Any, DiagramView } from '@likec4/core/types'
|
|
2
2
|
import type * as aux from '@likec4/core/types/aux'
|
|
3
3
|
import { cx } from '@likec4/styles/css'
|
|
4
4
|
import { ActionIcon, Box } from '@mantine/core'
|
|
@@ -20,27 +20,20 @@ export function LikeC4View<A extends aux.Any = aux.UnknownLayouted>({
|
|
|
20
20
|
...props
|
|
21
21
|
}: LikeC4ViewProps<A>) {
|
|
22
22
|
const likec4model = useLikeC4Model()
|
|
23
|
-
const view = likec4model
|
|
23
|
+
const view = likec4model.findView(viewId)
|
|
24
24
|
|
|
25
|
-
if (!
|
|
26
|
-
return
|
|
27
|
-
<ErrorMessage>
|
|
28
|
-
LikeC4Model not found. Make sure you have LikeC4ModelProvider.
|
|
29
|
-
</ErrorMessage>
|
|
30
|
-
)
|
|
25
|
+
if (!view) {
|
|
26
|
+
return <ViewNotFound viewId={viewId} />
|
|
31
27
|
}
|
|
32
|
-
|
|
28
|
+
|
|
29
|
+
if (!view.isDiagram()) {
|
|
33
30
|
return (
|
|
34
31
|
<ErrorMessage>
|
|
35
|
-
|
|
32
|
+
LikeC4 View "${viewId}" is not layouted. Make sure you have LikeC4ModelProvider with layouted model.
|
|
36
33
|
</ErrorMessage>
|
|
37
34
|
)
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
if (!view || !view.isDiagram()) {
|
|
41
|
-
return <ViewNotFound viewId={viewId} />
|
|
42
|
-
}
|
|
43
|
-
|
|
44
37
|
return <LikeC4ViewInner view={view.$view} {...props} />
|
|
45
38
|
}
|
|
46
39
|
|
|
@@ -57,7 +50,7 @@ const LikeC4ViewInner = memo<LikeC4ViewInnerProps<aux.Any>>(({
|
|
|
57
50
|
injectFontCss = true,
|
|
58
51
|
controls = false,
|
|
59
52
|
fitView = true,
|
|
60
|
-
fitViewPadding = '
|
|
53
|
+
fitViewPadding = '16px',
|
|
61
54
|
background = 'transparent',
|
|
62
55
|
browser = true,
|
|
63
56
|
showDiagramTitle = false,
|
|
@@ -154,11 +147,11 @@ const LikeC4ViewInner = memo<LikeC4ViewInnerProps<aux.Any>>(({
|
|
|
154
147
|
{...props}
|
|
155
148
|
/>
|
|
156
149
|
{browserView && (
|
|
157
|
-
<Overlay onClose={() => onNavigateTo(null)}>
|
|
150
|
+
<Overlay openDelay={0} onClose={() => onNavigateTo(null)}>
|
|
158
151
|
<LikeC4Diagram
|
|
159
152
|
view={browserView}
|
|
160
153
|
background="dots"
|
|
161
|
-
onNavigateTo={
|
|
154
|
+
onNavigateTo={onNavigateTo}
|
|
162
155
|
enableDynamicViewWalkthrough
|
|
163
156
|
enableFocusMode
|
|
164
157
|
enableRelationshipBrowser
|
|
@@ -169,12 +162,12 @@ const LikeC4ViewInner = memo<LikeC4ViewInnerProps<aux.Any>>(({
|
|
|
169
162
|
controls
|
|
170
163
|
readonly
|
|
171
164
|
fitView
|
|
172
|
-
fitViewPadding={'
|
|
173
|
-
renderNodes={renderNodes}
|
|
165
|
+
fitViewPadding={'32px'}
|
|
174
166
|
{...props}
|
|
175
167
|
{...browserProps}
|
|
176
168
|
showNotations={(browserProps.showNotations ?? true) &&
|
|
177
169
|
(browserView.notation?.nodes.length ?? 0) > 0}
|
|
170
|
+
renderNodes={renderNodes}
|
|
178
171
|
/>
|
|
179
172
|
<Box pos="absolute" top={'1rem'} right={'1rem'}>
|
|
180
173
|
<ActionIcon
|
|
@@ -47,15 +47,7 @@ export function ReactLikeC4<A extends aux.Any = aux.UnknownLayouted>({
|
|
|
47
47
|
...props
|
|
48
48
|
}: ReactLikeC4Props<A>) {
|
|
49
49
|
const likec4model = useLikeC4Model()
|
|
50
|
-
const view = likec4model
|
|
51
|
-
|
|
52
|
-
if (!likec4model) {
|
|
53
|
-
return (
|
|
54
|
-
<ErrorMessage>
|
|
55
|
-
LikeC4Model not found. Make sure you have LikeC4ModelProvider.
|
|
56
|
-
</ErrorMessage>
|
|
57
|
-
)
|
|
58
|
-
}
|
|
50
|
+
const view = likec4model.findView(viewId)
|
|
59
51
|
|
|
60
52
|
if (!view) {
|
|
61
53
|
return <ViewNotFound viewId={viewId} />
|
|
@@ -64,7 +56,7 @@ export function ReactLikeC4<A extends aux.Any = aux.UnknownLayouted>({
|
|
|
64
56
|
if (!view.isDiagram()) {
|
|
65
57
|
return (
|
|
66
58
|
<ErrorMessage>
|
|
67
|
-
|
|
59
|
+
LikeC4 View "${viewId}" is not layouted. Make sure you have LikeC4ModelProvider with layouted model.
|
|
68
60
|
</ErrorMessage>
|
|
69
61
|
)
|
|
70
62
|
}
|
|
@@ -45,15 +45,20 @@ export type CustomElementNodeProps<M extends Any = Any> = {
|
|
|
45
45
|
* ElementTitle,
|
|
46
46
|
* ElementActions,
|
|
47
47
|
* ElementDetailsButtonWithHandler,
|
|
48
|
+
* IfEnabled,
|
|
48
49
|
* IfNotReadOnly,
|
|
49
50
|
* ElementToolbar,
|
|
50
51
|
* DefaultHandles,
|
|
52
|
+
* ElementTags
|
|
51
53
|
* } from '@likec4/diagram/custom'
|
|
52
54
|
*
|
|
53
55
|
* const CustomElementNode = elementNode(({ nodeProps, nodeModel }) => (
|
|
54
56
|
* <ElementNodeContainer nodeProps={nodeProps}>
|
|
55
57
|
* <ElementShape {...nodeProps} />
|
|
56
58
|
* <ElementTitle {...nodeProps} />
|
|
59
|
+
* <IfEnabled feature="ElementTags">
|
|
60
|
+
* <ElementTags {...nodeProps} />
|
|
61
|
+
* </IfEnabled>
|
|
57
62
|
* <ElementActions
|
|
58
63
|
* {...nodeProps}
|
|
59
64
|
* extraButtons={[
|
package/src/custom/hooks.ts
CHANGED
|
@@ -92,13 +92,13 @@ export function LikeC4DiagramXYFlow({
|
|
|
92
92
|
$isPanning = usePanningAtom(),
|
|
93
93
|
isPanning = useTimeout(() => {
|
|
94
94
|
$isPanning.set(true)
|
|
95
|
-
}, isReducedGraphics ?
|
|
95
|
+
}, isReducedGraphics ? 120 : 200),
|
|
96
96
|
notPanning = useDebouncedCallback(() => {
|
|
97
97
|
isPanning.clear()
|
|
98
98
|
if ($isPanning.get()) {
|
|
99
99
|
$isPanning.set(false)
|
|
100
100
|
}
|
|
101
|
-
}, 200),
|
|
101
|
+
}, isReducedGraphics ? 350 : 200),
|
|
102
102
|
onMove: OnMove = useCallbackRef((event) => {
|
|
103
103
|
if (!event) {
|
|
104
104
|
return
|
|
@@ -109,7 +109,7 @@ export function LikeC4DiagramXYFlow({
|
|
|
109
109
|
notPanning()
|
|
110
110
|
}),
|
|
111
111
|
onMoveEnd: OnMoveEnd = useCallbackRef((event, viewport) => {
|
|
112
|
-
if (!!event) {
|
|
112
|
+
if (!!event && !isReducedGraphics) {
|
|
113
113
|
notPanning.flush()
|
|
114
114
|
}
|
|
115
115
|
diagram.send({ type: 'xyflow.viewportMoved', viewport, manually: !!event })
|
package/src/likec4model/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { aux, LikeC4Model } from '@likec4/core/model'
|
|
1
|
+
import type { aux, LikeC4Model, LikeC4ViewModel } from '@likec4/core/model'
|
|
2
2
|
import { useContext } from 'react'
|
|
3
3
|
import { isString } from 'remeda'
|
|
4
4
|
import { LikeC4ModelContext } from './LikeC4ModelContext'
|
|
@@ -12,7 +12,7 @@ export function useLikeC4Model(type?: 'layouted' | 'computed') {
|
|
|
12
12
|
const model = useContext(LikeC4ModelContext)
|
|
13
13
|
|
|
14
14
|
if (!model) {
|
|
15
|
-
throw new Error('
|
|
15
|
+
throw new Error('LikeC4Model not found. Make sure you have LikeC4ModelProvider.')
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
if (isString(type) && model.stage !== type) {
|
|
@@ -22,6 +22,20 @@ export function useLikeC4Model(type?: 'layouted' | 'computed') {
|
|
|
22
22
|
return model
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export function useLikeC4ViewModel<A extends aux.Any = aux.Unknown>(
|
|
26
|
+
viewId: aux.LooseViewId<A>,
|
|
27
|
+
): LikeC4ViewModel<aux.toLayouted<A>> | null {
|
|
28
|
+
const model = useLikeC4Model<A>('layouted')
|
|
29
|
+
const view = model.findView(viewId)
|
|
30
|
+
if (!view) {
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
if (!view.isDiagram()) {
|
|
34
|
+
throw new Error(`View "${viewId}" is not layouted`)
|
|
35
|
+
}
|
|
36
|
+
return view as any
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
export function useLikeC4Specification() {
|
|
26
40
|
const model = useLikeC4Model()
|
|
27
41
|
return model.$data.specification
|
|
@@ -2,7 +2,7 @@ import { cx } from '@likec4/styles/css'
|
|
|
2
2
|
import {
|
|
3
3
|
RemoveScroll,
|
|
4
4
|
} from '@mantine/core'
|
|
5
|
-
import { useMergedRef } from '@mantine/hooks'
|
|
5
|
+
import { useFocusTrap, useMergedRef } from '@mantine/hooks'
|
|
6
6
|
import { useDebouncedCallback, useTimeoutEffect } from '@react-hookz/web'
|
|
7
7
|
import { m, useReducedMotionConfig } from 'motion/react'
|
|
8
8
|
import { type PropsWithChildren, forwardRef, useLayoutEffect, useRef, useState } from 'react'
|
|
@@ -39,6 +39,7 @@ export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(({
|
|
|
39
39
|
...rest
|
|
40
40
|
}, ref) => {
|
|
41
41
|
const [opened, setOpened] = useState(openDelay === 0)
|
|
42
|
+
const focusTrapRef = useFocusTrap(opened)
|
|
42
43
|
const dialogRef = useRef<HTMLDialogElement>(null)
|
|
43
44
|
const isClosingRef = useRef(false)
|
|
44
45
|
|
|
@@ -79,8 +80,17 @@ export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(({
|
|
|
79
80
|
}
|
|
80
81
|
return (
|
|
81
82
|
<m.dialog
|
|
82
|
-
ref={useMergedRef(
|
|
83
|
-
|
|
83
|
+
ref={useMergedRef(
|
|
84
|
+
dialogRef,
|
|
85
|
+
focusTrapRef,
|
|
86
|
+
ref,
|
|
87
|
+
)}
|
|
88
|
+
className={cx(
|
|
89
|
+
classes?.dialog,
|
|
90
|
+
className,
|
|
91
|
+
styles.dialog,
|
|
92
|
+
fullscreen && RemoveScroll.classNames.fullWidth,
|
|
93
|
+
)}
|
|
84
94
|
layout
|
|
85
95
|
style={{
|
|
86
96
|
// @ts-ignore
|
|
@@ -143,8 +153,13 @@ export const Overlay = forwardRef<HTMLDialogElement, OverlayProps>(({
|
|
|
143
153
|
}}
|
|
144
154
|
{...rest}
|
|
145
155
|
>
|
|
146
|
-
<RemoveScroll forwardProps
|
|
147
|
-
<div
|
|
156
|
+
<RemoveScroll forwardProps>
|
|
157
|
+
<div
|
|
158
|
+
className={cx(
|
|
159
|
+
classes?.body,
|
|
160
|
+
styles.body,
|
|
161
|
+
'overlay-body',
|
|
162
|
+
)}>
|
|
148
163
|
{opened && <>{children}</>}
|
|
149
164
|
</div>
|
|
150
165
|
</RemoveScroll>
|
package/src/search/Search.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { css } from '@likec4/styles/css'
|
|
2
|
-
import { VStack } from '@likec4/styles/jsx'
|
|
3
|
-
import {
|
|
2
|
+
import { Box, VStack } from '@likec4/styles/jsx'
|
|
3
|
+
import { Grid, GridCol, Group, ScrollArea, Title } from '@mantine/core'
|
|
4
4
|
import {
|
|
5
5
|
useCallbackRef,
|
|
6
6
|
useHotkeys,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { useTimeoutEffect } from '@react-hookz/web'
|
|
9
9
|
import { useSelector } from '@xstate/react'
|
|
10
10
|
import { AnimatePresence, LayoutGroup } from 'motion/react'
|
|
11
|
-
import {
|
|
11
|
+
import { useRef } from 'react'
|
|
12
12
|
import { ErrorBoundary } from 'react-error-boundary'
|
|
13
13
|
import { isTruthy } from 'remeda'
|
|
14
14
|
import { ErrorFallback } from '../components/ErrorFallback'
|
|
@@ -63,27 +63,15 @@ const body = css({
|
|
|
63
63
|
|
|
64
64
|
const selectIsOpened = (s: SearchActorSnapshot) => !s.matches('inactive')
|
|
65
65
|
|
|
66
|
-
export
|
|
66
|
+
export function Search({ searchActorRef }: { searchActorRef: SearchActorRef }) {
|
|
67
67
|
const isOpened = useSelector(searchActorRef, selectIsOpened)
|
|
68
68
|
|
|
69
69
|
const openSearch = () => {
|
|
70
70
|
searchActorRef.send({ type: 'open' })
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const
|
|
73
|
+
const close = useCallbackRef(() => {
|
|
74
74
|
searchActorRef.send({ type: 'close' })
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const afterCloseCbRef = useRef<(() => void)>(null)
|
|
78
|
-
const close = useCallbackRef((cb?: () => void) => {
|
|
79
|
-
afterCloseCbRef.current = cb ?? null
|
|
80
|
-
sendClose()
|
|
81
|
-
})
|
|
82
|
-
const onExitComplete = useCallbackRef(() => {
|
|
83
|
-
if (afterCloseCbRef.current) {
|
|
84
|
-
afterCloseCbRef.current()
|
|
85
|
-
afterCloseCbRef.current = null
|
|
86
|
-
}
|
|
87
75
|
})
|
|
88
76
|
|
|
89
77
|
useHotkeys([
|
|
@@ -96,40 +84,34 @@ export const Search = memo<{ searchActorRef: SearchActorRef }>(({ searchActorRef
|
|
|
96
84
|
])
|
|
97
85
|
|
|
98
86
|
return (
|
|
99
|
-
<SearchActorContext
|
|
100
|
-
value={{
|
|
101
|
-
searchActorRef,
|
|
102
|
-
close,
|
|
103
|
-
}}>
|
|
87
|
+
<SearchActorContext value={searchActorRef}>
|
|
104
88
|
<DiagramFeatures.Overlays>
|
|
105
|
-
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
</AnimatePresence>
|
|
127
|
-
</LayoutGroup>
|
|
89
|
+
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={close}>
|
|
90
|
+
<AnimatePresence>
|
|
91
|
+
{isOpened && (
|
|
92
|
+
<Overlay
|
|
93
|
+
fullscreen
|
|
94
|
+
withBackdrop={false}
|
|
95
|
+
backdrop={{
|
|
96
|
+
opacity: 0.9,
|
|
97
|
+
}}
|
|
98
|
+
classes={{
|
|
99
|
+
dialog,
|
|
100
|
+
body,
|
|
101
|
+
}}
|
|
102
|
+
openDelay={0}
|
|
103
|
+
onClose={close}
|
|
104
|
+
data-likec4-search="true"
|
|
105
|
+
>
|
|
106
|
+
<SearchOverlay searchActorRef={searchActorRef} />
|
|
107
|
+
</Overlay>
|
|
108
|
+
)}
|
|
109
|
+
</AnimatePresence>
|
|
128
110
|
</ErrorBoundary>
|
|
129
111
|
</DiagramFeatures.Overlays>
|
|
130
112
|
</SearchActorContext>
|
|
131
113
|
)
|
|
132
|
-
}
|
|
114
|
+
}
|
|
133
115
|
|
|
134
116
|
const scrollArea = css({
|
|
135
117
|
height: [
|
|
@@ -145,23 +127,24 @@ const scrollArea = css({
|
|
|
145
127
|
},
|
|
146
128
|
})
|
|
147
129
|
|
|
148
|
-
const SearchOverlay = ({
|
|
130
|
+
const SearchOverlay = ({ searchActorRef }: { searchActorRef: SearchActorRef }) => {
|
|
131
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
149
132
|
const pickViewFor = usePickViewFor()
|
|
150
133
|
|
|
151
134
|
useTimeoutEffect(() => {
|
|
152
135
|
if (isTruthy(searchActorRef.getSnapshot().context.openedWithSearch)) {
|
|
153
|
-
focusToFirstFoundElement()
|
|
136
|
+
focusToFirstFoundElement(ref.current)
|
|
154
137
|
}
|
|
155
138
|
}, 150)
|
|
156
139
|
|
|
157
140
|
return (
|
|
158
|
-
<
|
|
141
|
+
<Box ref={ref} display={'contents'}>
|
|
159
142
|
<Group
|
|
160
143
|
className={'group'}
|
|
161
144
|
wrap="nowrap"
|
|
162
145
|
onClick={e => {
|
|
163
146
|
e.stopPropagation()
|
|
164
|
-
moveFocusToSearchInput()
|
|
147
|
+
moveFocusToSearchInput(ref.current)
|
|
165
148
|
}}>
|
|
166
149
|
<VStack flex={1} px={'sm'}>
|
|
167
150
|
<LikeC4SearchInput />
|
|
@@ -232,6 +215,6 @@ const SearchOverlay = ({ close, searchActorRef }: { close: () => void; searchAct
|
|
|
232
215
|
</GridCol>
|
|
233
216
|
</Grid>
|
|
234
217
|
{pickViewFor && <PickView elementFqn={pickViewFor} />}
|
|
235
|
-
</
|
|
218
|
+
</Box>
|
|
236
219
|
)
|
|
237
220
|
}
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
import { useCallbackRef } from '@mantine/hooks'
|
|
23
23
|
import { IconChevronRight } from '@tabler/icons-react'
|
|
24
24
|
import * as m from 'motion/react-m'
|
|
25
|
-
import { type KeyboardEventHandler, memo,
|
|
25
|
+
import { type KeyboardEventHandler, memo, useEffect, useMemo } from 'react'
|
|
26
26
|
import { first, isEmpty, only, pipe, reduce } from 'remeda'
|
|
27
27
|
import { IconOrShapeRenderer } from '../../context/IconRenderer'
|
|
28
28
|
import { useDiagram } from '../../hooks/useDiagram'
|
|
@@ -31,7 +31,7 @@ import { useLikeC4Model } from '../../likec4model/useLikeC4Model'
|
|
|
31
31
|
import { useNormalizedSearch, useSearchActor } from '../hooks'
|
|
32
32
|
import { buttonsva } from './_shared.css'
|
|
33
33
|
import * as styles from './ElementsColumn.css'
|
|
34
|
-
import { centerY, moveFocusToSearchInput, stopAndPrevent } from './utils'
|
|
34
|
+
import { centerY, moveFocusToSearchInput, queryAllFocusable, stopAndPrevent } from './utils'
|
|
35
35
|
import { NothingFound } from './ViewsColum'
|
|
36
36
|
|
|
37
37
|
interface LikeC4ModelTreeNodeData {
|
|
@@ -161,7 +161,7 @@ function ElementsTree({
|
|
|
161
161
|
if (e.key === 'ArrowUp') {
|
|
162
162
|
if (id === roots[0]?.value) {
|
|
163
163
|
stopAndPrevent(e)
|
|
164
|
-
moveFocusToSearchInput()
|
|
164
|
+
moveFocusToSearchInput(target)
|
|
165
165
|
}
|
|
166
166
|
return
|
|
167
167
|
}
|
|
@@ -172,9 +172,7 @@ function ElementsTree({
|
|
|
172
172
|
}
|
|
173
173
|
const label = (e.target as HTMLLIElement).querySelector<HTMLLIElement>('.mantine-Tree-label') ?? target
|
|
174
174
|
const maxY = label.getBoundingClientRect().y
|
|
175
|
-
const viewButtons =
|
|
176
|
-
`[data-likec4-search-views] .${styles.focusable}`,
|
|
177
|
-
)]
|
|
175
|
+
const viewButtons = queryAllFocusable(target, 'views')
|
|
178
176
|
let view = viewButtons.length > 1
|
|
179
177
|
? viewButtons.find((el, i, all) => centerY(el) > maxY || i === all.length - 1)
|
|
180
178
|
: null
|
|
@@ -299,23 +297,22 @@ function ElementTreeNode(
|
|
|
299
297
|
|
|
300
298
|
function useHandleElementSelection() {
|
|
301
299
|
const diagram = useDiagram()
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
return useCallback((element: LikeC4Model.Element) => {
|
|
300
|
+
const searchActorRef = useSearchActor()
|
|
301
|
+
return useCallbackRef((element: LikeC4Model.Element) => {
|
|
306
302
|
const views = [...element.views()]
|
|
307
303
|
if (views.length === 0) {
|
|
308
304
|
return
|
|
309
305
|
}
|
|
310
306
|
const singleView = only(views)
|
|
311
307
|
if (singleView) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
308
|
+
searchActorRef.send({ type: 'close' })
|
|
309
|
+
if (singleView.id !== diagram.currentView.id) {
|
|
310
|
+
setTimeout(() => {
|
|
311
|
+
diagram.navigateTo(singleView.id)
|
|
312
|
+
}, 100)
|
|
316
313
|
}
|
|
317
314
|
return
|
|
318
315
|
}
|
|
319
316
|
searchActorRef.send({ type: 'pickview.open', elementFqn: element.id })
|
|
320
|
-
}
|
|
317
|
+
})
|
|
321
318
|
}
|
|
@@ -10,14 +10,8 @@ import * as styles from './styles.css'
|
|
|
10
10
|
import { ViewButton } from './ViewsColum'
|
|
11
11
|
|
|
12
12
|
export function PickView({ elementFqn }: { elementFqn: Fqn }) {
|
|
13
|
-
const
|
|
13
|
+
const searchActorRef = useSearchActor()
|
|
14
14
|
const element = useLikeC4Model().element(elementFqn)
|
|
15
|
-
// const pickview = usePickView()
|
|
16
|
-
// const navigateTo = useCloseSearchAndNavigateTo()
|
|
17
|
-
// const focusTrapRef = useFocusTrap(!!pickview)
|
|
18
|
-
// useFocusReturn({
|
|
19
|
-
// opened: !!pickview,
|
|
20
|
-
// })
|
|
21
15
|
const scoped = [] as LikeC4ViewModel[]
|
|
22
16
|
const others = [] as LikeC4ViewModel[]
|
|
23
17
|
|
|
@@ -61,44 +55,44 @@ export function PickView({ elementFqn }: { elementFqn: Fqn }) {
|
|
|
61
55
|
closePickView()
|
|
62
56
|
}}>
|
|
63
57
|
</m.div>
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
e
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<ScrollAreaAutosize mah={'calc(100vh -
|
|
58
|
+
<FocusTrap>
|
|
59
|
+
<m.div
|
|
60
|
+
key="pickview"
|
|
61
|
+
initial={{
|
|
62
|
+
opacity: 0,
|
|
63
|
+
scale: 0.95,
|
|
64
|
+
originY: 0,
|
|
65
|
+
translateX: '-50%',
|
|
66
|
+
translateY: -20,
|
|
67
|
+
}}
|
|
68
|
+
animate={{
|
|
69
|
+
opacity: 1,
|
|
70
|
+
scale: 1,
|
|
71
|
+
translateY: 0,
|
|
72
|
+
}}
|
|
73
|
+
exit={{
|
|
74
|
+
opacity: 0,
|
|
75
|
+
scale: 0.98,
|
|
76
|
+
translateY: -20,
|
|
77
|
+
transition: {
|
|
78
|
+
duration: 0.1,
|
|
79
|
+
},
|
|
80
|
+
}}
|
|
81
|
+
className={styles.pickview}
|
|
82
|
+
data-likec4-search-views>
|
|
83
|
+
<Group px="sm" py="md" justify="space-between">
|
|
84
|
+
<Title order={2} lh={1}>Select view</Title>
|
|
85
|
+
<ActionIcon
|
|
86
|
+
size={'md'}
|
|
87
|
+
variant="default"
|
|
88
|
+
onClick={(e) => {
|
|
89
|
+
e.stopPropagation()
|
|
90
|
+
closePickView()
|
|
91
|
+
}}>
|
|
92
|
+
<IconX />
|
|
93
|
+
</ActionIcon>
|
|
94
|
+
</Group>
|
|
95
|
+
<ScrollAreaAutosize mah={'calc(100vh - 110px)'} type="never">
|
|
102
96
|
{scoped.length > 0 && (
|
|
103
97
|
<Stack gap={'sm'} px={'sm'} className={styles.pickviewGroup}>
|
|
104
98
|
<Title order={6} c={'dimmed'}>scoped views of the element</Title>
|
|
@@ -133,8 +127,8 @@ export function PickView({ elementFqn }: { elementFqn: Fqn }) {
|
|
|
133
127
|
</Stack>
|
|
134
128
|
)}
|
|
135
129
|
</ScrollAreaAutosize>
|
|
136
|
-
</
|
|
137
|
-
</
|
|
130
|
+
</m.div>
|
|
131
|
+
</FocusTrap>
|
|
138
132
|
</>
|
|
139
133
|
)
|
|
140
134
|
}
|