reshaped 3.9.0-canary.3 → 3.9.0-canary.5
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/bundle.js +2 -2
- package/dist/components/Calendar/Calendar.types.d.ts +4 -2
- package/dist/components/Calendar/CalendarControlled.js +2 -2
- package/dist/components/Calendar/CalendarDate.js +9 -7
- package/dist/components/Calendar/CalendarMonth.js +2 -2
- package/dist/components/Flyout/Flyout.constants.d.ts +2 -3
- package/dist/components/Flyout/Flyout.types.d.ts +3 -6
- package/dist/components/Flyout/FlyoutContent.js +1 -2
- package/dist/components/Flyout/FlyoutControlled.js +6 -2
- package/dist/components/Flyout/useFlyout.d.ts +1 -1
- package/dist/components/Flyout/useFlyout.js +3 -7
- package/dist/components/Flyout/utilities/calculatePosition.d.ts +6 -6
- package/dist/components/Flyout/utilities/calculatePosition.js +6 -6
- package/dist/components/Flyout/utilities/flyout.js +4 -1
- package/package.json +1 -1
|
@@ -24,6 +24,8 @@ export type BaseProps = {
|
|
|
24
24
|
firstWeekDay?: number;
|
|
25
25
|
/** Dates that are selected */
|
|
26
26
|
selectedDates?: Date[];
|
|
27
|
+
/** Dates that are disabled */
|
|
28
|
+
disabledDates?: Date[];
|
|
27
29
|
/** Render a custom weekday label, can be used for localization */
|
|
28
30
|
renderWeekDay?: (args: {
|
|
29
31
|
weekDay: number;
|
|
@@ -121,7 +123,7 @@ export type MonthProps = {
|
|
|
121
123
|
hoveredDate: Date | null;
|
|
122
124
|
onDateHover: (date: Date) => void;
|
|
123
125
|
onDateHoverEnd: (date: Date) => void;
|
|
124
|
-
} & Pick<BaseProps, "max" | "min" | "firstWeekDay" | "selectedDates" | "renderMonthLabel" | "renderWeekDay" | "renderSelectedMonthLabel" | "renderDateAriaLabel" | "renderDateSlot"> & Pick<ControlledProps, "value" | "onChange" | "range">;
|
|
126
|
+
} & Pick<BaseProps, "max" | "min" | "firstWeekDay" | "selectedDates" | "disabledDates" | "renderMonthLabel" | "renderWeekDay" | "renderSelectedMonthLabel" | "renderDateAriaLabel" | "renderDateSlot"> & Pick<ControlledProps, "value" | "onChange" | "range">;
|
|
125
127
|
export type YearProps = {
|
|
126
128
|
monthDate: Date;
|
|
127
129
|
onMonthClick: (month: number) => void;
|
|
@@ -136,7 +138,7 @@ export type DateProps = {
|
|
|
136
138
|
renderAriaLabel?: MonthProps["renderDateAriaLabel"];
|
|
137
139
|
renderSlot?: MonthProps["renderDateSlot"];
|
|
138
140
|
onDateFocus: (date: Date) => void;
|
|
139
|
-
} & Pick<MonthProps, "hoveredDate" | "onDateHover" | "onDateHoverEnd" | "onChange" | "range" | "min" | "max" | "selectedDates">;
|
|
141
|
+
} & Pick<MonthProps, "hoveredDate" | "onDateHover" | "onDateHoverEnd" | "onChange" | "range" | "min" | "max" | "selectedDates" | "disabledDates">;
|
|
140
142
|
export type ControlsProps = {
|
|
141
143
|
selectionMode: SelectionMode;
|
|
142
144
|
monthsToRender: number;
|
|
@@ -9,7 +9,7 @@ import CalendarMonth from "./CalendarMonth.js";
|
|
|
9
9
|
import CalendarYear from "./CalendarYear.js";
|
|
10
10
|
import useCalendarKeyboardNavigation from "./useCalendarKeyboardNavigation.js";
|
|
11
11
|
const CalendarControlled = (props) => {
|
|
12
|
-
const { value, onChange, defaultMonth, month, onMonthChange, min, max, range, firstWeekDay, selectedDates, monthsToRender = 1, renderMonthLabel, renderSelectedMonthLabel, renderWeekDay, previousMonthAriaLabel, previousYearAriaLabel, nextMonthAriaLabel, nextYearAriaLabel, monthSelectionAriaLabel, renderMonthAriaLabel, renderDateAriaLabel, renderDateSlot, } = props;
|
|
12
|
+
const { value, onChange, defaultMonth, month, onMonthChange, min, max, range, firstWeekDay, selectedDates, disabledDates, monthsToRender = 1, renderMonthLabel, renderSelectedMonthLabel, renderWeekDay, previousMonthAriaLabel, previousYearAriaLabel, nextMonthAriaLabel, nextYearAriaLabel, monthSelectionAriaLabel, renderMonthAriaLabel, renderDateAriaLabel, renderDateSlot, } = props;
|
|
13
13
|
const [selectionMode, setSelectionMode] = React.useState("date");
|
|
14
14
|
const [monthDate, setMonthDate] = React.useState(month || defaultMonth || new Date());
|
|
15
15
|
const [hoveredDate, setHoveredDate] = React.useState(null);
|
|
@@ -97,7 +97,7 @@ const CalendarControlled = (props) => {
|
|
|
97
97
|
Array.from({ length: monthsToRender }).map((_, index) => {
|
|
98
98
|
const currentMonthDate = new Date(monthDate);
|
|
99
99
|
currentMonthDate.setMonth(currentMonthDate.getMonth() + index);
|
|
100
|
-
return (_jsx(View.Item, { grow: true, children: _jsx(CalendarMonth, { date: currentMonthDate, value: value, onChange: onChange, min: min, max: max, range: range, firstWeekDay: firstWeekDay, hoveredDate: hoveredDate, selectedDates: selectedDates, onDateHover: handleDateHover, onDateHoverEnd: handleDateHoverEnd, renderWeekDay: renderWeekDay, renderDateAriaLabel: renderDateAriaLabel, renderDateSlot: renderDateSlot }) }, index));
|
|
100
|
+
return (_jsx(View.Item, { grow: true, children: _jsx(CalendarMonth, { date: currentMonthDate, value: value, onChange: onChange, min: min, max: max, range: range, firstWeekDay: firstWeekDay, hoveredDate: hoveredDate, selectedDates: selectedDates, disabledDates: disabledDates, onDateHover: handleDateHover, onDateHoverEnd: handleDateHoverEnd, renderWeekDay: renderWeekDay, renderDateAriaLabel: renderDateAriaLabel, renderDateSlot: renderDateSlot }) }, index));
|
|
101
101
|
}), selectionMode === "month" && (_jsx(CalendarYear, { monthDate: monthDate, onMonthClick: handleMonthClick, renderMonthLabel: renderMonthLabel, renderMonthAriaLabel: renderMonthAriaLabel, min: min, max: max }))] })] }));
|
|
102
102
|
};
|
|
103
103
|
CalendarControlled.displayName = "CalendarControlled";
|
|
@@ -5,17 +5,19 @@ import { classNames } from "../../utilities/props.js";
|
|
|
5
5
|
import s from "./Calendar.module.css";
|
|
6
6
|
import { getLocalISODate } from "./Calendar.utils.js";
|
|
7
7
|
const CalendarDate = (props) => {
|
|
8
|
-
const { date, isoDate, startValue, endValue, disabled, focusable, onChange, range, hoveredDate, onDateHover, onDateHoverEnd, onDateFocus, selectedDates, renderAriaLabel, renderSlot, } = props;
|
|
8
|
+
const { date, isoDate, startValue, endValue, disabled: passedDisabled, focusable, onChange, range, hoveredDate, onDateHover, onDateHoverEnd, onDateFocus, selectedDates, disabledDates, renderAriaLabel, renderSlot, } = props;
|
|
9
9
|
if (!date)
|
|
10
10
|
return _jsx("td", { className: s.cell, "aria-hidden": "true" });
|
|
11
11
|
const isoStartValue = startValue && getLocalISODate({ date: startValue });
|
|
12
12
|
const isoEndValue = endValue && getLocalISODate({ date: endValue });
|
|
13
|
-
const isStartValue =
|
|
14
|
-
const isEndValue =
|
|
15
|
-
const isAfterStartValue =
|
|
16
|
-
const isBeforeEndValue =
|
|
17
|
-
const isInHoveredRange = hoveredDate && !endValue && hoveredDate > date;
|
|
18
|
-
const isInSelectedDates = !!selectedDates?.
|
|
13
|
+
const isStartValue = Boolean(isoDate && !!isoStartValue && isoDate === isoStartValue);
|
|
14
|
+
const isEndValue = Boolean(isoDate && !!isoEndValue && isoDate === isoEndValue);
|
|
15
|
+
const isAfterStartValue = Boolean(isoDate && isoStartValue && isoDate > isoStartValue);
|
|
16
|
+
const isBeforeEndValue = Boolean(isoDate && isoEndValue && isoDate < isoEndValue);
|
|
17
|
+
const isInHoveredRange = Boolean(hoveredDate && !endValue && hoveredDate > date);
|
|
18
|
+
const isInSelectedDates = !!selectedDates?.some((selectedDate) => getLocalISODate({ date: selectedDate }) === isoDate);
|
|
19
|
+
const disabled = passedDisabled ||
|
|
20
|
+
disabledDates?.some((disabledDate) => getLocalISODate({ date: disabledDate }) === isoDate);
|
|
19
21
|
let selection;
|
|
20
22
|
switch (true) {
|
|
21
23
|
case isAfterStartValue && isInHoveredRange:
|
|
@@ -5,7 +5,7 @@ import s from "./Calendar.module.css";
|
|
|
5
5
|
import { getMonthWeeks, getWeekdayNames, getLocalISODate, isDateFocusable } from "./Calendar.utils.js";
|
|
6
6
|
import CalendarDate from "./CalendarDate.js";
|
|
7
7
|
const CalendarMonth = (props) => {
|
|
8
|
-
const { date, value, onChange, min, max, range, firstWeekDay, selectedDates, hoveredDate, onDateHover, onDateHoverEnd, renderWeekDay, renderDateAriaLabel, renderDateSlot, } = props;
|
|
8
|
+
const { date, value, onChange, min, max, range, firstWeekDay, selectedDates, disabledDates, hoveredDate, onDateHover, onDateHoverEnd, renderWeekDay, renderDateAriaLabel, renderDateSlot, } = props;
|
|
9
9
|
let foundFocusableDate = false;
|
|
10
10
|
const [lastFocusedDate, setLastFocusedDate] = useState();
|
|
11
11
|
const month = date.getMonth();
|
|
@@ -26,7 +26,7 @@ const CalendarMonth = (props) => {
|
|
|
26
26
|
// eslint-disable-next-line react-hooks/immutability
|
|
27
27
|
if (focusable)
|
|
28
28
|
foundFocusableDate = true;
|
|
29
|
-
return (_jsx(CalendarDate, { date: date, isoDate: isoDate, disabled: disabled, range: range, focusable: focusable, startValue: startValue, endValue: endValue, onChange: onChange, hoveredDate: hoveredDate, onDateHover: onDateHover, onDateHoverEnd: onDateHoverEnd, onDateFocus: setLastFocusedDate, renderAriaLabel: renderDateAriaLabel, selectedDates: selectedDates, renderSlot: renderDateSlot }, index));
|
|
29
|
+
return (_jsx(CalendarDate, { date: date, isoDate: isoDate, disabled: disabled, range: range, focusable: focusable, startValue: startValue, endValue: endValue, onChange: onChange, hoveredDate: hoveredDate, onDateHover: onDateHover, onDateHoverEnd: onDateHoverEnd, onDateFocus: setLastFocusedDate, renderAriaLabel: renderDateAriaLabel, selectedDates: selectedDates, disabledDates: disabledDates, renderSlot: renderDateSlot }, index));
|
|
30
30
|
}) }, key));
|
|
31
31
|
}) })] }));
|
|
32
32
|
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type * as T from "./Flyout.types";
|
|
2
1
|
export declare const mouseEnter = 600;
|
|
3
|
-
export declare const defaultStyles:
|
|
4
|
-
export declare const resetStyles:
|
|
2
|
+
export declare const defaultStyles: React.CSSProperties;
|
|
3
|
+
export declare const resetStyles: React.CSSProperties;
|
|
@@ -23,17 +23,14 @@ export type Options = Pick<BaseProps, "width" | "fallbackAdjustLayout" | "fallba
|
|
|
23
23
|
lastUsedPosition: Position;
|
|
24
24
|
onPositionChoose: (position: Position) => void;
|
|
25
25
|
};
|
|
26
|
-
export type Styles = React.CSSProperties;
|
|
27
26
|
export type State = {
|
|
28
|
-
styles: Styles;
|
|
29
27
|
position?: Position;
|
|
30
28
|
status: "idle" | "rendered" | "positioned" | "visible" | "hidden";
|
|
31
29
|
};
|
|
32
30
|
export type FlyoutData = {
|
|
33
|
-
styles: Styles;
|
|
34
31
|
position: Position;
|
|
35
32
|
};
|
|
36
|
-
export type UseFlyoutData = Pick<State, "
|
|
33
|
+
export type UseFlyoutData = Pick<State, "position" | "status"> & {
|
|
37
34
|
updatePosition: (args?: {
|
|
38
35
|
sync?: boolean;
|
|
39
36
|
fallback?: boolean;
|
|
@@ -72,7 +69,7 @@ export type TriggerAttributes = {
|
|
|
72
69
|
onFocus?: () => void;
|
|
73
70
|
onMouseDown?: () => void;
|
|
74
71
|
onMouseEnter?: () => void;
|
|
75
|
-
onMouseLeave?: () => void;
|
|
72
|
+
onMouseLeave?: (e: React.MouseEvent) => void;
|
|
76
73
|
onTouchStart?: () => void;
|
|
77
74
|
onClick?: () => void;
|
|
78
75
|
"aria-describedby"?: string;
|
|
@@ -180,7 +177,7 @@ export type ContextProps = {
|
|
|
180
177
|
}) => void;
|
|
181
178
|
handleOpen: () => void;
|
|
182
179
|
handleMouseEnter: () => void;
|
|
183
|
-
handleMouseLeave: () => void;
|
|
180
|
+
handleMouseLeave: (e: React.MouseEvent) => void;
|
|
184
181
|
handleMouseDown: () => void;
|
|
185
182
|
handleTransitionEnd: (e: React.TransitionEvent) => void;
|
|
186
183
|
handleTransitionStart: (e: TransitionEvent) => void;
|
|
@@ -12,7 +12,7 @@ import cooldown from "./utilities/cooldown.js";
|
|
|
12
12
|
const FlyoutContent = (props) => {
|
|
13
13
|
const { children, className, attributes } = props;
|
|
14
14
|
const { flyout, id, flyoutElRef, triggerElRef, handleClose, handleTransitionEnd, handleTransitionStart, triggerType, handleMouseEnter, handleMouseLeave, handleContentMouseDown, handleContentMouseUp, contentClassName, contentAttributes, contentGap, contentMaxHeight, trapFocusMode, disableContentHover, autoFocus, width, containerRef: passedContainerRef, isSubmenu, } = useFlyoutContext();
|
|
15
|
-
const {
|
|
15
|
+
const { status, position } = flyout;
|
|
16
16
|
const [mounted, setMounted] = React.useState(false);
|
|
17
17
|
const closestFixedContainer = React.useMemo(() => {
|
|
18
18
|
if (!mounted)
|
|
@@ -95,7 +95,6 @@ const FlyoutContent = (props) => {
|
|
|
95
95
|
role = "menubar";
|
|
96
96
|
}
|
|
97
97
|
const content = (_jsx(ContentProvider, { value: { elRef: flyoutElRef }, children: _jsx("div", { className: rootClassNames, style: {
|
|
98
|
-
...styles,
|
|
99
98
|
"--rs-flyout-gap": contentGap,
|
|
100
99
|
"--rs-flyout-max-h": contentMaxHeight,
|
|
101
100
|
}, ref: flyoutElRef, onTransitionEnd: handleTransitionEnd, onMouseEnter: triggerType === "hover" ? handleMouseEnter : undefined, onMouseLeave: triggerType === "hover" ? handleMouseLeave : undefined, onMouseDown: handleContentMouseDown, onTouchStart: handleContentMouseDown, onMouseUp: handleContentMouseUp, onTouchEnd: handleContentMouseUp, children: _jsx("div", { role: role, ...attributes, id: id, tabIndex: !autoFocus ? -1 : undefined, "aria-modal": role === "dialog" ? true : undefined, style: { ...attributes?.style, ...contentAttributes?.style }, className: innerClassNames, children: children }) }) }));
|
|
@@ -146,11 +146,15 @@ const FlyoutControlled = (props) => {
|
|
|
146
146
|
}, groupTimeouts && cooldown.status === "warming" ? timeouts.mouseEnter : 0);
|
|
147
147
|
}
|
|
148
148
|
}, [clearTimer, handleOpen, groupTimeouts]);
|
|
149
|
-
const handleMouseLeave = React.useCallback(() => {
|
|
149
|
+
const handleMouseLeave = React.useCallback((e) => {
|
|
150
|
+
if (e.relatedTarget === flyoutElRef.current)
|
|
151
|
+
return;
|
|
152
|
+
if (e.relatedTarget === triggerElRef.current)
|
|
153
|
+
return;
|
|
150
154
|
cooldown.cool();
|
|
151
155
|
clearTimer();
|
|
152
156
|
handleClose({});
|
|
153
|
-
}, [clearTimer, handleClose]);
|
|
157
|
+
}, [clearTimer, handleClose, triggerElRef, flyoutElRef]);
|
|
154
158
|
const handleTriggerClick = React.useCallback(() => {
|
|
155
159
|
if (!isRendered) {
|
|
156
160
|
handleOpen();
|
|
@@ -7,7 +7,7 @@ type UseFlyout = (args: Pick<T.Props, "width" | "position" | "defaultActive" | "
|
|
|
7
7
|
triggerElRef: React.RefObject<HTMLElement | null>;
|
|
8
8
|
flyoutElRef: React.RefObject<HTMLElement | null>;
|
|
9
9
|
triggerBounds?: DOMRect | G.Coordinates | null;
|
|
10
|
-
}) => Pick<T.State, "
|
|
10
|
+
}) => Pick<T.State, "position" | "status"> & {
|
|
11
11
|
updatePosition: (options?: {
|
|
12
12
|
sync?: boolean;
|
|
13
13
|
}) => void;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import useRTL from "../../hooks/useRTL.js";
|
|
3
|
-
import { defaultStyles, resetStyles } from "./Flyout.constants.js";
|
|
4
3
|
import flyout from "./utilities/flyout.js";
|
|
5
4
|
const flyoutReducer = (state, action) => {
|
|
6
5
|
switch (action.type) {
|
|
@@ -8,7 +7,7 @@ const flyoutReducer = (state, action) => {
|
|
|
8
7
|
if (state.status !== "idle")
|
|
9
8
|
return state;
|
|
10
9
|
// Disable events before it's positioned to avoid mouseleave getting triggered
|
|
11
|
-
return { ...state, status: "rendered"
|
|
10
|
+
return { ...state, status: "rendered" };
|
|
12
11
|
case "position":
|
|
13
12
|
if (!action.payload.sync && state.status !== "rendered")
|
|
14
13
|
return state;
|
|
@@ -18,7 +17,6 @@ const flyoutReducer = (state, action) => {
|
|
|
18
17
|
...state,
|
|
19
18
|
status: action.payload.sync ? "visible" : "positioned",
|
|
20
19
|
position: action.payload.position,
|
|
21
|
-
styles: { ...defaultStyles, ...action.payload.styles },
|
|
22
20
|
};
|
|
23
21
|
case "show":
|
|
24
22
|
if (state.status !== "positioned")
|
|
@@ -31,7 +29,7 @@ const flyoutReducer = (state, action) => {
|
|
|
31
29
|
case "remove":
|
|
32
30
|
if (state.status !== "hidden" && state.status !== "visible")
|
|
33
31
|
return state;
|
|
34
|
-
return { ...state, status: "idle"
|
|
32
|
+
return { ...state, status: "idle" };
|
|
35
33
|
default:
|
|
36
34
|
throw new Error("[Reshaped] Invalid flyout reducer type");
|
|
37
35
|
}
|
|
@@ -47,7 +45,6 @@ const useFlyout = (args) => {
|
|
|
47
45
|
const [isRTL] = useRTL();
|
|
48
46
|
const [state, dispatch] = React.useReducer(flyoutReducer, {
|
|
49
47
|
position: defaultPosition,
|
|
50
|
-
styles: defaultStyles,
|
|
51
48
|
status: "idle",
|
|
52
49
|
});
|
|
53
50
|
const render = React.useCallback(() => {
|
|
@@ -114,13 +111,12 @@ const useFlyout = (args) => {
|
|
|
114
111
|
}, [state.status, updatePosition]);
|
|
115
112
|
return React.useMemo(() => ({
|
|
116
113
|
position: state.position,
|
|
117
|
-
styles: state.styles,
|
|
118
114
|
status: state.status,
|
|
119
115
|
updatePosition,
|
|
120
116
|
render,
|
|
121
117
|
hide,
|
|
122
118
|
remove,
|
|
123
119
|
show,
|
|
124
|
-
}), [render, updatePosition, hide, remove, show, state.position, state.
|
|
120
|
+
}), [render, updatePosition, hide, remove, show, state.position, state.status]);
|
|
125
121
|
};
|
|
126
122
|
export default useFlyout;
|
|
@@ -13,13 +13,13 @@ declare const calculatePosition: (args: {
|
|
|
13
13
|
} & Pick<T.Options, "position" | "rtl" | "width" | "contentGap" | "contentShift" | "fallbackAdjustLayout" | "fallbackMinWidth" | "fallbackMinHeight">) => {
|
|
14
14
|
position: T.Position;
|
|
15
15
|
styles: {
|
|
16
|
-
left:
|
|
17
|
-
right:
|
|
18
|
-
top:
|
|
19
|
-
bottom:
|
|
16
|
+
left: string | null;
|
|
17
|
+
right: string | null;
|
|
18
|
+
top: string | null;
|
|
19
|
+
bottom: string | null;
|
|
20
20
|
transform: string;
|
|
21
|
-
height:
|
|
22
|
-
width: string |
|
|
21
|
+
height: string | null;
|
|
22
|
+
width: string | null;
|
|
23
23
|
};
|
|
24
24
|
boundaries: {
|
|
25
25
|
left: number;
|
|
@@ -159,13 +159,13 @@ const calculatePosition = (args) => {
|
|
|
159
159
|
return {
|
|
160
160
|
position,
|
|
161
161
|
styles: {
|
|
162
|
-
left: right === null ?
|
|
163
|
-
right: right === null ?
|
|
164
|
-
top: bottom === null ?
|
|
165
|
-
bottom: bottom === null ?
|
|
162
|
+
left: right === null ? "0px" : null,
|
|
163
|
+
right: right === null ? null : "0px",
|
|
164
|
+
top: bottom === null ? "0px" : null,
|
|
165
|
+
bottom: bottom === null ? null : "0px",
|
|
166
166
|
transform: `translate(${translateX}px, ${translateY}px)`,
|
|
167
|
-
height,
|
|
168
|
-
width: width ??
|
|
167
|
+
height: height !== undefined ? `${height}px` : null,
|
|
168
|
+
width: width !== undefined ? `${width}px` : (passedWidth ?? null),
|
|
169
169
|
},
|
|
170
170
|
boundaries: {
|
|
171
171
|
left,
|
|
@@ -79,6 +79,9 @@ const flyout = (args) => {
|
|
|
79
79
|
calculated = applyPosition(lastUsedPosition);
|
|
80
80
|
onPositionChoose(calculated.position);
|
|
81
81
|
targetClone.parentNode?.removeChild(targetClone);
|
|
82
|
-
|
|
82
|
+
Object.entries(calculated.styles).forEach(([key, value]) => {
|
|
83
|
+
flyoutEl.style.setProperty(key, value);
|
|
84
|
+
});
|
|
85
|
+
return { position: calculated.position };
|
|
83
86
|
};
|
|
84
87
|
export default flyout;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reshaped",
|
|
3
3
|
"description": "Professionally crafted design system in React & Figma for building products of any scale and complexity",
|
|
4
|
-
"version": "3.9.0-canary.
|
|
4
|
+
"version": "3.9.0-canary.5",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"email": "hello@reshaped.so",
|
|
7
7
|
"homepage": "https://reshaped.so",
|