@pushwoosh/dumb-components 4.1.0 → 4.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.
- package/Button/Button.js +17 -3
- package/Button/constants.d.ts +3 -0
- package/Button/constants.js +12 -7
- package/Button/styles.d.ts +6 -0
- package/Button/styles.js +16 -7
- package/Button/types.d.ts +6 -1
- package/DropdownMenu/components/DropdownMenuButton/DropdownMenuButton.js +2 -0
- package/DropdownMenu/components/DropdownMenuButton/types.d.ts +6 -1
- package/package.json +1 -1
package/Button/Button.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronIcon } from '@pushwoosh/kit-icons';
|
|
2
3
|
import { Spinner } from '../Spinner';
|
|
3
4
|
import { geometryBySizeAndView, spinnerSizeBySize } from './constants';
|
|
4
|
-
import { ButtonBox, ButtonIcon, ButtonSpinner, ButtonContent } from './styles';
|
|
5
|
+
import { ButtonBox, ButtonChevron, ButtonIcon, ButtonSpinner, ButtonContent } from './styles';
|
|
5
6
|
export function Button(props) {
|
|
6
7
|
const {
|
|
7
8
|
type = 'button',
|
|
@@ -14,6 +15,7 @@ export function Button(props) {
|
|
|
14
15
|
isOpen,
|
|
15
16
|
icon,
|
|
16
17
|
rightIcon,
|
|
18
|
+
hasChevron,
|
|
17
19
|
children,
|
|
18
20
|
onClick,
|
|
19
21
|
...rest
|
|
@@ -26,6 +28,10 @@ export function Button(props) {
|
|
|
26
28
|
console.error(`[Button]: cannot render with color "${color}" and size "${size}" and view "${view}"`);
|
|
27
29
|
return null;
|
|
28
30
|
}
|
|
31
|
+
if (process.env.NODE_ENV === 'development' && hasChevron && rightIcon) {
|
|
32
|
+
console.warn('[Button]: rightIcon is ignored when hasChevron is set');
|
|
33
|
+
}
|
|
34
|
+
const isShowRightIcon = !!rightIcon && !hasChevron;
|
|
29
35
|
const isShowOverlaySpinner = !!children && !icon && view !== 'ghost' && isLoading;
|
|
30
36
|
const isDisabledNonButton = !!isDisabled && !!rest.as && rest.as !== 'button';
|
|
31
37
|
const handleClick = isDisabledNonButton ? event => {
|
|
@@ -47,7 +53,8 @@ export function Button(props) {
|
|
|
47
53
|
"$isOpen": isOpen,
|
|
48
54
|
"$hideContent": isShowOverlaySpinner,
|
|
49
55
|
"$hasLeftIcon": !!icon,
|
|
50
|
-
"$hasRightIcon":
|
|
56
|
+
"$hasRightIcon": isShowRightIcon,
|
|
57
|
+
"$hasChevron": hasChevron,
|
|
51
58
|
"$hasContent": !!children,
|
|
52
59
|
ref: boxRef,
|
|
53
60
|
children: [icon && _jsx(ButtonIcon, {
|
|
@@ -58,8 +65,15 @@ export function Button(props) {
|
|
|
58
65
|
children: _jsx("div", {
|
|
59
66
|
children: children
|
|
60
67
|
})
|
|
61
|
-
}),
|
|
68
|
+
}), isShowRightIcon && _jsx(ButtonIcon, {
|
|
62
69
|
children: rightIcon
|
|
70
|
+
}), hasChevron && _jsx(ButtonChevron, {
|
|
71
|
+
"$isOpen": isOpen,
|
|
72
|
+
"aria-hidden": true,
|
|
73
|
+
children: _jsx(ChevronIcon, {
|
|
74
|
+
size: "small",
|
|
75
|
+
direction: "down"
|
|
76
|
+
})
|
|
63
77
|
}), isShowOverlaySpinner && _jsx(ButtonSpinner, {
|
|
64
78
|
children: _jsx(Spinner, {
|
|
65
79
|
color: "danger",
|
package/Button/constants.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ export type ButtonGeometry = {
|
|
|
7
7
|
readonly labelPadding: string;
|
|
8
8
|
readonly gap: string;
|
|
9
9
|
readonly iconSlot: string;
|
|
10
|
+
/** Label→chevron distance; defaults to `gap`. */
|
|
11
|
+
readonly chevronGap?: string;
|
|
12
|
+
readonly chevronEdge: string;
|
|
10
13
|
readonly fontSize: string;
|
|
11
14
|
readonly lineHeight: number;
|
|
12
15
|
readonly fontWeight: number;
|
package/Button/constants.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FontSize, FontWeight, LineHeight, UnitSize } from '@pushwoosh/kit-constants';
|
|
2
|
+
const rhythm = '6px';
|
|
2
3
|
const fieldTypography = {
|
|
3
4
|
fontSize: FontSize.REGULAR,
|
|
4
5
|
lineHeight: LineHeight.REGULAR,
|
|
@@ -13,15 +14,17 @@ const compactTypography = {
|
|
|
13
14
|
};
|
|
14
15
|
const boxed = {
|
|
15
16
|
border: '1px',
|
|
16
|
-
padding:
|
|
17
|
-
labelPadding:
|
|
18
|
-
gap: '0px'
|
|
17
|
+
padding: rhythm,
|
|
18
|
+
labelPadding: rhythm,
|
|
19
|
+
gap: '0px',
|
|
20
|
+
chevronGap: '4px'
|
|
19
21
|
};
|
|
20
22
|
const fieldBox = {
|
|
21
23
|
...fieldTypography,
|
|
22
24
|
...boxed,
|
|
23
25
|
height: UnitSize.FIELD_HEIGHT,
|
|
24
|
-
iconSlot: UnitSize.ICON_MEDIUM
|
|
26
|
+
iconSlot: UnitSize.ICON_MEDIUM,
|
|
27
|
+
chevronEdge: '8px'
|
|
25
28
|
};
|
|
26
29
|
/** Geometry per `size` × `view`; colors live in `colors.ts`. */
|
|
27
30
|
export const geometryBySizeAndView = {
|
|
@@ -34,7 +37,8 @@ export const geometryBySizeAndView = {
|
|
|
34
37
|
...compactTypography,
|
|
35
38
|
...boxed,
|
|
36
39
|
height: UnitSize.COMPACT_HEIGHT,
|
|
37
|
-
iconSlot: UnitSize.ICON_SMALL
|
|
40
|
+
iconSlot: UnitSize.ICON_SMALL,
|
|
41
|
+
chevronEdge: rhythm
|
|
38
42
|
},
|
|
39
43
|
ghost: {
|
|
40
44
|
...compactTypography,
|
|
@@ -42,8 +46,9 @@ export const geometryBySizeAndView = {
|
|
|
42
46
|
border: '0px',
|
|
43
47
|
padding: '0px',
|
|
44
48
|
labelPadding: '0px',
|
|
45
|
-
gap:
|
|
46
|
-
iconSlot: UnitSize.ICON_SMALL
|
|
49
|
+
gap: rhythm,
|
|
50
|
+
iconSlot: UnitSize.ICON_SMALL,
|
|
51
|
+
chevronEdge: '0px'
|
|
47
52
|
}
|
|
48
53
|
}
|
|
49
54
|
};
|
package/Button/styles.d.ts
CHANGED
|
@@ -5,6 +5,11 @@ export declare const ButtonIcon: import("styled-components/dist/types").IStyledC
|
|
|
5
5
|
export declare const ButtonContent: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style"> & {
|
|
6
6
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
7
7
|
}> & string;
|
|
8
|
+
export declare const ButtonChevron: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Merged<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style"> & {
|
|
9
|
+
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
$isOpen?: boolean;
|
|
12
|
+
}>> & string;
|
|
8
13
|
export declare const ButtonSpinner: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style"> & {
|
|
9
14
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
10
15
|
}> & string;
|
|
@@ -18,6 +23,7 @@ type ButtonBoxProps = {
|
|
|
18
23
|
$hideContent?: boolean;
|
|
19
24
|
$hasLeftIcon?: boolean;
|
|
20
25
|
$hasRightIcon?: boolean;
|
|
26
|
+
$hasChevron?: boolean;
|
|
21
27
|
$hasContent?: boolean;
|
|
22
28
|
};
|
|
23
29
|
export declare const ButtonBox: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Merged<Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "style"> & {
|
package/Button/styles.js
CHANGED
|
@@ -10,24 +10,32 @@ export const ButtonContent = styled.div.withConfig({
|
|
|
10
10
|
displayName: "ButtonContent",
|
|
11
11
|
componentId: "sc-qltvdl-1"
|
|
12
12
|
})(["display:grid;justify-content:start;padding:0 var(--btn-label-padding);& > div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}&:first-child{justify-content:center;}"]);
|
|
13
|
+
export const ButtonChevron = styled.span.withConfig({
|
|
14
|
+
displayName: "ButtonChevron",
|
|
15
|
+
componentId: "sc-qltvdl-2"
|
|
16
|
+
})(["display:flex;margin-right:calc(var(--btn-chevron-edge) - var(--btn-padding));transform:", ";transition:transform 150ms;@media (prefers-reduced-motion:reduce){transition:none;}"], ({
|
|
17
|
+
$isOpen
|
|
18
|
+
}) => $isOpen ? 'rotate(180deg)' : 'none');
|
|
13
19
|
export const ButtonSpinner = styled.div.withConfig({
|
|
14
20
|
displayName: "ButtonSpinner",
|
|
15
|
-
componentId: "sc-qltvdl-
|
|
21
|
+
componentId: "sc-qltvdl-3"
|
|
16
22
|
})(["position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-wrap:nowrap;align-items:center;justify-content:center;"]);
|
|
17
23
|
function geometryVars(geometry) {
|
|
18
|
-
return css(["--btn-height:", ";--btn-border:", ";--btn-padding:", ";--btn-label-padding:", ";--btn-gap:", ";--btn-icon-slot:", ";font-size:", ";line-height:", ";font-weight:", ";text-transform:", ";"], geometry.height, geometry.border, geometry.padding, geometry.labelPadding, geometry.gap, geometry.iconSlot, geometry.fontSize, geometry.lineHeight, geometry.fontWeight, geometry.textTransform);
|
|
24
|
+
return css(["--btn-height:", ";--btn-border:", ";--btn-padding:", ";--btn-label-padding:", ";--btn-gap:", ";--btn-icon-slot:", ";--btn-chevron-gap:", ";--btn-chevron-edge:", ";font-size:", ";line-height:", ";font-weight:", ";text-transform:", ";"], geometry.height, geometry.border, geometry.padding, geometry.labelPadding, geometry.gap, geometry.iconSlot, geometry.chevronGap ?? geometry.gap, geometry.chevronEdge, geometry.fontSize, geometry.lineHeight, geometry.fontWeight, geometry.textTransform);
|
|
19
25
|
}
|
|
20
26
|
const ghostCompactHitArea = css(["&::before{content:'';position:absolute;inset:calc((var(--btn-height) - ", ") / 2) 0;}"], UnitSize.COMPACT_HEIGHT);
|
|
21
27
|
export const ButtonBox = styled.button.withConfig({
|
|
22
28
|
displayName: "ButtonBox",
|
|
23
|
-
componentId: "sc-qltvdl-
|
|
24
|
-
})(["", " position:relative;display:inline-grid;align-items:center;grid-template-columns:", ";gap:var(--btn-gap);box-sizing:border-box;height:var(--btn-height);padding:0 calc(var(--btn-padding) - var(--btn-border));appearance:none !important;border-style:solid;border-width:var(--btn-border) var(--btn-border-right,var(--btn-border)) var(--btn-border) var(--btn-border-left,var(--btn-border));border-radius:var(--btn-radius-left,", ") var(--btn-radius-right,", ") var(--btn-radius-right,", ") var(--btn-radius-left,", ");text-decoration:none;cursor:", ";user-select:none;&:hover{text-decoration:none;}&:focus{outline:none;}&:active{pointer-events:", ";}", " & ", "{opacity:", ";}", ""], ({
|
|
29
|
+
componentId: "sc-qltvdl-4"
|
|
30
|
+
})(["", " position:relative;display:inline-grid;align-items:center;grid-template-columns:", ";gap:var(--btn-gap);box-sizing:border-box;height:var(--btn-height);padding:0 calc(var(--btn-padding) - var(--btn-border));appearance:none !important;border-style:solid;border-width:var(--btn-border) var(--btn-border-right,var(--btn-border)) var(--btn-border) var(--btn-border-left,var(--btn-border));border-radius:var(--btn-radius-left,", ") var(--btn-radius-right,", ") var(--btn-radius-right,", ") var(--btn-radius-left,", ");text-decoration:none;cursor:", ";user-select:none;&:hover{text-decoration:none;}&:focus{outline:none;}&:active{pointer-events:", ";}", " & ", "{margin-left:", ";}& ", ",& ", "{opacity:", ";}", ""], ({
|
|
25
31
|
$size,
|
|
26
32
|
$view
|
|
27
|
-
}) => geometryVars(geometryBySizeAndView[$size][$view]), props => [props.$hasLeftIcon && 'auto', props.$hasContent && 'minmax(0, 1fr)', props.$hasRightIcon && 'auto'].filter(Boolean).join(' '), ShapeRadius.CONTROL, ShapeRadius.CONTROL, ShapeRadius.CONTROL, ShapeRadius.CONTROL, props => props.$isDisabled || props.$isLoading ? 'not-allowed!important' : 'pointer', props => props.$isDisabled || props.$isLoading ? 'none' : 'auto', ({
|
|
33
|
+
}) => geometryVars(geometryBySizeAndView[$size][$view]), props => [props.$hasLeftIcon && 'auto', props.$hasContent && 'minmax(0, 1fr)', (props.$hasRightIcon || props.$hasChevron) && 'auto'].filter(Boolean).join(' '), ShapeRadius.CONTROL, ShapeRadius.CONTROL, ShapeRadius.CONTROL, ShapeRadius.CONTROL, props => props.$isDisabled || props.$isLoading ? 'not-allowed!important' : 'pointer', props => props.$isDisabled || props.$isLoading ? 'none' : 'auto', ({
|
|
28
34
|
$size,
|
|
29
35
|
$view
|
|
30
|
-
}) => $size === 'compact' && $view === 'ghost' && ghostCompactHitArea, /* sc-selector */
|
|
36
|
+
}) => $size === 'compact' && $view === 'ghost' && ghostCompactHitArea, /* sc-selector */ButtonChevron, ({
|
|
37
|
+
$hasContent
|
|
38
|
+
}) => $hasContent ? 'calc(var(--btn-chevron-gap) - var(--btn-label-padding) - var(--btn-gap))' : 'calc(var(--btn-chevron-gap) - var(--btn-gap))', /* sc-selector */ButtonContent, /* sc-selector */ButtonChevron, ({
|
|
31
39
|
$hideContent,
|
|
32
40
|
$isLoading
|
|
33
41
|
}) => {
|
|
@@ -42,10 +50,11 @@ export const ButtonBox = styled.button.withConfig({
|
|
|
42
50
|
}) => colorStyles[$color][$view][$size]);
|
|
43
51
|
export const RemoveButtonBox = styled.button.withConfig({
|
|
44
52
|
displayName: "RemoveButtonBox",
|
|
45
|
-
componentId: "sc-qltvdl-
|
|
53
|
+
componentId: "sc-qltvdl-5"
|
|
46
54
|
})(["width:", ";height:", ";padding:0;appearance:none !important;border:none;background:transparent;cursor:pointer;color:", ";&:hover{color:", ";}&:active{color:", ";}"], UnitSize.ICON_SMALL, UnitSize.ICON_SMALL, Color.PHANTOM, Color.DANGER_HOVER, Color.DANGER_PRESSED);
|
|
47
55
|
ButtonBox.displayName = 'ButtonBox';
|
|
48
56
|
ButtonIcon.displayName = 'ButtonIcon';
|
|
49
57
|
ButtonContent.displayName = 'ButtonContent';
|
|
58
|
+
ButtonChevron.displayName = 'ButtonChevron';
|
|
50
59
|
ButtonSpinner.displayName = 'ButtonSpinner';
|
|
51
60
|
RemoveButtonBox.displayName = 'RemoveButtonBox';
|
package/Button/types.d.ts
CHANGED
|
@@ -25,8 +25,13 @@ export type RequiredButtonProps = {
|
|
|
25
25
|
readonly isOpen?: boolean;
|
|
26
26
|
/** Icon rendered before the label. */
|
|
27
27
|
readonly icon?: ReactNode;
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Icon rendered after the label.
|
|
30
|
+
* @deprecated Use `hasChevron` for the dropdown chevron; ignored when `hasChevron` is set.
|
|
31
|
+
*/
|
|
29
32
|
readonly rightIcon?: ReactNode;
|
|
33
|
+
/** Render a chevron after the label that turns over while `isOpen`. Decoration only: it does not make the button a menu trigger. */
|
|
34
|
+
readonly hasChevron?: boolean;
|
|
30
35
|
/** Button label. */
|
|
31
36
|
readonly children?: ReactNode;
|
|
32
37
|
};
|
|
@@ -29,8 +29,13 @@ export type DropdownMenuButtonProps = {
|
|
|
29
29
|
className?: string;
|
|
30
30
|
/** Icon before the label. */
|
|
31
31
|
icon?: RequiredButtonProps['icon'];
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Icon after the label.
|
|
34
|
+
* @deprecated Use `hasChevron`; ignored when `hasChevron` is set.
|
|
35
|
+
*/
|
|
33
36
|
rightIcon?: RequiredButtonProps['rightIcon'];
|
|
37
|
+
/** Render a chevron after the label that turns over while the menu is open. Off by default. */
|
|
38
|
+
hasChevron?: RequiredButtonProps['hasChevron'];
|
|
34
39
|
/** Menu contents — nodes, or a function returning nodes. */
|
|
35
40
|
children: (() => ReactNode) | ReactNode;
|
|
36
41
|
};
|