@charcuterie/ui 1.1.0 → 2.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/Combobox/Combobox.d.ts +54 -0
- package/dist/Combobox/Combobox.d.ts.map +1 -0
- package/dist/Combobox/Combobox.js +216 -0
- package/dist/Combobox/Combobox.js.map +1 -0
- package/dist/Combobox/ComboboxOption.d.ts +36 -0
- package/dist/Combobox/ComboboxOption.d.ts.map +1 -0
- package/dist/Combobox/ComboboxOption.js +32 -0
- package/dist/Combobox/ComboboxOption.js.map +1 -0
- package/dist/Dialog/Dialog.d.ts +53 -0
- package/dist/Dialog/Dialog.d.ts.map +1 -0
- package/dist/Dialog/Dialog.js +52 -0
- package/dist/Dialog/Dialog.js.map +1 -0
- package/dist/Lightbox/Lightbox.d.ts +7 -7
- package/dist/Lightbox/Lightbox.js +9 -9
- package/dist/Lightbox/Lightbox.js.map +1 -1
- package/dist/Listbox/Listbox.d.ts +56 -0
- package/dist/Listbox/Listbox.d.ts.map +1 -0
- package/dist/Listbox/Listbox.js +151 -0
- package/dist/Listbox/Listbox.js.map +1 -0
- package/dist/Listbox/ListboxOption.d.ts +36 -0
- package/dist/Listbox/ListboxOption.d.ts.map +1 -0
- package/dist/Listbox/ListboxOption.js +42 -0
- package/dist/Listbox/ListboxOption.js.map +1 -0
- package/dist/Menu/Menu.d.ts.map +1 -1
- package/dist/Menu/Menu.js +47 -97
- package/dist/Menu/Menu.js.map +1 -1
- package/dist/Modal/Modal.d.ts +11 -59
- package/dist/Modal/Modal.d.ts.map +1 -1
- package/dist/Modal/Modal.js +28 -101
- package/dist/Modal/Modal.js.map +1 -1
- package/dist/Overlay/OverlayPanel.d.ts +50 -0
- package/dist/Overlay/OverlayPanel.d.ts.map +1 -0
- package/dist/Overlay/OverlayPanel.js +72 -0
- package/dist/Overlay/OverlayPanel.js.map +1 -0
- package/dist/Overlay/OverlayStack.d.ts +47 -0
- package/dist/Overlay/OverlayStack.d.ts.map +1 -0
- package/dist/Overlay/OverlayStack.js +46 -0
- package/dist/Overlay/OverlayStack.js.map +1 -0
- package/dist/Overlay/SharedBackdrop.d.ts +24 -0
- package/dist/Overlay/SharedBackdrop.d.ts.map +1 -0
- package/dist/Overlay/SharedBackdrop.js +28 -0
- package/dist/Overlay/SharedBackdrop.js.map +1 -0
- package/dist/Overlay/lockScrollBehind.d.ts.map +1 -0
- package/dist/Overlay/lockScrollBehind.js.map +1 -0
- package/dist/Overlay/overlayPanelClass.d.ts +17 -0
- package/dist/Overlay/overlayPanelClass.d.ts.map +1 -0
- package/dist/Overlay/overlayPanelClass.js +17 -0
- package/dist/Overlay/overlayPanelClass.js.map +1 -0
- package/dist/Overlay/useAnchoredOverlay.d.ts +97 -0
- package/dist/Overlay/useAnchoredOverlay.d.ts.map +1 -0
- package/dist/Overlay/useAnchoredOverlay.js +81 -0
- package/dist/Overlay/useAnchoredOverlay.js.map +1 -0
- package/dist/Popover/Popover.d.ts +16 -24
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +32 -68
- package/dist/Popover/Popover.js.map +1 -1
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/Tooltip.js +25 -25
- package/dist/Tooltip/Tooltip.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/mountStory.testHelpers.d.ts +1 -0
- package/dist/mountStory.testHelpers.d.ts.map +1 -1
- package/dist/mountStory.testHelpers.js +21 -1
- package/dist/mountStory.testHelpers.js.map +1 -1
- package/package.json +3 -2
- package/src/Combobox/Combobox.mdx +71 -0
- package/src/Combobox/Combobox.stories.tsx +245 -0
- package/src/Combobox/Combobox.test.tsx +289 -0
- package/src/Combobox/Combobox.tsx +513 -0
- package/src/Combobox/ComboboxOption.tsx +88 -0
- package/src/Dialog/Dialog.mdx +87 -0
- package/src/Dialog/Dialog.stories.tsx +397 -0
- package/src/Dialog/Dialog.test.tsx +340 -0
- package/src/Dialog/Dialog.tsx +133 -0
- package/src/Field/Field.test.tsx +6 -4
- package/src/Lightbox/Lightbox.mdx +9 -8
- package/src/Lightbox/Lightbox.stories.tsx +5 -5
- package/src/Lightbox/Lightbox.test.tsx +25 -36
- package/src/Lightbox/Lightbox.tsx +10 -10
- package/src/Listbox/Listbox.mdx +68 -0
- package/src/Listbox/Listbox.stories.tsx +205 -0
- package/src/Listbox/Listbox.test.tsx +198 -0
- package/src/Listbox/Listbox.tsx +295 -0
- package/src/Listbox/ListboxOption.tsx +104 -0
- package/src/Menu/Menu.mdx +11 -13
- package/src/Menu/Menu.test.tsx +47 -38
- package/src/Menu/Menu.tsx +81 -142
- package/src/Modal/Modal.mdx +29 -82
- package/src/Modal/Modal.stories.tsx +55 -188
- package/src/Modal/Modal.test.tsx +44 -177
- package/src/Modal/Modal.tsx +54 -204
- package/src/Overlay/OverlayPanel.tsx +195 -0
- package/src/Overlay/OverlayStack.test.tsx +235 -0
- package/src/Overlay/OverlayStack.tsx +132 -0
- package/src/Overlay/SharedBackdrop.tsx +40 -0
- package/src/Overlay/overlayPanelClass.ts +17 -0
- package/src/Overlay/useAnchoredOverlay.ts +197 -0
- package/src/Popover/Popover.mdx +17 -20
- package/src/Popover/Popover.stories.tsx +33 -0
- package/src/Popover/Popover.test.tsx +68 -32
- package/src/Popover/Popover.tsx +56 -95
- package/src/Tooltip/Tooltip.mdx +12 -9
- package/src/Tooltip/Tooltip.test.tsx +17 -19
- package/src/Tooltip/Tooltip.tsx +35 -32
- package/src/index.ts +13 -4
- package/src/mountStory.testHelpers.ts +25 -1
- package/src/sourceRules.test.ts +12 -3
- package/src/storyControls.test.ts +25 -12
- package/dist/Modal/lockScrollBehind.d.ts.map +0 -1
- package/dist/Modal/lockScrollBehind.js.map +0 -1
- /package/dist/{Modal → Overlay}/lockScrollBehind.d.ts +0 -0
- /package/dist/{Modal → Overlay}/lockScrollBehind.js +0 -0
- /package/src/{Modal → Overlay}/lockScrollBehind.ts +0 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { useVisibility } from "@charcuterie/logic"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import type { ReactNode } from "react"
|
|
4
|
+
|
|
5
|
+
import { toStoryChoice } from "../argTypes.storyHelpers.ts"
|
|
6
|
+
import { Button } from "../Button/Button.tsx"
|
|
7
|
+
import { StoryRow } from "../board.storyHelpers.tsx"
|
|
8
|
+
import { OverlayStackProvider } from "../Overlay/OverlayStack.tsx"
|
|
9
|
+
import type { DialogSize } from "./Dialog.tsx"
|
|
10
|
+
import { Dialog } from "./Dialog.tsx"
|
|
11
|
+
|
|
12
|
+
const meta = {
|
|
13
|
+
title: "Components/Dialog",
|
|
14
|
+
component: Dialog,
|
|
15
|
+
parameters: { layout: "padded" },
|
|
16
|
+
argTypes: {
|
|
17
|
+
size: toStoryChoice([
|
|
18
|
+
"sm",
|
|
19
|
+
"md",
|
|
20
|
+
"lg",
|
|
21
|
+
"xl",
|
|
22
|
+
"full",
|
|
23
|
+
] as const),
|
|
24
|
+
},
|
|
25
|
+
args: {
|
|
26
|
+
// `render` supplies the real content; this satisfies the required
|
|
27
|
+
// `children` for the Controls panel's arg type.
|
|
28
|
+
children: null,
|
|
29
|
+
headingLevel: 2,
|
|
30
|
+
isDismissable: true,
|
|
31
|
+
size: "md",
|
|
32
|
+
},
|
|
33
|
+
} satisfies Meta<typeof Dialog>
|
|
34
|
+
|
|
35
|
+
export default meta
|
|
36
|
+
|
|
37
|
+
type Story = StoryObj<typeof meta>
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Every board here is triggers-plus-one-open-dialog rather than a
|
|
41
|
+
* grid, and that is the component rather than the stories being
|
|
42
|
+
* awkward: a dialog covers the viewport, so two side by side is not
|
|
43
|
+
* the real arrangement. What the boards show instead is a page, a
|
|
44
|
+
* scrim, and one dialog over both.
|
|
45
|
+
*
|
|
46
|
+
* `footer` is a function of `hide` because a dialog's own buttons
|
|
47
|
+
* are the normal way out of it, and they need the same `hide` the
|
|
48
|
+
* close button gets. One `useVisibility` per demo, no second source
|
|
49
|
+
* of truth.
|
|
50
|
+
*/
|
|
51
|
+
const DialogDemo = ({
|
|
52
|
+
buttonLabel,
|
|
53
|
+
children,
|
|
54
|
+
footer,
|
|
55
|
+
heading,
|
|
56
|
+
isDismissable = true,
|
|
57
|
+
size,
|
|
58
|
+
}: {
|
|
59
|
+
buttonLabel: string
|
|
60
|
+
children?: ReactNode
|
|
61
|
+
footer?: (hide: () => void) => ReactNode
|
|
62
|
+
heading: string
|
|
63
|
+
isDismissable?: boolean
|
|
64
|
+
size?: DialogSize
|
|
65
|
+
}): ReactNode => {
|
|
66
|
+
const { hide, isVisible, show } = useVisibility()
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<>
|
|
70
|
+
<Button appearance="soft" onClick={show} size="sm">
|
|
71
|
+
{buttonLabel}
|
|
72
|
+
</Button>
|
|
73
|
+
|
|
74
|
+
<Dialog
|
|
75
|
+
footer={footer?.(hide)}
|
|
76
|
+
heading={heading}
|
|
77
|
+
isDismissable={isDismissable}
|
|
78
|
+
isVisible={isVisible}
|
|
79
|
+
onClose={hide}
|
|
80
|
+
size={size}
|
|
81
|
+
>
|
|
82
|
+
{children ?? (
|
|
83
|
+
<p className="text-content-secondary text-sm">
|
|
84
|
+
MakeMKV reported a read error on title 4. The
|
|
85
|
+
rip can continue with the remaining eight
|
|
86
|
+
titles, or stop here and keep what has
|
|
87
|
+
completed.
|
|
88
|
+
</p>
|
|
89
|
+
)}
|
|
90
|
+
</Dialog>
|
|
91
|
+
</>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const Default: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
heading: "Bay 3",
|
|
98
|
+
isVisible: false,
|
|
99
|
+
onClose: () => {},
|
|
100
|
+
},
|
|
101
|
+
render: () => (
|
|
102
|
+
<DialogDemo
|
|
103
|
+
buttonLabel="Show the read error"
|
|
104
|
+
heading="Read error on title 4"
|
|
105
|
+
/>
|
|
106
|
+
),
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const SIZES: DialogSize[] = ["sm", "md", "lg", "xl", "full"]
|
|
110
|
+
|
|
111
|
+
export const AllVariants: Story = {
|
|
112
|
+
args: {
|
|
113
|
+
heading: "Bay 3",
|
|
114
|
+
isVisible: false,
|
|
115
|
+
onClose: () => {},
|
|
116
|
+
},
|
|
117
|
+
render: () => (
|
|
118
|
+
<StoryRow>
|
|
119
|
+
{SIZES.map((size) => (
|
|
120
|
+
<DialogDemo
|
|
121
|
+
buttonLabel={`Open ${size}`}
|
|
122
|
+
heading={`Read error — ${size}`}
|
|
123
|
+
key={size}
|
|
124
|
+
size={size}
|
|
125
|
+
/>
|
|
126
|
+
))}
|
|
127
|
+
</StoryRow>
|
|
128
|
+
),
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const AllStates: Story = {
|
|
132
|
+
args: {
|
|
133
|
+
heading: "Bay 3",
|
|
134
|
+
isVisible: false,
|
|
135
|
+
onClose: () => {},
|
|
136
|
+
},
|
|
137
|
+
render: () => (
|
|
138
|
+
<StoryRow>
|
|
139
|
+
<DialogDemo
|
|
140
|
+
buttonLabel="With a footer"
|
|
141
|
+
footer={(hide) => (
|
|
142
|
+
<>
|
|
143
|
+
<Button
|
|
144
|
+
appearance="soft"
|
|
145
|
+
intent="neutral"
|
|
146
|
+
onClick={hide}
|
|
147
|
+
size="sm"
|
|
148
|
+
>
|
|
149
|
+
Keep going
|
|
150
|
+
</Button>
|
|
151
|
+
|
|
152
|
+
<Button intent="danger" size="sm">
|
|
153
|
+
Stop the rip
|
|
154
|
+
</Button>
|
|
155
|
+
</>
|
|
156
|
+
)}
|
|
157
|
+
heading="Stop the rip?"
|
|
158
|
+
/>
|
|
159
|
+
|
|
160
|
+
<DialogDemo
|
|
161
|
+
buttonLabel="Not dismissable"
|
|
162
|
+
footer={(hide) => (
|
|
163
|
+
<>
|
|
164
|
+
<Button
|
|
165
|
+
appearance="soft"
|
|
166
|
+
intent="neutral"
|
|
167
|
+
onClick={hide}
|
|
168
|
+
size="sm"
|
|
169
|
+
>
|
|
170
|
+
Keep the titles
|
|
171
|
+
</Button>
|
|
172
|
+
|
|
173
|
+
<Button intent="danger" size="sm">
|
|
174
|
+
Erase and re-rip
|
|
175
|
+
</Button>
|
|
176
|
+
</>
|
|
177
|
+
)}
|
|
178
|
+
heading="This erases eight completed titles"
|
|
179
|
+
isDismissable={false}
|
|
180
|
+
/>
|
|
181
|
+
|
|
182
|
+
<DialogDemo
|
|
183
|
+
buttonLabel="Long body"
|
|
184
|
+
heading="MakeMKV log"
|
|
185
|
+
>
|
|
186
|
+
<div className="flex flex-col gap-2">
|
|
187
|
+
{Array.from(
|
|
188
|
+
{ length: 24 },
|
|
189
|
+
(_, index) =>
|
|
190
|
+
`14:0${index % 10}:22 title ${
|
|
191
|
+
index + 1
|
|
192
|
+
} AACS handshake complete, reading…`,
|
|
193
|
+
).map((line) => (
|
|
194
|
+
<p
|
|
195
|
+
className="font-mono text-content-secondary text-xs"
|
|
196
|
+
key={line}
|
|
197
|
+
>
|
|
198
|
+
{line}
|
|
199
|
+
</p>
|
|
200
|
+
))}
|
|
201
|
+
</div>
|
|
202
|
+
</DialogDemo>
|
|
203
|
+
</StoryRow>
|
|
204
|
+
),
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Stacking — one dialog opened from inside another. An
|
|
209
|
+
* `OverlayStackProvider` at the app root is the one piece of setup a
|
|
210
|
+
* lone dialog does not need: it renders a **single** shared scrim
|
|
211
|
+
* behind both, keeps the second on top, marks the first `inert`, and
|
|
212
|
+
* closes them top-first on Escape or an outside press. Open the first,
|
|
213
|
+
* then open the second from its footer.
|
|
214
|
+
*/
|
|
215
|
+
const StackedDemo = (): ReactNode => {
|
|
216
|
+
const first = useVisibility()
|
|
217
|
+
|
|
218
|
+
const second = useVisibility()
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<OverlayStackProvider>
|
|
222
|
+
<Button
|
|
223
|
+
appearance="soft"
|
|
224
|
+
onClick={first.show}
|
|
225
|
+
size="sm"
|
|
226
|
+
>
|
|
227
|
+
Open the first dialog
|
|
228
|
+
</Button>
|
|
229
|
+
|
|
230
|
+
<Dialog
|
|
231
|
+
footer={
|
|
232
|
+
<Button onClick={second.show} size="sm">
|
|
233
|
+
Open a second over it
|
|
234
|
+
</Button>
|
|
235
|
+
}
|
|
236
|
+
heading="First dialog"
|
|
237
|
+
isVisible={first.isVisible}
|
|
238
|
+
onClose={first.hide}
|
|
239
|
+
>
|
|
240
|
+
<p className="text-content-secondary text-sm">
|
|
241
|
+
This one stays open and goes `inert` while the
|
|
242
|
+
second is over it.
|
|
243
|
+
</p>
|
|
244
|
+
</Dialog>
|
|
245
|
+
|
|
246
|
+
<Dialog
|
|
247
|
+
footer={
|
|
248
|
+
<Button
|
|
249
|
+
appearance="soft"
|
|
250
|
+
intent="neutral"
|
|
251
|
+
onClick={second.hide}
|
|
252
|
+
size="sm"
|
|
253
|
+
>
|
|
254
|
+
Close this one
|
|
255
|
+
</Button>
|
|
256
|
+
}
|
|
257
|
+
heading="Second dialog"
|
|
258
|
+
isVisible={second.isVisible}
|
|
259
|
+
onClose={second.hide}
|
|
260
|
+
>
|
|
261
|
+
<p className="text-content-secondary text-sm">
|
|
262
|
+
One scrim behind both. Escape or a backdrop press
|
|
263
|
+
closes this one first.
|
|
264
|
+
</p>
|
|
265
|
+
</Dialog>
|
|
266
|
+
</OverlayStackProvider>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export const Stacked: Story = {
|
|
271
|
+
args: {
|
|
272
|
+
heading: "First dialog",
|
|
273
|
+
isVisible: false,
|
|
274
|
+
onClose: () => {},
|
|
275
|
+
},
|
|
276
|
+
render: () => <StackedDemo />,
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* A dialog is the one surface in this library that is **not** a
|
|
281
|
+
* container-query citizen, so it gets no `ContainerBoard`. Its size
|
|
282
|
+
* is measured against the viewport by definition — it is the thing
|
|
283
|
+
* covering the viewport — and boarding it at three container widths
|
|
284
|
+
* would be measuring something that has no bearing on it.
|
|
285
|
+
*
|
|
286
|
+
* What does vary is the clamp: every size caps at `85dvh`, and
|
|
287
|
+
* `full` takes the lot. `dvh` rather than `vh` because on a phone
|
|
288
|
+
* the two differ by the address bar, and a dialog whose footer is
|
|
289
|
+
* underneath it is a dialog with no buttons.
|
|
290
|
+
*/
|
|
291
|
+
export const Responsive: Story = {
|
|
292
|
+
args: {
|
|
293
|
+
heading: "Bay 3",
|
|
294
|
+
isVisible: false,
|
|
295
|
+
onClose: () => {},
|
|
296
|
+
},
|
|
297
|
+
render: () => (
|
|
298
|
+
<DialogDemo
|
|
299
|
+
buttonLabel="Open full-bleed"
|
|
300
|
+
heading="MakeMKV log — full"
|
|
301
|
+
size="full"
|
|
302
|
+
>
|
|
303
|
+
<p className="text-content-secondary text-sm">
|
|
304
|
+
`full` is the kiosk and phone case: the dialog is
|
|
305
|
+
the screen, so there is nothing behind it worth
|
|
306
|
+
showing.
|
|
307
|
+
</p>
|
|
308
|
+
</DialogDemo>
|
|
309
|
+
),
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* The keyboard contract, driven. `OverlayPanel`'s
|
|
314
|
+
* `FloatingFocusManager` supplies the focus trap and the focus
|
|
315
|
+
* restore, and `useDismiss` routes Escape through `onClose` — which,
|
|
316
|
+
* unlike the old native `<dialog>`, a synthetic `userEvent.keyboard`
|
|
317
|
+
* can now press for real.
|
|
318
|
+
*/
|
|
319
|
+
/**
|
|
320
|
+
* A confirm dialog with **no body**: the question is the `heading`
|
|
321
|
+
* (which is the dialog's accessible name) and the answers are the
|
|
322
|
+
* `footer`. `children` is optional precisely for this shape — the old
|
|
323
|
+
* chrome `Modal` extended `<dialog>`'s DOM props, so it allowed it,
|
|
324
|
+
* and image-viewer's delete-confirm renders exactly this.
|
|
325
|
+
*/
|
|
326
|
+
const NoBodyDemo = (): ReactNode => {
|
|
327
|
+
const { hide, isVisible, show } = useVisibility()
|
|
328
|
+
|
|
329
|
+
return (
|
|
330
|
+
<>
|
|
331
|
+
<Button appearance="soft" onClick={show} size="sm">
|
|
332
|
+
Delete the file
|
|
333
|
+
</Button>
|
|
334
|
+
|
|
335
|
+
<Dialog
|
|
336
|
+
footer={
|
|
337
|
+
<>
|
|
338
|
+
<Button
|
|
339
|
+
appearance="soft"
|
|
340
|
+
intent="neutral"
|
|
341
|
+
onClick={hide}
|
|
342
|
+
size="sm"
|
|
343
|
+
>
|
|
344
|
+
Keep it
|
|
345
|
+
</Button>
|
|
346
|
+
|
|
347
|
+
<Button intent="danger" size="sm">
|
|
348
|
+
Delete
|
|
349
|
+
</Button>
|
|
350
|
+
</>
|
|
351
|
+
}
|
|
352
|
+
heading="Delete this file? This cannot be undone."
|
|
353
|
+
isVisible={isVisible}
|
|
354
|
+
onClose={hide}
|
|
355
|
+
/>
|
|
356
|
+
</>
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export const NoBody: Story = {
|
|
361
|
+
args: {
|
|
362
|
+
heading: "Delete this file? This cannot be undone.",
|
|
363
|
+
isVisible: false,
|
|
364
|
+
onClose: () => {},
|
|
365
|
+
},
|
|
366
|
+
render: () => <NoBodyDemo />,
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export const Interactive: Story = {
|
|
370
|
+
args: {
|
|
371
|
+
heading: "Bay 3",
|
|
372
|
+
isVisible: false,
|
|
373
|
+
onClose: () => {},
|
|
374
|
+
},
|
|
375
|
+
render: () => (
|
|
376
|
+
<DialogDemo
|
|
377
|
+
buttonLabel="Stop the rip"
|
|
378
|
+
footer={(hide) => (
|
|
379
|
+
<>
|
|
380
|
+
<Button
|
|
381
|
+
appearance="soft"
|
|
382
|
+
intent="neutral"
|
|
383
|
+
onClick={hide}
|
|
384
|
+
size="sm"
|
|
385
|
+
>
|
|
386
|
+
Keep going
|
|
387
|
+
</Button>
|
|
388
|
+
|
|
389
|
+
<Button intent="danger" size="sm">
|
|
390
|
+
Stop it now
|
|
391
|
+
</Button>
|
|
392
|
+
</>
|
|
393
|
+
)}
|
|
394
|
+
heading="Stop the rip?"
|
|
395
|
+
/>
|
|
396
|
+
),
|
|
397
|
+
}
|