@pushwoosh/dumb-components 4.0.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/Calendar/CalendarSuperForm/CalendarSuperForm.js +6 -4
- package/Calendar/CalendarSuperForm/types.d.ts +2 -0
- 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
|
};
|
|
@@ -27,10 +27,12 @@ export const CalendarSuperForm = props => {
|
|
|
27
27
|
gap,
|
|
28
28
|
firstDayOfWeek,
|
|
29
29
|
initialVisibleMonth,
|
|
30
|
-
footerContent
|
|
30
|
+
footerContent,
|
|
31
|
+
limitHint
|
|
31
32
|
} = props;
|
|
32
33
|
const isRangePicker = mode === 'range-date' || mode === 'range-datetime';
|
|
33
34
|
const withTime = mode === 'single-datetime' || mode === 'range-datetime';
|
|
35
|
+
const notice = limitHint ?? (maxDays && maxDays > 0 ? `Max length: ${maxDays} days` : undefined);
|
|
34
36
|
return _jsx(Vertical, {
|
|
35
37
|
"$gap": 8,
|
|
36
38
|
children: _jsx(CalendarRangePicker, {
|
|
@@ -87,17 +89,17 @@ export const CalendarSuperForm = props => {
|
|
|
87
89
|
visibleCount: visibleCount !== undefined ? visibleCount : isRangePicker ? 2 : 1,
|
|
88
90
|
monthWidth: monthWidth,
|
|
89
91
|
gap: gap,
|
|
90
|
-
renderAfter: withTime ||
|
|
92
|
+
renderAfter: withTime || notice || footerContent ? () => {
|
|
91
93
|
return _jsxs(Vertical, {
|
|
92
94
|
"$gap": 8,
|
|
93
95
|
"$margin": {
|
|
94
96
|
top: 16
|
|
95
97
|
},
|
|
96
98
|
children: [_jsxs(Vertical, {
|
|
97
|
-
children: [
|
|
99
|
+
children: [notice && _jsx(Text, {
|
|
98
100
|
"$variant": "footnote",
|
|
99
101
|
"$color": Color.LOCKED,
|
|
100
|
-
children:
|
|
102
|
+
children: notice
|
|
101
103
|
}), footerContent]
|
|
102
104
|
}), withTime && timezone && onChangeTimezone && _jsx(CalendarTimeAndTimezoneSection, {
|
|
103
105
|
isRangePicker: isRangePicker,
|
|
@@ -18,6 +18,8 @@ interface BaseProps {
|
|
|
18
18
|
initialVisibleMonth?: DateStruct;
|
|
19
19
|
/** Custom footer node rendered below the calendar and the min/max-days notice. */
|
|
20
20
|
footerContent?: ReactNode;
|
|
21
|
+
/** Replaces the default `Max length: N days` notice, e.g. `Limitation: last 12 months | Max length: 3 months`. */
|
|
22
|
+
limitHint?: ReactNode;
|
|
21
23
|
}
|
|
22
24
|
/** Selection mode of {@link CalendarSuperForm}: single vs range, date-only vs date-time. */
|
|
23
25
|
export type CalendarMode = 'single-date' | 'single-datetime' | 'range-date' | 'range-datetime';
|
|
@@ -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
|
};
|