@propellerads/dialog 0.1.0 → 0.2.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/README.md +24 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +25 -23
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -99,6 +99,29 @@ for a fifth name:
|
|
|
99
99
|
.onboarding .dialog { --dialog-width: 720px; }
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
### Long content
|
|
103
|
+
|
|
104
|
+
The box grows with what is inside it, as shadcn's does. A dialog taller than the
|
|
105
|
+
screen scrolls with the page behind it, header and footer included. Nothing
|
|
106
|
+
inside is clipped, so a select menu, a date picker or a hint opens past the edge
|
|
107
|
+
of the box and stays clickable.
|
|
108
|
+
|
|
109
|
+
Content that is long by nature brings its own scroller, the way shadcn's
|
|
110
|
+
"Scrollable content" example does. The heading and the buttons then hold their
|
|
111
|
+
place:
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
<Dialog isOpen={isOpen} onClose={close} title="Preferred columns" footer={…}>
|
|
115
|
+
<div className="columns">…</div>
|
|
116
|
+
</Dialog>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```css
|
|
120
|
+
.columns { max-height: 50vh; overflow-y: auto; }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Keep menus out of that scroller: a select inside it is clipped by it.
|
|
124
|
+
|
|
102
125
|
## Props
|
|
103
126
|
|
|
104
127
|
| Prop | Type | Default | What it does |
|
|
@@ -108,7 +131,7 @@ for a fifth name:
|
|
|
108
131
|
| `onOpenChange` | `(isOpen: boolean) => void` | — | Called whenever the dialog opens or closes, whoever asked for it. |
|
|
109
132
|
| `onClose` | `() => void` | — | Called when the dialog asks to be closed. The shorthand for `onOpenChange`. |
|
|
110
133
|
| `title` | `ReactNode` | — | The heading, and the dialog's accessible name. |
|
|
111
|
-
| `children` | `ReactNode` | — | What the dialog is about.
|
|
134
|
+
| `children` | `ReactNode` | — | What the dialog is about. It does not scroll on its own: see [Long content](#long-content). |
|
|
112
135
|
| `footer` | `ReactNode` | — | The row at the bottom. Buttons end at the right edge. |
|
|
113
136
|
| `width` | `DialogWidth` | `'minimum'` | How wide the box may grow. |
|
|
114
137
|
| `isDismissable` | `boolean` | `true` | Whether the dialog can be dismissed at all: the cross, Escape and the backdrop together. |
|
package/dist/index.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.dialog__backdrop{--dialog-backdrop-blur:4px;z-index:var(--dialog-layer,3000);background-color:var(--dialog-backdrop,var(--over-fullscreen));-webkit-backdrop-filter:blur(var(--dialog-backdrop-blur));backdrop-filter:blur(var(--dialog-backdrop-blur));transition:opacity var(--duration-100) var(--ease-flat);position:fixed;inset:0}.dialog__viewport{z-index:var(--dialog-layer,3000);padding:var(--space-24);overscroll-behavior:contain;justify-content:center;align-items:
|
|
1
|
+
.dialog__backdrop{--dialog-backdrop-blur:4px;z-index:var(--dialog-layer,3000);background-color:var(--dialog-backdrop,var(--over-fullscreen));-webkit-backdrop-filter:blur(var(--dialog-backdrop-blur));backdrop-filter:blur(var(--dialog-backdrop-blur));transition:opacity var(--duration-100) var(--ease-flat);position:fixed;inset:0}.dialog__viewport{z-index:var(--dialog-layer,3000);padding:var(--space-24);overscroll-behavior:contain;justify-content:center;align-items:flex-start;display:flex;position:fixed;inset:0;overflow:auto}.dialog{--dialog-bg:var(--color-light-inversion);--dialog-fg:var(--color-dark-inversion);--dialog-padding:var(--space-24);--dialog-gap:var(--space-24);--dialog-radius:var(--radius-24);--dialog-shadow:var(--shadow-big);--dialog-width:296px;box-sizing:border-box;gap:var(--dialog-gap);width:min(var(--dialog-width), 100%);padding:var(--dialog-padding);border-radius:var(--dialog-radius);background-color:var(--dialog-bg);box-shadow:var(--dialog-shadow);color:var(--dialog-fg);font:var(--font-text);font-family:var(--font-family);--dialog-ease:var(--ease-enter);transition:opacity var(--duration-100) var(--ease-flat), transform var(--duration-100) var(--dialog-ease);outline:none;flex-direction:column;margin-block:auto;display:flex;position:relative}.dialog--minimum{--dialog-width:296px}.dialog--small{--dialog-width:400px}.dialog--medium{--dialog-width:600px}.dialog--big{--dialog-width:1080px}.dialog__header{align-items:flex-start;gap:var(--space-8);display:flex}.dialog__title{min-width:0;margin:var(--space-0);font:var(--font-h2);font-family:var(--font-family);word-break:break-word;flex:auto}.dialog__header:has(.dialog__close) .dialog__title{padding-block-start:calc(var(--space-4) + var(--space-2))}.dialog__close.button{--button-accent:var(--color-quiet);flex:none}.dialog__close svg{width:var(--space-24);height:var(--space-24);fill:currentColor;display:block}.dialog__content{word-break:break-word;flex:none}.dialog__footer{justify-content:flex-end;align-items:center;gap:var(--space-12);flex-wrap:wrap;flex:none;display:flex}.dialog__backdrop[data-starting-style],.dialog__backdrop[data-ending-style]{opacity:0}.dialog[data-starting-style],.dialog[data-ending-style]{opacity:0;transform:scale(.95)}.dialog[data-ending-style]{--dialog-ease:var(--ease-exit)}@media (width<=480px){.dialog{--dialog-padding:var(--space-16);--dialog-gap:var(--space-16);--dialog-width:100%}.dialog__viewport{padding:var(--space-16)}}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { Dialog as Dialog$1 } from '@base-ui-components/react/dialog';
|
|
1
|
+
import * as react from 'react';
|
|
3
2
|
import { ReactNode, ReactElement, ComponentPropsWithoutRef, Ref } from 'react';
|
|
3
|
+
import { Dialog as Dialog$1 } from '@base-ui-components/react/dialog';
|
|
4
4
|
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -96,7 +96,7 @@ declare const dialogVariants: (props?: ({
|
|
|
96
96
|
width?: "minimum" | "small" | "medium" | "big" | null | undefined;
|
|
97
97
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
98
98
|
|
|
99
|
-
declare const Dialog:
|
|
99
|
+
declare const Dialog: react.ForwardRefExoticComponent<Omit<DialogProps, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
100
100
|
|
|
101
101
|
export { Dialog, DialogWidth, Dialog as default, dialogVariants };
|
|
102
102
|
export type { DialogProps };
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { Dialog as e } from "@base-ui-components/react/dialog";
|
|
2
2
|
import t, { ButtonVariant as n } from "@propellerads/button";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { forwardRef as r } from "react";
|
|
4
|
+
import { clsx as i } from "clsx";
|
|
5
|
+
import { cva as a } from "class-variance-authority";
|
|
6
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
6
7
|
import './index.css';//#region src/cn.ts
|
|
7
|
-
var
|
|
8
|
+
var c = (...e) => i(e), l = {
|
|
8
9
|
Minimum: "minimum",
|
|
9
10
|
Small: "small",
|
|
10
11
|
Medium: "medium",
|
|
11
12
|
Big: "big"
|
|
12
|
-
},
|
|
13
|
+
}, u = a("dialog", {
|
|
13
14
|
variants: { width: {
|
|
14
15
|
minimum: "dialog--minimum",
|
|
15
16
|
small: "dialog--small",
|
|
@@ -17,51 +18,51 @@ var s = (...e) => r(e), c = {
|
|
|
17
18
|
big: "dialog--big"
|
|
18
19
|
} },
|
|
19
20
|
defaultVariants: { width: "minimum" }
|
|
20
|
-
}),
|
|
21
|
+
}), d = () => /* @__PURE__ */ o("svg", {
|
|
21
22
|
viewBox: "0 0 24 24",
|
|
22
23
|
"aria-hidden": "true",
|
|
23
24
|
focusable: "false",
|
|
24
|
-
children: /* @__PURE__ */
|
|
25
|
-
}),
|
|
25
|
+
children: /* @__PURE__ */ o("path", { d: "M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z" })
|
|
26
|
+
}), f = r(({ isOpen: r, defaultOpen: i, onOpenChange: a, onClose: f, title: p, footer: m, children: h, width: g = l.Minimum, isDismissable: _ = !0, closeLabel: v = "Close", closeIcon: y, trigger: b, render: x, className: S, backdropClassName: C, initialFocus: w, finalFocus: T, "aria-label": E }, D) => {
|
|
26
27
|
let O = (e, t) => {
|
|
27
28
|
if (!e && !_ && t.reason !== "close-press") {
|
|
28
29
|
t.cancel();
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
+
a?.(e), e || f?.();
|
|
32
33
|
}, k = p !== void 0 || _;
|
|
33
|
-
return /* @__PURE__ */
|
|
34
|
+
return /* @__PURE__ */ s(e.Root, {
|
|
34
35
|
open: r,
|
|
35
36
|
defaultOpen: i,
|
|
36
37
|
disablePointerDismissal: !_,
|
|
37
38
|
onOpenChange: O,
|
|
38
|
-
children: [b !== void 0 && /* @__PURE__ */
|
|
39
|
+
children: [b !== void 0 && /* @__PURE__ */ o(e.Trigger, { render: b }), /* @__PURE__ */ s(e.Portal, { children: [/* @__PURE__ */ o(e.Backdrop, { className: c("dialog__backdrop", C) }), /* @__PURE__ */ o(e.Viewport, {
|
|
39
40
|
className: "dialog__viewport",
|
|
40
|
-
children: /* @__PURE__ */
|
|
41
|
-
ref:
|
|
41
|
+
children: /* @__PURE__ */ s(e.Popup, {
|
|
42
|
+
ref: D,
|
|
42
43
|
render: x,
|
|
43
|
-
className:
|
|
44
|
+
className: c(u({ width: g }), S),
|
|
44
45
|
initialFocus: w,
|
|
45
46
|
finalFocus: T,
|
|
46
|
-
"aria-label": p === void 0 ?
|
|
47
|
+
"aria-label": p === void 0 ? E : void 0,
|
|
47
48
|
children: [
|
|
48
|
-
k && /* @__PURE__ */
|
|
49
|
+
k && /* @__PURE__ */ s("header", {
|
|
49
50
|
className: "dialog__header",
|
|
50
|
-
children: [p !== void 0 && /* @__PURE__ */
|
|
51
|
+
children: [p !== void 0 && /* @__PURE__ */ o(e.Title, {
|
|
51
52
|
className: "dialog__title",
|
|
52
53
|
children: p
|
|
53
|
-
}), _ && /* @__PURE__ */
|
|
54
|
+
}), _ && /* @__PURE__ */ o(e.Close, { render: /* @__PURE__ */ o(t, {
|
|
54
55
|
variant: n.Ghost,
|
|
55
56
|
className: "dialog__close",
|
|
56
|
-
icon: y ?? /* @__PURE__ */
|
|
57
|
+
icon: y ?? /* @__PURE__ */ o(d, {}),
|
|
57
58
|
"aria-label": v
|
|
58
59
|
}) })]
|
|
59
60
|
}),
|
|
60
|
-
h !== void 0 && /* @__PURE__ */
|
|
61
|
+
h !== void 0 && /* @__PURE__ */ o("div", {
|
|
61
62
|
className: "dialog__content",
|
|
62
63
|
children: h
|
|
63
64
|
}),
|
|
64
|
-
m !== void 0 && /* @__PURE__ */
|
|
65
|
+
m !== void 0 && /* @__PURE__ */ o("footer", {
|
|
65
66
|
className: "dialog__footer",
|
|
66
67
|
children: m
|
|
67
68
|
})
|
|
@@ -69,8 +70,9 @@ var s = (...e) => r(e), c = {
|
|
|
69
70
|
})
|
|
70
71
|
})] })]
|
|
71
72
|
});
|
|
72
|
-
};
|
|
73
|
+
});
|
|
74
|
+
f.displayName = "Dialog";
|
|
73
75
|
//#endregion
|
|
74
|
-
export {
|
|
76
|
+
export { f as Dialog, f as default, l as DialogWidth, u as dialogVariants };
|
|
75
77
|
|
|
76
78
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/cn.ts","../src/types.ts","../src/variants.ts","../src/index.tsx"],"sourcesContent":["import {type ClassValue, clsx} from 'clsx';\n\n/**\n * Joins class names. There is no tailwind-merge here on purpose: the dialog ships\n * its own stylesheet instead of utility classes, so there is nothing to\n * deduplicate.\n */\nconst cn = (...inputs: ClassValue[]): string => clsx(inputs);\n\nexport {cn};\n","import type {Dialog} from '@base-ui-components/react/dialog';\nimport type {ComponentPropsWithoutRef, ReactElement, ReactNode, Ref} from 'react';\n\n/**\n * How wide the box is allowed to grow. It is a maximum, not a width: the dialog\n * shrinks with the viewport, and below the mobile breakpoint every step is the\n * same — the screen decides.\n *\n * `Minimum` is the design file's own size and the default. The three above it\n * carry the sizes v4 called `small`, `medium` and `big`, so a popup that names\n * its size keeps it through the move.\n */\nexport const DialogWidth = {\n Minimum: 'minimum',\n Small: 'small',\n Medium: 'medium',\n Big: 'big',\n} as const;\nexport type DialogWidth = (typeof DialogWidth)[keyof typeof DialogWidth];\n\ntype DialogPopupProps = ComponentPropsWithoutRef<typeof Dialog.Popup>;\n\nexport type DialogProps = {\n /** Whether the dialog is open. Leave it out for an uncontrolled dialog driven by `trigger`. */\n isOpen?: boolean;\n /** The open state an uncontrolled dialog starts in. @default false */\n defaultOpen?: boolean;\n /** Called whenever the dialog opens or closes, whoever asked for it. */\n onOpenChange?: (isOpen: boolean) => void;\n /**\n * Called when the dialog asks to be closed — the close button, the backdrop,\n * Escape. The shorthand for `onOpenChange` when only the closing matters.\n */\n onClose?: () => void;\n /**\n * The heading. Read out as the dialog's accessible name, so a dialog without\n * one has to carry an `aria-label` instead.\n */\n title?: ReactNode;\n /** The row at the bottom. Buttons go here, and they end at the right edge. */\n footer?: ReactNode;\n /** What the dialog is about. */\n children?: ReactNode;\n /** How wide the box may grow. @default 'minimum' */\n width?: DialogWidth;\n /**\n * Whether the dialog can be dismissed at all — the cross, Escape and the\n * backdrop are one decision, not three. Turn it off for a choice that has to be\n * taken: the cross goes with them, so the dialog does not offer a way out it\n * will not honour, and it then closes only through what its own content does.\n * @default true\n */\n isDismissable?: boolean;\n /** The cross's accessible name. @default 'Close' */\n closeLabel?: string;\n /** Replaces the glyph inside the close button. */\n closeIcon?: ReactNode;\n /**\n * What opens the dialog. The element itself becomes the trigger — Base UI's\n * polymorphism, so a `<Button>` here stays a single button rather than being\n * wrapped in one.\n */\n trigger?: ReactElement<Record<string, unknown>>;\n /**\n * Replaces the box with another element — Base UI's polymorphism, in place of\n * Radix's `asChild`. The header, the content and the footer are then inside\n * whatever you pass, which is how a dialog becomes a form:\n * `render={<form onSubmit={save} />}`.\n */\n render?: DialogPopupProps['render'];\n /** Goes on the box. */\n className?: string;\n /** Goes on the backdrop, for a product that dims its own way. */\n backdropClassName?: string;\n /** Where the focus lands when the dialog opens. @default true — the first tabbable element */\n initialFocus?: DialogPopupProps['initialFocus'];\n /** Where the focus returns when it closes. @default true — the trigger */\n finalFocus?: DialogPopupProps['finalFocus'];\n ref?: Ref<HTMLDivElement>;\n /** Required when there is no `title`: a dialog has to say what it is. */\n 'aria-label'?: string;\n};\n","import {cva} from 'class-variance-authority';\n\n/**\n * The class names the stylesheet defines.\n *\n * Width is the only axis. Everything else about a dialog — the padding, the\n * corner, the shadow — is the same whatever is inside it, and the parts are\n * plain element classes rather than variants.\n *\n * `dialogVariants` is exported so a product that renders Base UI's parts itself\n * can still wear the dialog's look:\n * `<Dialog.Popup className={dialogVariants({width: 'medium'})} />`.\n */\nconst dialogVariants = cva('dialog', {\n variants: {\n width: {\n minimum: 'dialog--minimum',\n small: 'dialog--small',\n medium: 'dialog--medium',\n big: 'dialog--big',\n },\n },\n defaultVariants: {\n width: 'minimum',\n },\n});\n\nexport {dialogVariants};\n","import {Dialog as BaseDialog} from '@base-ui-components/react/dialog';\nimport Button, {ButtonVariant} from '@propellerads/button';\nimport './dialog.css';\nimport {cn} from './cn';\nimport {type DialogProps, DialogWidth} from './types';\nimport {dialogVariants} from './variants';\n\n/** The same glyph @propellerads/icon exports as `Close`, inlined so the dialog\n * does not pull styled-components in for one cross. `closeIcon` replaces it. */\nconst CloseGlyph = () => (\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z\" />\n </svg>\n);\n\nconst Dialog = ({\n isOpen,\n defaultOpen,\n onOpenChange,\n onClose,\n title,\n footer,\n children,\n width = DialogWidth.Minimum,\n isDismissable = true,\n closeLabel = 'Close',\n closeIcon,\n trigger,\n render,\n className,\n backdropClassName,\n initialFocus,\n finalFocus,\n ref,\n 'aria-label': ariaLabel,\n}: DialogProps) => {\n const handleOpenChange = (open: boolean, details: BaseDialog.Root.ChangeEventDetails): void => {\n // A dialog that is not dismissable closes through what it renders itself and\n // nothing else: the backdrop is already handled by `disablePointerDismissal`,\n // and this turns away Escape and anything else that asks from the outside.\n if (!open && !isDismissable && details.reason !== 'close-press') {\n details.cancel();\n return;\n }\n\n onOpenChange?.(open);\n\n if (!open) {\n onClose?.();\n }\n };\n\n // The cross is the visible half of being dismissable.\n const hasHeader = title !== undefined || isDismissable;\n\n return (\n <BaseDialog.Root\n open={isOpen}\n defaultOpen={defaultOpen}\n disablePointerDismissal={!isDismissable}\n onOpenChange={handleOpenChange}\n >\n {trigger !== undefined && <BaseDialog.Trigger render={trigger} />}\n <BaseDialog.Portal>\n <BaseDialog.Backdrop className={cn('dialog__backdrop', backdropClassName)} />\n <BaseDialog.Viewport className=\"dialog__viewport\">\n <BaseDialog.Popup\n ref={ref}\n render={render}\n className={cn(dialogVariants({width}), className)}\n initialFocus={initialFocus}\n finalFocus={finalFocus}\n aria-label={title === undefined ? ariaLabel : undefined}\n >\n {hasHeader && (\n <header className=\"dialog__header\">\n {title !== undefined && <BaseDialog.Title className=\"dialog__title\">{title}</BaseDialog.Title>}\n {isDismissable && (\n // The cross is a ghost icon button like any other, repainted by\n // the class: it is the quietest thing in the dialog, and the\n // action colour belongs to the footer.\n <BaseDialog.Close\n render={\n <Button\n variant={ButtonVariant.Ghost}\n className=\"dialog__close\"\n icon={closeIcon ?? <CloseGlyph />}\n aria-label={closeLabel}\n />\n }\n />\n )}\n </header>\n )}\n {children !== undefined && <div className=\"dialog__content\">{children}</div>}\n {footer !== undefined && <footer className=\"dialog__footer\">{footer}</footer>}\n </BaseDialog.Popup>\n </BaseDialog.Viewport>\n </BaseDialog.Portal>\n </BaseDialog.Root>\n );\n};\n\nexport default Dialog;\n\nexport type {DialogProps};\nexport {Dialog, DialogWidth, dialogVariants};\n"],"mappings":";;;;;;AAOA,IAAM,KAAM,GAAG,MAAiC,EAAK,EAAO,ECK/C,IAAc;CACzB,SAAS;CACT,OAAO;CACP,QAAQ;CACR,KAAK;CACN,ECJK,IAAiB,EAAI,UAAU;CACnC,UAAU,EACR,OAAO;EACL,SAAS;EACT,OAAO;EACP,QAAQ;EACR,KAAK;EACN,EACF;CACD,iBAAiB,EACf,OAAO,WACR;CACF,CAAC,EChBI,UACJ,kBAAC,OAAD;CAAK,SAAQ;CAAY,eAAY;CAAO,WAAU;WACpD,kBAAC,QAAD,EAAM,GAAE,mRAAoR,CAAA;CACxR,CAAA,EAGF,KAAU,EACd,WACA,gBACA,iBACA,YACA,UACA,WACA,aACA,WAAQ,EAAY,SACpB,mBAAgB,IAChB,gBAAa,SACb,cACA,YACA,WACA,cACA,sBACA,iBACA,eACA,QACA,cAAc,QACG;CACjB,IAAM,KAAoB,GAAe,MAAsD;EAI7F,IAAI,CAAC,KAAQ,CAAC,KAAiB,EAAQ,WAAW,eAAe;GAC/D,EAAQ,QAAQ;GAChB;;EAKF,AAFA,IAAe,EAAK,EAEf,KACH,KAAW;IAKT,IAAY,MAAU,KAAA,KAAa;CAEzC,OACE,kBAAC,EAAW,MAAZ;EACE,MAAM;EACO;EACb,yBAAyB,CAAC;EAC1B,cAAc;YAJhB,CAMG,MAAY,KAAA,KAAa,kBAAC,EAAW,SAAZ,EAAoB,QAAQ,GAAW,CAAA,EACjE,kBAAC,EAAW,QAAZ,EAAA,UAAA,CACE,kBAAC,EAAW,UAAZ,EAAqB,WAAW,EAAG,oBAAoB,EAAkB,EAAI,CAAA,EAC7E,kBAAC,EAAW,UAAZ;GAAqB,WAAU;aAC7B,kBAAC,EAAW,OAAZ;IACO;IACG;IACR,WAAW,EAAG,EAAe,EAAC,UAAM,CAAC,EAAE,EAAU;IACnC;IACF;IACZ,cAAY,MAAU,KAAA,IAAY,IAAY,KAAA;cANhD;KAQG,KACC,kBAAC,UAAD;MAAQ,WAAU;gBAAlB,CACG,MAAU,KAAA,KAAa,kBAAC,EAAW,OAAZ;OAAkB,WAAU;iBAAiB;OAAyB,CAAA,EAC7F,KAIC,kBAAC,EAAW,OAAZ,EACE,QACE,kBAAC,GAAD;OACE,SAAS,EAAc;OACvB,WAAU;OACV,MAAM,KAAa,kBAAC,GAAD,EAAc,CAAA;OACjC,cAAY;OACZ,CAAA,EAEJ,CAAA,CAEG;;KAEV,MAAa,KAAA,KAAa,kBAAC,OAAD;MAAK,WAAU;MAAmB;MAAe,CAAA;KAC3E,MAAW,KAAA,KAAa,kBAAC,UAAD;MAAQ,WAAU;gBAAkB;MAAgB,CAAA;KAC5D;;GACC,CAAA,CACJ,EAAA,CAAA,CACJ"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/cn.ts","../src/types.ts","../src/variants.ts","../src/index.tsx"],"sourcesContent":["import {type ClassValue, clsx} from 'clsx';\n\n/**\n * Joins class names. There is no tailwind-merge here on purpose: the dialog ships\n * its own stylesheet instead of utility classes, so there is nothing to\n * deduplicate.\n */\nconst cn = (...inputs: ClassValue[]): string => clsx(inputs);\n\nexport {cn};\n","import type {Dialog} from '@base-ui-components/react/dialog';\nimport type {ComponentPropsWithoutRef, ReactElement, ReactNode, Ref} from 'react';\n\n/**\n * How wide the box is allowed to grow. It is a maximum, not a width: the dialog\n * shrinks with the viewport, and below the mobile breakpoint every step is the\n * same — the screen decides.\n *\n * `Minimum` is the design file's own size and the default. The three above it\n * carry the sizes v4 called `small`, `medium` and `big`, so a popup that names\n * its size keeps it through the move.\n */\nexport const DialogWidth = {\n Minimum: 'minimum',\n Small: 'small',\n Medium: 'medium',\n Big: 'big',\n} as const;\nexport type DialogWidth = (typeof DialogWidth)[keyof typeof DialogWidth];\n\ntype DialogPopupProps = ComponentPropsWithoutRef<typeof Dialog.Popup>;\n\nexport type DialogProps = {\n /** Whether the dialog is open. Leave it out for an uncontrolled dialog driven by `trigger`. */\n isOpen?: boolean;\n /** The open state an uncontrolled dialog starts in. @default false */\n defaultOpen?: boolean;\n /** Called whenever the dialog opens or closes, whoever asked for it. */\n onOpenChange?: (isOpen: boolean) => void;\n /**\n * Called when the dialog asks to be closed — the close button, the backdrop,\n * Escape. The shorthand for `onOpenChange` when only the closing matters.\n */\n onClose?: () => void;\n /**\n * The heading. Read out as the dialog's accessible name, so a dialog without\n * one has to carry an `aria-label` instead.\n */\n title?: ReactNode;\n /** The row at the bottom. Buttons go here, and they end at the right edge. */\n footer?: ReactNode;\n /** What the dialog is about. */\n children?: ReactNode;\n /** How wide the box may grow. @default 'minimum' */\n width?: DialogWidth;\n /**\n * Whether the dialog can be dismissed at all — the cross, Escape and the\n * backdrop are one decision, not three. Turn it off for a choice that has to be\n * taken: the cross goes with them, so the dialog does not offer a way out it\n * will not honour, and it then closes only through what its own content does.\n * @default true\n */\n isDismissable?: boolean;\n /** The cross's accessible name. @default 'Close' */\n closeLabel?: string;\n /** Replaces the glyph inside the close button. */\n closeIcon?: ReactNode;\n /**\n * What opens the dialog. The element itself becomes the trigger — Base UI's\n * polymorphism, so a `<Button>` here stays a single button rather than being\n * wrapped in one.\n */\n trigger?: ReactElement<Record<string, unknown>>;\n /**\n * Replaces the box with another element — Base UI's polymorphism, in place of\n * Radix's `asChild`. The header, the content and the footer are then inside\n * whatever you pass, which is how a dialog becomes a form:\n * `render={<form onSubmit={save} />}`.\n */\n render?: DialogPopupProps['render'];\n /** Goes on the box. */\n className?: string;\n /** Goes on the backdrop, for a product that dims its own way. */\n backdropClassName?: string;\n /** Where the focus lands when the dialog opens. @default true — the first tabbable element */\n initialFocus?: DialogPopupProps['initialFocus'];\n /** Where the focus returns when it closes. @default true — the trigger */\n finalFocus?: DialogPopupProps['finalFocus'];\n ref?: Ref<HTMLDivElement>;\n /** Required when there is no `title`: a dialog has to say what it is. */\n 'aria-label'?: string;\n};\n","import {cva} from 'class-variance-authority';\n\n/**\n * The class names the stylesheet defines.\n *\n * Width is the only axis. Everything else about a dialog — the padding, the\n * corner, the shadow — is the same whatever is inside it, and the parts are\n * plain element classes rather than variants.\n *\n * `dialogVariants` is exported so a product that renders Base UI's parts itself\n * can still wear the dialog's look:\n * `<Dialog.Popup className={dialogVariants({width: 'medium'})} />`.\n */\nconst dialogVariants = cva('dialog', {\n variants: {\n width: {\n minimum: 'dialog--minimum',\n small: 'dialog--small',\n medium: 'dialog--medium',\n big: 'dialog--big',\n },\n },\n defaultVariants: {\n width: 'minimum',\n },\n});\n\nexport {dialogVariants};\n","import {Dialog as BaseDialog} from '@base-ui-components/react/dialog';\nimport Button, {ButtonVariant} from '@propellerads/button';\nimport {forwardRef, type PropsWithoutRef} from 'react';\nimport './dialog.css';\nimport {cn} from './cn';\nimport {type DialogProps, DialogWidth} from './types';\nimport {dialogVariants} from './variants';\n\n/** The same glyph @propellerads/icon exports as `Close`, inlined so the dialog\n * does not pull styled-components in for one cross. `closeIcon` replaces it. */\nconst CloseGlyph = () => (\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z\" />\n </svg>\n);\n\nconst Dialog = forwardRef<HTMLDivElement, PropsWithoutRef<DialogProps>>(\n (\n {\n isOpen,\n defaultOpen,\n onOpenChange,\n onClose,\n title,\n footer,\n children,\n width = DialogWidth.Minimum,\n isDismissable = true,\n closeLabel = 'Close',\n closeIcon,\n trigger,\n render,\n className,\n backdropClassName,\n initialFocus,\n finalFocus,\n 'aria-label': ariaLabel,\n },\n ref,\n ) => {\n const handleOpenChange = (open: boolean, details: BaseDialog.Root.ChangeEventDetails): void => {\n // A dialog that is not dismissable closes through what it renders itself and\n // nothing else: the backdrop is already handled by `disablePointerDismissal`,\n // and this turns away Escape and anything else that asks from the outside.\n if (!open && !isDismissable && details.reason !== 'close-press') {\n details.cancel();\n return;\n }\n\n onOpenChange?.(open);\n\n if (!open) {\n onClose?.();\n }\n };\n\n // The cross is the visible half of being dismissable.\n const hasHeader = title !== undefined || isDismissable;\n\n return (\n <BaseDialog.Root\n open={isOpen}\n defaultOpen={defaultOpen}\n disablePointerDismissal={!isDismissable}\n onOpenChange={handleOpenChange}\n >\n {trigger !== undefined && <BaseDialog.Trigger render={trigger} />}\n <BaseDialog.Portal>\n <BaseDialog.Backdrop className={cn('dialog__backdrop', backdropClassName)} />\n <BaseDialog.Viewport className=\"dialog__viewport\">\n <BaseDialog.Popup\n ref={ref}\n render={render}\n className={cn(dialogVariants({width}), className)}\n initialFocus={initialFocus}\n finalFocus={finalFocus}\n aria-label={title === undefined ? ariaLabel : undefined}\n >\n {hasHeader && (\n <header className=\"dialog__header\">\n {title !== undefined && <BaseDialog.Title className=\"dialog__title\">{title}</BaseDialog.Title>}\n {isDismissable && (\n // The cross is a ghost icon button like any other, repainted by\n // the class: it is the quietest thing in the dialog, and the\n // action colour belongs to the footer.\n <BaseDialog.Close\n render={\n <Button\n variant={ButtonVariant.Ghost}\n className=\"dialog__close\"\n icon={closeIcon ?? <CloseGlyph />}\n aria-label={closeLabel}\n />\n }\n />\n )}\n </header>\n )}\n {children !== undefined && <div className=\"dialog__content\">{children}</div>}\n {footer !== undefined && <footer className=\"dialog__footer\">{footer}</footer>}\n </BaseDialog.Popup>\n </BaseDialog.Viewport>\n </BaseDialog.Portal>\n </BaseDialog.Root>\n );\n },\n);\nDialog.displayName = 'Dialog';\n\nexport default Dialog;\n\nexport type {DialogProps};\nexport {Dialog, DialogWidth, dialogVariants};\n"],"mappings":";;;;;;;AAOA,IAAM,KAAM,GAAG,MAAiC,EAAK,EAAO,ECK/C,IAAc;CACzB,SAAS;CACT,OAAO;CACP,QAAQ;CACR,KAAK;CACN,ECJK,IAAiB,EAAI,UAAU;CACnC,UAAU,EACR,OAAO;EACL,SAAS;EACT,OAAO;EACP,QAAQ;EACR,KAAK;EACN,EACF;CACD,iBAAiB,EACf,OAAO,WACR;CACF,CAAC,ECfI,UACJ,kBAAC,OAAD;CAAK,SAAQ;CAAY,eAAY;CAAO,WAAU;WACpD,kBAAC,QAAD,EAAM,GAAE,mRAAoR,CAAA;CACxR,CAAA,EAGF,IAAS,GAEX,EACE,WACA,gBACA,iBACA,YACA,UACA,WACA,aACA,WAAQ,EAAY,SACpB,mBAAgB,IAChB,gBAAa,SACb,cACA,YACA,WACA,cACA,sBACA,iBACA,eACA,cAAc,KAEhB,MACG;CACH,IAAM,KAAoB,GAAe,MAAsD;EAI7F,IAAI,CAAC,KAAQ,CAAC,KAAiB,EAAQ,WAAW,eAAe;GAC/D,EAAQ,QAAQ;GAChB;;EAKF,AAFA,IAAe,EAAK,EAEf,KACH,KAAW;IAKT,IAAY,MAAU,KAAA,KAAa;CAEzC,OACE,kBAAC,EAAW,MAAZ;EACE,MAAM;EACO;EACb,yBAAyB,CAAC;EAC1B,cAAc;YAJhB,CAMG,MAAY,KAAA,KAAa,kBAAC,EAAW,SAAZ,EAAoB,QAAQ,GAAW,CAAA,EACjE,kBAAC,EAAW,QAAZ,EAAA,UAAA,CACE,kBAAC,EAAW,UAAZ,EAAqB,WAAW,EAAG,oBAAoB,EAAkB,EAAI,CAAA,EAC7E,kBAAC,EAAW,UAAZ;GAAqB,WAAU;aAC7B,kBAAC,EAAW,OAAZ;IACO;IACG;IACR,WAAW,EAAG,EAAe,EAAC,UAAM,CAAC,EAAE,EAAU;IACnC;IACF;IACZ,cAAY,MAAU,KAAA,IAAY,IAAY,KAAA;cANhD;KAQG,KACC,kBAAC,UAAD;MAAQ,WAAU;gBAAlB,CACG,MAAU,KAAA,KAAa,kBAAC,EAAW,OAAZ;OAAkB,WAAU;iBAAiB;OAAyB,CAAA,EAC7F,KAIC,kBAAC,EAAW,OAAZ,EACE,QACE,kBAAC,GAAD;OACE,SAAS,EAAc;OACvB,WAAU;OACV,MAAM,KAAa,kBAAC,GAAD,EAAc,CAAA;OACjC,cAAY;OACZ,CAAA,EAEJ,CAAA,CAEG;;KAEV,MAAa,KAAA,KAAa,kBAAC,OAAD;MAAK,WAAU;MAAmB;MAAe,CAAA;KAC3E,MAAW,KAAA,KAAa,kBAAC,UAAD;MAAQ,WAAU;gBAAkB;MAAgB,CAAA;KAC5D;;GACC,CAAA,CACJ,EAAA,CAAA,CACJ;;EAGvB;AACD,EAAO,cAAc"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@propellerads/dialog",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"style": "dist/index.css",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"homepage": "https://github.com/propellerads/ui-components#readme",
|
|
30
30
|
"private": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@propellerads/button": "^11.0.
|
|
32
|
+
"@propellerads/button": "^11.0.1",
|
|
33
33
|
"@base-ui-components/react": "1.0.0-rc.0",
|
|
34
34
|
"class-variance-authority": "^0.7.1",
|
|
35
35
|
"clsx": "^2.1.1"
|