Skip to content

Commit 966d85b

Browse files
authored
Feat: re-use ModelConfirmation component from IDE page (#1155)
1 parent 0ea9010 commit 966d85b

File tree

7 files changed

+200
-217
lines changed

7 files changed

+200
-217
lines changed

web/client/src/context/context.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { type Confirmation } from '@components/modal/ModalConfirmation'
12
import { ModelSQLMeshModel } from '@models/sqlmesh-model'
23
import { create } from 'zustand'
34
import {
@@ -14,11 +15,16 @@ import {
1415
import { isStringEmptyOrNil } from '~/utils'
1516

1617
interface ContextStore {
18+
showConfirmation: boolean
19+
confirmations: Confirmation[]
1720
environment: ModelEnvironment
1821
environments: Set<ModelEnvironment>
1922
initialStartDate?: ContextEnvironmentStart
2023
initialEndDate?: ContextEnvironmentEnd
2124
models: Map<string, ModelSQLMeshModel>
25+
setShowConfirmation: (showConfirmation: boolean) => void
26+
addConfirmation: (confirmation: Confirmation) => void
27+
removeConfirmation: () => void
2228
setModels: (models?: Model[]) => void
2329
isExistingEnvironment: (
2430
environment: ModelEnvironment | EnvironmentName,
@@ -42,11 +48,36 @@ const environments = new Set(ModelEnvironment.getDefaultEnvironments())
4248
const environment = environments.values().next().value
4349

4450
export const useStoreContext = create<ContextStore>((set, get) => ({
51+
showConfirmation: false,
52+
confirmations: [],
4553
environment,
4654
environments,
4755
initialStartDate: undefined,
4856
initialEndDate: undefined,
4957
models: new Map(),
58+
setShowConfirmation(showConfirmation) {
59+
set(() => ({
60+
showConfirmation,
61+
}))
62+
},
63+
addConfirmation(confirmation) {
64+
set(s => {
65+
s.confirmations.push(confirmation)
66+
67+
return {
68+
confirmations: Array.from(s.confirmations),
69+
}
70+
})
71+
},
72+
removeConfirmation() {
73+
const s = get()
74+
75+
s.confirmations.shift()
76+
77+
set(() => ({
78+
confirmations: Array.from(s.confirmations),
79+
}))
80+
},
5081
setModels(models = []) {
5182
const s = get()
5283

web/client/src/library/components/button/Button.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ const SHAPE = new Map<ButtonShape, string>([
9494

9595
const SIZE = new Map<ButtonSize, string>([
9696
[EnumSize.xs, `px-2 py-0 text-xs leading-2 border`],
97-
[EnumSize.sm, `px-2 py-[0.125rem] text-xs leading-4 border-2`],
98-
[EnumSize.md, `px-3 py-2 text-base leading-6 border-2`],
99-
[EnumSize.lg, `px-4 py-3 text-lg border-4`],
97+
[EnumSize.sm, `px-2 py-[0.125rem] text-xs leading-2 border-2`],
98+
[EnumSize.md, `px-3 py-2 text-base leading-4 border-2`],
99+
[EnumSize.lg, `px-4 py-3 text-lg leading-6 border-4`],
100100
])
101101

102102
const Button = makeButton(
@@ -131,6 +131,14 @@ function ButtonPlain(
131131
form={form}
132132
disabled={disabled}
133133
onClick={onClick}
134+
onKeyDown={(e: React.KeyboardEvent<HTMLButtonElement>) => {
135+
if (e.key === 'Enter' || e.key === ' ') {
136+
e.preventDefault()
137+
e.stopPropagation()
138+
139+
onClick?.(e as unknown as React.MouseEvent<HTMLButtonElement>)
140+
}
141+
}}
134142
className={className}
135143
>
136144
{children}

web/client/src/library/components/fileExplorer/FileExplorer.tsx

Lines changed: 5 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import React, { type MouseEvent } from 'react'
22
import clsx from 'clsx'
3-
import ModalConfirmation from '../modal/ModalConfirmation'
4-
import { Button } from '../button/Button'
53
import Directory from './Directory'
64
import { useStoreProject } from '@context/project'
75
import * as ContextMenu from '@radix-ui/react-context-menu'
@@ -14,6 +12,8 @@ import { CheckCircleIcon, XCircleIcon } from '@heroicons/react/24/solid'
1412
import SearchList from '@components/search/SearchList'
1513
import { type ModelFile } from '@models/file'
1614
import { EnumSize } from '~/types/enum'
15+
import { useStoreContext } from '@context/context'
16+
1717
/* TODO:
1818
- add drag and drop files/directories from desktop
1919
- add copy and paste
@@ -28,12 +28,10 @@ const FileExplorer = function FileExplorer({
2828
const project = useStoreProject(s => s.project)
2929
const setSelectedFile = useStoreProject(s => s.setSelectedFile)
3030

31+
const addConfirmation = useStoreContext(s => s.addConfirmation)
32+
3133
const {
3234
activeRange,
33-
confirmation,
34-
showConfirmation,
35-
setShowConfirmation,
36-
setConfirmation,
3735
removeArtifacts,
3836
setActiveRange,
3937
createDirectory,
@@ -46,7 +44,7 @@ const FileExplorer = function FileExplorer({
4644
}
4745

4846
if (e.metaKey && e.key === 'Backspace' && activeRange.size > 0) {
49-
setConfirmation({
47+
addConfirmation({
5048
headline: 'Removing Selected Files/Directories',
5149
description: `Are you sure you want to remove ${activeRange.size} items?`,
5250
yesText: 'Yes, Remove',
@@ -116,57 +114,6 @@ const FileExplorer = function FileExplorer({
116114
<div className="ml-auto pl-5"></div>
117115
</ContextMenu.Item>
118116
</FileExplorer.ContextMenu>
119-
<ModalConfirmation
120-
show={showConfirmation}
121-
onClose={() => {
122-
setShowConfirmation(false)
123-
}}
124-
>
125-
<ModalConfirmation.Main>
126-
{confirmation?.headline != null && (
127-
<ModalConfirmation.Headline>
128-
{confirmation?.headline}
129-
</ModalConfirmation.Headline>
130-
)}
131-
{confirmation?.description != null && (
132-
<ModalConfirmation.Description>
133-
{confirmation?.description}
134-
</ModalConfirmation.Description>
135-
)}
136-
{confirmation?.details != null && (
137-
<ModalConfirmation.Details details={confirmation?.details} />
138-
)}
139-
</ModalConfirmation.Main>
140-
<ModalConfirmation.Actions>
141-
<Button
142-
className="font-bold"
143-
size="md"
144-
variant="danger"
145-
onClick={(e: MouseEvent) => {
146-
e.stopPropagation()
147-
148-
confirmation?.action?.()
149-
150-
setShowConfirmation(false)
151-
}}
152-
>
153-
{confirmation?.yesText ?? 'Confirm'}
154-
</Button>
155-
<Button
156-
size="md"
157-
variant="alternative"
158-
onClick={(e: MouseEvent) => {
159-
e.stopPropagation()
160-
161-
confirmation?.cancel?.()
162-
163-
setShowConfirmation(false)
164-
}}
165-
>
166-
{confirmation?.noText ?? 'Cancel'}
167-
</Button>
168-
</ModalConfirmation.Actions>
169-
</ModalConfirmation>
170117
</div>
171118
)
172119
}

web/client/src/library/components/fileExplorer/context.tsx

Lines changed: 22 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@ import {
1919
} from '@utils/index'
2020
import { ModelFile } from '@models/file'
2121
import { useStoreEditor } from '@context/editor'
22-
import { type Confirmation } from '@components/modal/ModalConfirmation'
2322
import { type ResponseWithDetail } from '@api/instance'
23+
import { useStoreContext } from '@context/context'
2424

2525
interface FileExplorer {
26-
confirmation?: Confirmation
27-
showConfirmation: boolean
2826
activeRange: Set<ModelArtifact>
2927
setActiveRange: (activeRange: Set<ModelArtifact>) => void
3028
selectArtifactsInRange: (to: ModelArtifact) => void
@@ -34,13 +32,9 @@ interface FileExplorer {
3432
removeArtifacts: (artifacts: Set<ModelArtifact>) => void
3533
removeArtifactWithConfirmation: (artifact: ModelArtifact) => void
3634
moveArtifacts: (artifacts: Set<ModelArtifact>, target: ModelDirectory) => void
37-
setShowConfirmation: (show: boolean) => void
38-
setConfirmation: (confirmation?: Confirmation) => void
3935
}
4036

4137
export const FileExplorerContext = createContext<FileExplorer>({
42-
confirmation: undefined,
43-
showConfirmation: false,
4438
activeRange: new Set(),
4539
setActiveRange: () => {},
4640
selectArtifactsInRange: () => {},
@@ -50,8 +44,6 @@ export const FileExplorerContext = createContext<FileExplorer>({
5044
removeArtifacts: () => {},
5145
removeArtifactWithConfirmation: () => {},
5246
moveArtifacts: () => {},
53-
setShowConfirmation: () => {},
54-
setConfirmation: () => {},
5547
})
5648

5749
export default function FileExplorerProvider({
@@ -64,17 +56,13 @@ export default function FileExplorerProvider({
6456
const setSelectedFile = useStoreProject(s => s.setSelectedFile)
6557
const setFiles = useStoreProject(s => s.setFiles)
6658

59+
const addConfirmation = useStoreContext(s => s.addConfirmation)
60+
6761
const tab = useStoreEditor(s => s.tab)
6862
const closeTab = useStoreEditor(s => s.closeTab)
6963

7064
const [isLoading, setIsLoading] = useState(false)
7165
const [activeRange, setActiveRange] = useState(new Set<ModelArtifact>())
72-
const [confirmation, setConfirmation] = useState<Confirmation>()
73-
const [showConfirmation, setShowConfirmation] = useState(false)
74-
75-
useEffect(() => {
76-
setShowConfirmation(isNotNil(confirmation))
77-
}, [confirmation])
7866

7967
useEffect(() => {
8068
setSelectedFile(tab?.file)
@@ -266,34 +254,10 @@ export default function FileExplorerProvider({
266254
}
267255

268256
function removeArtifactWithConfirmation(artifact: ModelArtifact): void {
269-
let confirmation: Confirmation = {
270-
headline: `Removing ${
271-
artifact instanceof ModelDirectory ? 'Directory' : 'File'
272-
}`,
273-
description: `Are you sure you want to remove the ${
274-
artifact instanceof ModelDirectory ? 'directory' : 'file'
275-
} "${artifact.name}"?`,
276-
yesText: 'Yes, Remove',
277-
noText: 'No, Cancel',
278-
action: () => {
279-
if (artifact instanceof ModelDirectory) {
280-
if (artifact.parent != null) {
281-
removeArtifacts(new Set([artifact]))
282-
}
283-
}
284-
285-
if (artifact instanceof ModelFile) {
286-
if (artifact.parent != null) {
287-
removeArtifacts(new Set([artifact]))
288-
}
289-
}
290-
},
291-
}
292-
293257
if (activeRange.has(artifact)) {
294258
// User selected multiple including current directory
295259
// so here we should prompt to delete all selected
296-
confirmation = {
260+
addConfirmation({
297261
headline: 'Removing Selected Files/Directories',
298262
description: `Are you sure you want to remove ${activeRange.size} items?`,
299263
yesText: 'Yes, Remove',
@@ -302,10 +266,24 @@ export default function FileExplorerProvider({
302266
action: () => {
303267
removeArtifacts(activeRange)
304268
},
305-
}
269+
})
270+
} else {
271+
addConfirmation({
272+
headline: `Removing ${
273+
artifact instanceof ModelDirectory ? 'Directory' : 'File'
274+
}`,
275+
description: `Are you sure you want to remove the ${
276+
artifact instanceof ModelDirectory ? 'directory' : 'file'
277+
} "${artifact.name}"?`,
278+
yesText: 'Yes, Remove',
279+
noText: 'No, Cancel',
280+
action: () => {
281+
if (isNotNil(artifact.parent)) {
282+
removeArtifacts(new Set([artifact]))
283+
}
284+
},
285+
})
306286
}
307-
308-
setConfirmation(confirmation)
309287
}
310288

311289
function moveArtifacts(
@@ -362,7 +340,7 @@ export default function FileExplorerProvider({
362340
})
363341

364342
if (isArrayNotEmpty(duplicates)) {
365-
setConfirmation({
343+
addConfirmation({
366344
headline: 'Moving Duplicates',
367345
description: `All duplicated names will be renamed!`,
368346
yesText: 'Yes, Rename',
@@ -394,8 +372,6 @@ export default function FileExplorerProvider({
394372
return (
395373
<FileExplorerContext.Provider
396374
value={{
397-
confirmation,
398-
showConfirmation,
399375
activeRange,
400376
createDirectory,
401377
createFile,
@@ -404,8 +380,6 @@ export default function FileExplorerProvider({
404380
removeArtifactWithConfirmation,
405381
moveArtifacts,
406382
selectArtifactsInRange,
407-
setConfirmation,
408-
setShowConfirmation,
409383
setActiveRange(activeRange) {
410384
setActiveRange(
411385
() =>

web/client/src/library/components/modal/ModalConfirmation.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import Modal from './Modal'
33

44
interface PropsModalConfirmation extends React.HTMLAttributes<HTMLElement> {
55
show: boolean
6-
onClose: () => void
6+
onClose?: () => void
7+
afterLeave?: () => void
78
}
89

910
export interface Confirmation {
@@ -15,6 +16,7 @@ export interface Confirmation {
1516
tagline?: string
1617
yesText: string
1718
noText: string
19+
children?: React.ReactNode
1820
}
1921

2022
export interface WithConfirmation {
@@ -25,11 +27,13 @@ function ModalConfirmation({
2527
show,
2628
children,
2729
onClose,
30+
afterLeave,
2831
}: PropsModalConfirmation): JSX.Element {
2932
return (
3033
<Modal
3134
show={show}
3235
onClose={onClose}
36+
afterLeave={afterLeave}
3337
>
3438
<Dialog.Panel className="w-[30rem] transform rounded-xl bg-theme text-left align-middle shadow-xl transition-all">
3539
{children}

0 commit comments

Comments
 (0)