@sproutsocial/seeds-react-collapsible 1.0.0 → 1.0.2
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +15 -0
- package/dist/esm/index.js +11 -35
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +9 -7
- package/dist/index.d.ts +9 -7
- package/dist/index.js +17 -41
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/Collapsible.tsx +7 -3
- package/src/CollapsibleTypes.ts +9 -0
- package/{__tests__ → src/__tests__}/features.test.tsx +1 -1
- package/{__tests__ → src/__tests__}/types.test.tsx +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -8,14 +8,14 @@ CLI Target: es2022
|
|
|
8
8
|
CLI Cleaning output folder
|
|
9
9
|
CJS Build start
|
|
10
10
|
ESM Build start
|
|
11
|
-
ESM dist/esm/index.js
|
|
12
|
-
ESM dist/esm/index.js.map
|
|
13
|
-
ESM ⚡️ Build success in
|
|
14
|
-
CJS dist/index.js
|
|
15
|
-
CJS dist/index.js.map
|
|
16
|
-
CJS ⚡️ Build success in
|
|
11
|
+
ESM dist/esm/index.js 3.92 KB
|
|
12
|
+
ESM dist/esm/index.js.map 8.89 KB
|
|
13
|
+
ESM ⚡️ Build success in 158ms
|
|
14
|
+
CJS dist/index.js 5.92 KB
|
|
15
|
+
CJS dist/index.js.map 8.98 KB
|
|
16
|
+
CJS ⚡️ Build success in 170ms
|
|
17
17
|
DTS Build start
|
|
18
|
-
DTS ⚡️ Build success in
|
|
19
|
-
DTS dist/index.d.ts 1.
|
|
20
|
-
DTS dist/index.d.mts 1.
|
|
21
|
-
Done in
|
|
18
|
+
DTS ⚡️ Build success in 40024ms
|
|
19
|
+
DTS dist/index.d.ts 1.28 KB
|
|
20
|
+
DTS dist/index.d.mts 1.28 KB
|
|
21
|
+
Done in 48.21s.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @sproutsocial/seeds-react-collapsible
|
|
2
2
|
|
|
3
|
+
## 1.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9fd8bac: Update dependencies to use semantic package version instead of wildcards
|
|
8
|
+
- Updated dependencies [9fd8bac]
|
|
9
|
+
- @sproutsocial/seeds-react-hooks@3.0.2
|
|
10
|
+
- @sproutsocial/seeds-react-box@1.1.2
|
|
11
|
+
|
|
12
|
+
## 1.0.1
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 753d7c9: Fix various ts issues for Menu.Item, Collapsible, and VisuallyHidden
|
|
17
|
+
|
|
3
18
|
## 1.0.0
|
|
4
19
|
|
|
5
20
|
### Major Changes
|
package/dist/esm/index.js
CHANGED
|
@@ -1,29 +1,7 @@
|
|
|
1
1
|
// src/Collapsible.tsx
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import { useState
|
|
4
|
-
|
|
5
|
-
// ../seeds-react-hooks/dist/index.mjs
|
|
6
|
-
import { useState, useLayoutEffect, useCallback, useReducer, useRef, useEffect, useMemo } from "react";
|
|
7
|
-
import { useTheme } from "styled-components";
|
|
8
|
-
var v = Object.freeze({ x: 0, y: 0, width: 0, height: 0, top: 0, right: 0, bottom: 0, left: 0 });
|
|
9
|
-
function q(r) {
|
|
10
|
-
let [t, e] = useState(v);
|
|
11
|
-
return useLayoutEffect(() => {
|
|
12
|
-
if (!r.current || !("ResizeObserver" in window))
|
|
13
|
-
return;
|
|
14
|
-
let n = new ResizeObserver(([o]) => {
|
|
15
|
-
if (!o)
|
|
16
|
-
return;
|
|
17
|
-
let { x: u, y: a, width: i, height: f, top: m, right: b, bottom: d, left: y } = o.contentRect;
|
|
18
|
-
e({ x: u, y: a, width: i, height: f, top: m, right: b, bottom: d, left: y });
|
|
19
|
-
});
|
|
20
|
-
return n.observe(r.current), () => {
|
|
21
|
-
n.disconnect();
|
|
22
|
-
};
|
|
23
|
-
}, [r]), t;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// src/Collapsible.tsx
|
|
3
|
+
import { useState, useRef, useContext, useEffect } from "react";
|
|
4
|
+
import { useMeasure } from "@sproutsocial/seeds-react-hooks";
|
|
27
5
|
import Box2 from "@sproutsocial/seeds-react-box";
|
|
28
6
|
|
|
29
7
|
// src/styles.ts
|
|
@@ -69,7 +47,7 @@ var Collapsible = ({
|
|
|
69
47
|
collapsedHeight = 0,
|
|
70
48
|
openHeight
|
|
71
49
|
}) => {
|
|
72
|
-
const [id] =
|
|
50
|
+
const [id] = useState(`Racine-collapsible-${idCounter++}`);
|
|
73
51
|
return /* @__PURE__ */ jsx(
|
|
74
52
|
CollapsibleContext.Provider,
|
|
75
53
|
{
|
|
@@ -85,10 +63,8 @@ var Collapsible = ({
|
|
|
85
63
|
);
|
|
86
64
|
};
|
|
87
65
|
var determineMaxHeight = (isHidden, openHeight, computedHeight) => {
|
|
88
|
-
if (isHidden === void 0)
|
|
89
|
-
|
|
90
|
-
if (openHeight)
|
|
91
|
-
return openHeight;
|
|
66
|
+
if (isHidden === void 0) return void 0;
|
|
67
|
+
if (openHeight) return openHeight;
|
|
92
68
|
return computedHeight;
|
|
93
69
|
};
|
|
94
70
|
var Trigger = ({ children, ...rest }) => {
|
|
@@ -108,16 +84,16 @@ var Panel = ({ children, ...rest }) => {
|
|
|
108
84
|
collapsedHeight,
|
|
109
85
|
openHeight
|
|
110
86
|
} = useContext(CollapsibleContext);
|
|
111
|
-
const ref =
|
|
112
|
-
const measurement =
|
|
113
|
-
const [isHidden, setIsHidden] =
|
|
87
|
+
const ref = useRef(null);
|
|
88
|
+
const measurement = useMeasure(ref);
|
|
89
|
+
const [isHidden, setIsHidden] = useState(void 0);
|
|
114
90
|
const maxHeight = determineMaxHeight(
|
|
115
91
|
isHidden,
|
|
116
92
|
openHeight,
|
|
117
93
|
// Round up to the nearest pixel to prevent subpixel rendering issues
|
|
118
94
|
Math.ceil(measurement.height + offset)
|
|
119
95
|
);
|
|
120
|
-
|
|
96
|
+
useEffect(() => {
|
|
121
97
|
if (!isOpen) {
|
|
122
98
|
const timeoutID = setTimeout(() => setIsHidden(!isOpen), 300);
|
|
123
99
|
return () => clearTimeout(timeoutID);
|
|
@@ -159,9 +135,9 @@ var Collapsible_default = Collapsible;
|
|
|
159
135
|
import "react";
|
|
160
136
|
|
|
161
137
|
// src/index.ts
|
|
162
|
-
var
|
|
138
|
+
var index_default = Collapsible_default;
|
|
163
139
|
export {
|
|
164
140
|
Collapsible_default as Collapsible,
|
|
165
|
-
|
|
141
|
+
index_default as default
|
|
166
142
|
};
|
|
167
143
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/Collapsible.tsx","../../../seeds-react-hooks/src/useMeasure/useMeasure.ts","../../../seeds-react-hooks/src/useSelect/useSelect.ts","../../../seeds-react-hooks/src/useMultiselect/useMultiselect.ts","../../../seeds-react-hooks/src/useMutationObserver/useMutationObserver.ts","../../../seeds-react-hooks/src/useTextContent/useTextContent.ts","../../../seeds-react-hooks/src/useWhyDidYouUpdate/useWhyDidYouUpdate.ts","../../../seeds-react-hooks/src/useInteractiveColor/useInteractiveColor.ts","../../src/styles.ts","../../src/CollapsibleTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport { useState, useRef, useContext, useEffect } from \"react\";\nimport { useMeasure } from \"@sproutsocial/seeds-react-hooks\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport { CollapsingBox } from \"./styles\";\nimport type { TypeCollapsibleProps } from \"./CollapsibleTypes\";\n\nlet idCounter = 0;\n\ninterface TypeCollapsibleContext {\n isOpen?: boolean;\n id?: string;\n offset?: number;\n openHeight?: number;\n collapsedHeight?: number;\n}\n\nconst CollapsibleContext = React.createContext<TypeCollapsibleContext>({});\n\nconst Collapsible = ({\n children,\n isOpen = false,\n offset = 0,\n collapsedHeight = 0,\n openHeight,\n}: TypeCollapsibleProps) => {\n const [id] = useState(`Racine-collapsible-${idCounter++}`);\n return (\n <CollapsibleContext.Provider\n value={{\n isOpen,\n id,\n offset,\n collapsedHeight,\n openHeight,\n }}\n >\n {children}\n </CollapsibleContext.Provider>\n );\n};\n\nconst determineMaxHeight = (\n isHidden?: boolean,\n openHeight?: number,\n computedHeight?: number\n): number | undefined => {\n // If isHidden is undefined this is the first render. Return undefined so the max-height prop is not added\n // This is a hack to prevent css from animating if it begins in the open state\n // css animates when attribute values change (IE from 0 to another number)\n // css does not animate when simply adding an attribute to an HTML element\n if (isHidden === undefined) return undefined;\n // If the user has defined an explicit open height, return that as the max height\n if (openHeight) return openHeight;\n // Otherwise, fallback to the computed height\n return computedHeight;\n};\n\nconst Trigger = ({ children, ...rest }: { children: React.ReactElement }) => {\n const { isOpen, id } = useContext(CollapsibleContext);\n return (\n <React.Fragment>\n {React.cloneElement(children, {\n \"aria-controls\": id,\n \"aria-expanded\": !!isOpen,\n ...rest,\n })}\n </React.Fragment>\n );\n};\n\nTrigger.displayName = \"Collapsible.Trigger\";\n\nconst Panel = ({ children, ...rest }: { children: React.ReactNode }) => {\n const {\n isOpen,\n id,\n offset = 0,\n collapsedHeight,\n openHeight,\n } = useContext(CollapsibleContext);\n\n const ref = useRef<HTMLDivElement | null>(null);\n const measurement = useMeasure(ref);\n const [isHidden, setIsHidden] = useState<boolean | undefined>(undefined);\n const maxHeight = determineMaxHeight(\n isHidden,\n openHeight,\n // Round up to the nearest pixel to prevent subpixel rendering issues\n Math.ceil(measurement.height + offset)\n );\n\n /* We use the \"hidden\" attribute to remove the contents of the panel from the tab order of the page, but it interferes with the animation. This logic sets a slight timeout on setting the prop so that the animation has time to complete before the attribute is set. */\n useEffect(() => {\n if (!isOpen) {\n const timeoutID = setTimeout(() => setIsHidden(!isOpen), 300);\n return () => clearTimeout(timeoutID);\n } else {\n // Similar to the close animation, we need to delay setting hidden to run slightly async.\n // An issue occurs with the initial render isHidden logic that causes the animation to occur sporadically.\n // using this 0 second timeout just allows this component to initially render with an undefined max height,\n // Then go directly from undefined to the full max height, without a brief 0 value that triggers an animation\n const timeoutID = setTimeout(() => setIsHidden(!isOpen), 0);\n return () => clearTimeout(timeoutID);\n }\n }, [isOpen]);\n\n return (\n <CollapsingBox\n hasShadow={Boolean(collapsedHeight || (openHeight && openHeight > 0))}\n scrollable={isOpen}\n maxHeight={isOpen ? maxHeight : collapsedHeight}\n minHeight={collapsedHeight}\n data-qa-collapsible=\"\"\n data-qa-collapsible-isopen={isOpen === true}\n {...rest}\n >\n <Box\n width=\"100%\"\n hidden={isHidden && collapsedHeight === 0}\n aria-hidden={!isOpen}\n id={id}\n ref={ref}\n >\n {children}\n </Box>\n </CollapsingBox>\n );\n};\n\nPanel.displayName = \"Collapsible.Panel\";\n\nCollapsible.Trigger = Trigger;\nCollapsible.Panel = Panel;\n\nexport default Collapsible;\n","import { useState, useLayoutEffect, type RefObject } from \"react\";\n\ninterface DOMRectObject {\n x: number;\n y: number;\n width: number;\n height: number;\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\nconst initialBounds = Object.freeze({\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n});\n\nexport function useMeasure<TElement extends Element>(ref: RefObject<TElement>) {\n const [bounds, setContentRect] =\n useState<Readonly<DOMRectObject>>(initialBounds);\n\n useLayoutEffect(() => {\n const element = ref.current;\n\n if (\n !element ||\n // in non-browser environments (e.g. Jest tests) ResizeObserver is not defined\n !(\"ResizeObserver\" in window)\n ) {\n return;\n }\n\n const resizeObserver = new ResizeObserver(([entry]) => {\n if (!entry) return;\n const { x, y, width, height, top, right, bottom, left } =\n entry.contentRect;\n setContentRect({\n x,\n y,\n width,\n height,\n top,\n right,\n bottom,\n left,\n });\n });\n resizeObserver.observe(ref.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [ref]);\n\n return bounds;\n}\n","import { useState, useCallback } from \"react\";\n\ntype TypeSingleSelectProps<T extends string> = {\n initialValue?: T | \"\";\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChange?: (value: string | T) => any;\n};\n\nexport const useSelect = <T extends string>(\n {\n initialValue = \"\",\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: userOnChange = () => {},\n }: TypeSingleSelectProps<T> = {\n initialValue: \"\",\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: () => {},\n }\n) => {\n const [value, setValue] = useState<string | T>(initialValue);\n\n const onChange = useCallback(\n (newValue: string) => {\n if (newValue !== value) {\n setValue(newValue);\n userOnChange(newValue);\n }\n },\n [userOnChange, value]\n );\n\n return { value, onChange };\n};\n","import { useCallback, useEffect, useReducer, useRef } from \"react\";\n\ntype TypeMultiSelectProps<T extends string> = {\n initialValue?: T[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChange?: (value: Array<string | T>) => any;\n};\n\nconst valueReducer = (\n state: Set<string>,\n action: { type: string; value?: string }\n): Set<string> => {\n const newState = new Set(state);\n switch (action.type) {\n case \"reset\": {\n return new Set();\n }\n case \"toggle_item\":\n default: {\n if (action.value) {\n if (newState.has(action.value)) {\n newState.delete(action.value);\n } else {\n newState.add(action.value);\n }\n }\n return newState;\n }\n }\n};\n\nexport const useMultiselect = <T extends string>(\n {\n initialValue = [],\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: userOnChange = () => {},\n }: TypeMultiSelectProps<T> = {\n initialValue: [],\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: () => {},\n }\n) => {\n const [value, dispatch] = useReducer(valueReducer, new Set(initialValue));\n\n const getArrayValue = (value: Set<string | T>) =>\n Array.from<string | T>(value);\n\n const onChange = useCallback(\n (newValue: string) => {\n dispatch({ type: \"toggle_item\", value: newValue });\n },\n [dispatch]\n );\n\n const isFirstRun = useRef(true);\n\n useEffect(() => {\n if (isFirstRun.current) {\n isFirstRun.current = false;\n return;\n }\n userOnChange(getArrayValue(value));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [userOnChange, value]);\n\n const onClear = useCallback(() => {\n dispatch({ type: \"reset\" });\n }, [dispatch]);\n\n return { value: getArrayValue(value), onChange, onClear };\n};\n","import { canUseDOM } from \"@sproutsocial/seeds-react-utilities\";\nimport { useEffect, useMemo, useState } from \"react\";\n\ntype TypeMutationObserverInitRequired =\n | {\n childList: true;\n }\n | {\n attributes: true;\n }\n | {\n characterData: true;\n };\n\ntype TypeMutationObserverInit = {\n subtree?: boolean;\n attributeOldValue?: boolean;\n characterDataOldValue?: boolean;\n attributeFilter?: Array<string>;\n} & TypeMutationObserverInitRequired;\n\ntype TypeMutationObserverCallback = (\n mutationList?: MutationRecord[],\n observer?: MutationObserver\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n) => any;\n\nconst defaultCallback: TypeMutationObserverCallback = (mutationList) =>\n mutationList;\n\nexport function useMutationObserver(\n targetNode: Node | null,\n config: TypeMutationObserverInit,\n callback: TypeMutationObserverCallback = defaultCallback\n) {\n if (!canUseDOM()) {\n return;\n }\n /* eslint-disable-next-line */\n const [value, setValue] = useState(undefined);\n /* eslint-disable-next-line */\n const observer = useMemo(\n () =>\n new MutationObserver((mutationList, observer) => {\n const result = callback(mutationList, observer);\n setValue(result);\n }),\n [callback]\n );\n /* eslint-disable-next-line */\n useEffect(() => {\n if (targetNode) {\n observer.observe(targetNode, config);\n return () => {\n observer.disconnect();\n };\n }\n }, [targetNode, config, observer]);\n\n return value;\n}\n\nexport function useMutationObserverOnce(\n targetNode: Node | null,\n config: TypeMutationObserverInit,\n callback: TypeMutationObserverCallback\n) {\n const [isObserving, setObserving] = useState(true);\n const node = isObserving ? targetNode : null;\n const value = useMutationObserver(node, config, callback);\n if (value !== undefined && isObserving) {\n setObserving(false);\n }\n return value;\n}\n","import { useCallback, useState } from \"react\";\n\nexport type textContentRef = ((node: Node) => void) & { current?: string };\nexport function useTextContent(initial: string) {\n const [textContent, setTextContent] = useState(initial);\n\n const ref: textContentRef = useCallback((node: Node) => {\n if (node && node.textContent !== null) {\n setTextContent(node.textContent);\n }\n }, []);\n\n ref.current = textContent;\n return ref;\n}\n","import { useRef, useEffect } from \"react\";\n\nexport function useWhyDidYouUpdate(\n name: string,\n props: { [key: string]: any }\n) {\n // Get a mutable ref object where we can store props ...\n // ... for comparison next time this hook runs.\n const previousProps = useRef<typeof props>({});\n\n useEffect(() => {\n if (previousProps.current) {\n // Get all keys from previous and current props\n const allKeys = Object.keys({ ...previousProps.current, ...props });\n // Use this object to keep track of changed props\n const changesObj: typeof props = {};\n // Iterate through keys\n allKeys.forEach((key) => {\n // If previous is different from current\n\n if (previousProps.current[key] !== props[key]) {\n // Add to changesObj\n\n changesObj[key] = {\n from: previousProps.current[key],\n\n to: props[key],\n };\n }\n });\n\n // If changesObj not empty then output to console\n if (Object.keys(changesObj).length) {\n // eslint-disable-next-line no-console\n console.log(\"[why-did-you-update]\", name, changesObj);\n }\n }\n\n // Finally update previousProps with current props for next hook call\n previousProps.current = props;\n });\n}\n","import { darken, lighten } from \"polished\";\nimport { useTheme } from \"styled-components\";\nimport type { TypeTheme } from \"@sproutsocial/seeds-react-theme\";\n\n/**\n * The useInteractiveColor hook has context of theme mode (light or dark)\n * and can be used to lighten or darken a color dynamically\n *\n * note: colors are limited to our theme colors\n */\nconst useInteractiveColor = (themeColor: string): string => {\n // Throw error if used outside of a ThemeProvider (styled-components)\n if (!useTheme()) {\n throw new Error(\n \"useInteractiveColor() must be used within a Styled Components ThemeProvider\"\n );\n }\n\n // Get the current theme mode ie. 'light' or 'dark'\n const theme: TypeTheme = useTheme() as TypeTheme;\n const themeMode = theme.mode;\n\n // If the theme mode is dark, return a lightened version of the themeValue\n if (themeMode === \"dark\") {\n return lighten(0.2, themeColor);\n } else {\n // If the theme mode is light, return a darkened version of the themeValue\n return darken(0.2, themeColor);\n }\n};\n\nexport { useInteractiveColor };\n","import styled from \"styled-components\";\nimport Box from \"@sproutsocial/seeds-react-box\";\n\nexport const CollapsingBox = styled(Box)<{\n hasShadow?: boolean;\n scrollable?: boolean;\n}>`\n transition: max-height ${(p) => p.theme.duration.medium}\n ${(p) => p.theme.easing.ease_inout};\n will-change: max-height;\n position: relative;\n overflow: auto;\n ${({ hasShadow, scrollable }) =>\n hasShadow\n ? `background: /* Shadow covers */ linear-gradient(\n transparent 30%,\n rgba(255, 255, 255, 0)\n ),\n linear-gradient(rgba(255, 255, 255, 0), transparent 70%) 0 100%,\n /* Shadows */\n radial-gradient(\n farthest-side at 50% 0,\n rgb(39 51 51 / 5%),\n rgba(0, 0, 0, 0)\n ),\n radial-gradient(\n farthest-side at 50% 100%,\n rgb(39 51 51 / 5%),\n rgba(0, 0, 0, 0)\n )\n 0 100%;\n background-repeat: no-repeat;\n background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;\n background-attachment: local, local, scroll, scroll;\n ${scrollable ? `overflow: auto` : `overflow: hidden`};`\n : \"\"}\n`;\n","import * as React from \"react\";\n\n// The flow type is inexact but the underlying component does not accept any other props.\n// It might be worth extending the box props here for the refactor, but allowing it would provide no functionality right now.\nexport interface TypeCollapsibleProps {\n isOpen: boolean;\n children: React.ReactNode;\n\n /** If the children of the collapsible panel have a top or bottom margin, it will throw off the calculations for the height of the content. The total amount of vertical margin (in pixels) can be supplied to this prop to correct this. */\n offset?: number;\n collapsedHeight?: number;\n openHeight?: number;\n}\n","import Collapsible from \"./Collapsible\";\n\nexport default Collapsible;\nexport { Collapsible };\nexport * from \"./CollapsibleTypes\";\n"],"mappings":"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","names":["useState","useRef","useEffect","useState","Box","useState","useRef","useEffect","Box"]}
|
|
1
|
+
{"version":3,"sources":["../../src/Collapsible.tsx","../../src/styles.ts","../../src/CollapsibleTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport { useState, useRef, useContext, useEffect } from \"react\";\nimport { useMeasure } from \"@sproutsocial/seeds-react-hooks\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport { CollapsingBox } from \"./styles\";\nimport type {\n TypeCollapsibleProps,\n TypeCollapsiblePanelProps,\n TypeCollapsibleTriggerProps,\n} from \"./CollapsibleTypes\";\n\nlet idCounter = 0;\n\ninterface TypeCollapsibleContext {\n isOpen?: boolean;\n id?: string;\n offset?: number;\n openHeight?: number;\n collapsedHeight?: number;\n}\n\nconst CollapsibleContext = React.createContext<TypeCollapsibleContext>({});\n\nconst Collapsible = ({\n children,\n isOpen = false,\n offset = 0,\n collapsedHeight = 0,\n openHeight,\n}: TypeCollapsibleProps) => {\n const [id] = useState(`Racine-collapsible-${idCounter++}`);\n return (\n <CollapsibleContext.Provider\n value={{\n isOpen,\n id,\n offset,\n collapsedHeight,\n openHeight,\n }}\n >\n {children}\n </CollapsibleContext.Provider>\n );\n};\n\nconst determineMaxHeight = (\n isHidden?: boolean,\n openHeight?: number,\n computedHeight?: number\n): number | undefined => {\n // If isHidden is undefined this is the first render. Return undefined so the max-height prop is not added\n // This is a hack to prevent css from animating if it begins in the open state\n // css animates when attribute values change (IE from 0 to another number)\n // css does not animate when simply adding an attribute to an HTML element\n if (isHidden === undefined) return undefined;\n // If the user has defined an explicit open height, return that as the max height\n if (openHeight) return openHeight;\n // Otherwise, fallback to the computed height\n return computedHeight;\n};\n\nconst Trigger = ({ children, ...rest }: TypeCollapsibleTriggerProps) => {\n const { isOpen, id } = useContext(CollapsibleContext);\n return (\n <React.Fragment>\n {React.cloneElement(children, {\n \"aria-controls\": id,\n \"aria-expanded\": !!isOpen,\n ...rest,\n })}\n </React.Fragment>\n );\n};\n\nTrigger.displayName = \"Collapsible.Trigger\";\n\nconst Panel = ({ children, ...rest }: TypeCollapsiblePanelProps) => {\n const {\n isOpen,\n id,\n offset = 0,\n collapsedHeight,\n openHeight,\n } = useContext(CollapsibleContext);\n\n const ref = useRef<HTMLDivElement | null>(null);\n const measurement = useMeasure(ref);\n const [isHidden, setIsHidden] = useState<boolean | undefined>(undefined);\n const maxHeight = determineMaxHeight(\n isHidden,\n openHeight,\n // Round up to the nearest pixel to prevent subpixel rendering issues\n Math.ceil(measurement.height + offset)\n );\n\n /* We use the \"hidden\" attribute to remove the contents of the panel from the tab order of the page, but it interferes with the animation. This logic sets a slight timeout on setting the prop so that the animation has time to complete before the attribute is set. */\n useEffect(() => {\n if (!isOpen) {\n const timeoutID = setTimeout(() => setIsHidden(!isOpen), 300);\n return () => clearTimeout(timeoutID);\n } else {\n // Similar to the close animation, we need to delay setting hidden to run slightly async.\n // An issue occurs with the initial render isHidden logic that causes the animation to occur sporadically.\n // using this 0 second timeout just allows this component to initially render with an undefined max height,\n // Then go directly from undefined to the full max height, without a brief 0 value that triggers an animation\n const timeoutID = setTimeout(() => setIsHidden(!isOpen), 0);\n return () => clearTimeout(timeoutID);\n }\n }, [isOpen]);\n\n return (\n <CollapsingBox\n hasShadow={Boolean(collapsedHeight || (openHeight && openHeight > 0))}\n scrollable={isOpen}\n maxHeight={isOpen ? maxHeight : collapsedHeight}\n minHeight={collapsedHeight}\n data-qa-collapsible=\"\"\n data-qa-collapsible-isopen={isOpen === true}\n {...rest}\n >\n <Box\n width=\"100%\"\n hidden={isHidden && collapsedHeight === 0}\n aria-hidden={!isOpen}\n id={id}\n ref={ref}\n >\n {children}\n </Box>\n </CollapsingBox>\n );\n};\n\nPanel.displayName = \"Collapsible.Panel\";\n\nCollapsible.Trigger = Trigger;\nCollapsible.Panel = Panel;\n\nexport default Collapsible;\n","import styled from \"styled-components\";\nimport Box from \"@sproutsocial/seeds-react-box\";\n\nexport const CollapsingBox = styled(Box)<{\n hasShadow?: boolean;\n scrollable?: boolean;\n}>`\n transition: max-height ${(p) => p.theme.duration.medium}\n ${(p) => p.theme.easing.ease_inout};\n will-change: max-height;\n position: relative;\n overflow: auto;\n ${({ hasShadow, scrollable }) =>\n hasShadow\n ? `background: /* Shadow covers */ linear-gradient(\n transparent 30%,\n rgba(255, 255, 255, 0)\n ),\n linear-gradient(rgba(255, 255, 255, 0), transparent 70%) 0 100%,\n /* Shadows */\n radial-gradient(\n farthest-side at 50% 0,\n rgb(39 51 51 / 5%),\n rgba(0, 0, 0, 0)\n ),\n radial-gradient(\n farthest-side at 50% 100%,\n rgb(39 51 51 / 5%),\n rgba(0, 0, 0, 0)\n )\n 0 100%;\n background-repeat: no-repeat;\n background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;\n background-attachment: local, local, scroll, scroll;\n ${scrollable ? `overflow: auto` : `overflow: hidden`};`\n : \"\"}\n`;\n","import * as React from \"react\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\n\n// The flow type is inexact but the underlying component does not accept any other props.\n// It might be worth extending the box props here for the refactor, but allowing it would provide no functionality right now.\nexport interface TypeCollapsibleProps {\n isOpen: boolean;\n children: React.ReactNode;\n\n /** If the children of the collapsible panel have a top or bottom margin, it will throw off the calculations for the height of the content. The total amount of vertical margin (in pixels) can be supplied to this prop to correct this. */\n offset?: number;\n collapsedHeight?: number;\n openHeight?: number;\n}\n\nexport interface TypeCollapsibleTriggerProps\n extends Omit<TypeBoxProps, \"children\"> {\n // Children is required for the Trigger\n children: React.ReactElement;\n}\n\nexport interface TypeCollapsiblePanelProps extends TypeBoxProps {}\n","import Collapsible from \"./Collapsible\";\n\nexport default Collapsible;\nexport { Collapsible };\nexport * from \"./CollapsibleTypes\";\n"],"mappings":"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","names":["Box","Box"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
+
import { TypeBoxProps } from '@sproutsocial/seeds-react-box';
|
|
3
4
|
|
|
4
5
|
interface TypeCollapsibleProps {
|
|
5
6
|
isOpen: boolean;
|
|
@@ -9,21 +10,22 @@ interface TypeCollapsibleProps {
|
|
|
9
10
|
collapsedHeight?: number;
|
|
10
11
|
openHeight?: number;
|
|
11
12
|
}
|
|
13
|
+
interface TypeCollapsibleTriggerProps extends Omit<TypeBoxProps, "children"> {
|
|
14
|
+
children: React.ReactElement;
|
|
15
|
+
}
|
|
16
|
+
interface TypeCollapsiblePanelProps extends TypeBoxProps {
|
|
17
|
+
}
|
|
12
18
|
|
|
13
19
|
declare const Collapsible: {
|
|
14
20
|
({ children, isOpen, offset, collapsedHeight, openHeight, }: TypeCollapsibleProps): react_jsx_runtime.JSX.Element;
|
|
15
21
|
Trigger: {
|
|
16
|
-
({ children, ...rest }:
|
|
17
|
-
children: React.ReactElement;
|
|
18
|
-
}): react_jsx_runtime.JSX.Element;
|
|
22
|
+
({ children, ...rest }: TypeCollapsibleTriggerProps): react_jsx_runtime.JSX.Element;
|
|
19
23
|
displayName: string;
|
|
20
24
|
};
|
|
21
25
|
Panel: {
|
|
22
|
-
({ children, ...rest }:
|
|
23
|
-
children: React.ReactNode;
|
|
24
|
-
}): react_jsx_runtime.JSX.Element;
|
|
26
|
+
({ children, ...rest }: TypeCollapsiblePanelProps): react_jsx_runtime.JSX.Element;
|
|
25
27
|
displayName: string;
|
|
26
28
|
};
|
|
27
29
|
};
|
|
28
30
|
|
|
29
|
-
export { Collapsible, type TypeCollapsibleProps, Collapsible as default };
|
|
31
|
+
export { Collapsible, type TypeCollapsiblePanelProps, type TypeCollapsibleProps, type TypeCollapsibleTriggerProps, Collapsible as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
+
import { TypeBoxProps } from '@sproutsocial/seeds-react-box';
|
|
3
4
|
|
|
4
5
|
interface TypeCollapsibleProps {
|
|
5
6
|
isOpen: boolean;
|
|
@@ -9,21 +10,22 @@ interface TypeCollapsibleProps {
|
|
|
9
10
|
collapsedHeight?: number;
|
|
10
11
|
openHeight?: number;
|
|
11
12
|
}
|
|
13
|
+
interface TypeCollapsibleTriggerProps extends Omit<TypeBoxProps, "children"> {
|
|
14
|
+
children: React.ReactElement;
|
|
15
|
+
}
|
|
16
|
+
interface TypeCollapsiblePanelProps extends TypeBoxProps {
|
|
17
|
+
}
|
|
12
18
|
|
|
13
19
|
declare const Collapsible: {
|
|
14
20
|
({ children, isOpen, offset, collapsedHeight, openHeight, }: TypeCollapsibleProps): react_jsx_runtime.JSX.Element;
|
|
15
21
|
Trigger: {
|
|
16
|
-
({ children, ...rest }:
|
|
17
|
-
children: React.ReactElement;
|
|
18
|
-
}): react_jsx_runtime.JSX.Element;
|
|
22
|
+
({ children, ...rest }: TypeCollapsibleTriggerProps): react_jsx_runtime.JSX.Element;
|
|
19
23
|
displayName: string;
|
|
20
24
|
};
|
|
21
25
|
Panel: {
|
|
22
|
-
({ children, ...rest }:
|
|
23
|
-
children: React.ReactNode;
|
|
24
|
-
}): react_jsx_runtime.JSX.Element;
|
|
26
|
+
({ children, ...rest }: TypeCollapsiblePanelProps): react_jsx_runtime.JSX.Element;
|
|
25
27
|
displayName: string;
|
|
26
28
|
};
|
|
27
29
|
};
|
|
28
30
|
|
|
29
|
-
export { Collapsible, type TypeCollapsibleProps, Collapsible as default };
|
|
31
|
+
export { Collapsible, type TypeCollapsiblePanelProps, type TypeCollapsibleProps, type TypeCollapsibleTriggerProps, Collapsible as default };
|
package/dist/index.js
CHANGED
|
@@ -28,45 +28,23 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
30
|
// src/index.ts
|
|
31
|
-
var
|
|
32
|
-
__export(
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
33
|
Collapsible: () => Collapsible_default,
|
|
34
|
-
default: () =>
|
|
34
|
+
default: () => index_default
|
|
35
35
|
});
|
|
36
|
-
module.exports = __toCommonJS(
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
|
|
38
38
|
// src/Collapsible.tsx
|
|
39
39
|
var React = __toESM(require("react"));
|
|
40
|
-
var import_react2 = require("react");
|
|
41
|
-
|
|
42
|
-
// ../seeds-react-hooks/dist/index.mjs
|
|
43
40
|
var import_react = require("react");
|
|
44
|
-
var
|
|
45
|
-
var v = Object.freeze({ x: 0, y: 0, width: 0, height: 0, top: 0, right: 0, bottom: 0, left: 0 });
|
|
46
|
-
function q(r) {
|
|
47
|
-
let [t, e] = (0, import_react.useState)(v);
|
|
48
|
-
return (0, import_react.useLayoutEffect)(() => {
|
|
49
|
-
if (!r.current || !("ResizeObserver" in window))
|
|
50
|
-
return;
|
|
51
|
-
let n = new ResizeObserver(([o]) => {
|
|
52
|
-
if (!o)
|
|
53
|
-
return;
|
|
54
|
-
let { x: u, y: a, width: i, height: f, top: m, right: b, bottom: d, left: y } = o.contentRect;
|
|
55
|
-
e({ x: u, y: a, width: i, height: f, top: m, right: b, bottom: d, left: y });
|
|
56
|
-
});
|
|
57
|
-
return n.observe(r.current), () => {
|
|
58
|
-
n.disconnect();
|
|
59
|
-
};
|
|
60
|
-
}, [r]), t;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// src/Collapsible.tsx
|
|
41
|
+
var import_seeds_react_hooks = require("@sproutsocial/seeds-react-hooks");
|
|
64
42
|
var import_seeds_react_box2 = __toESM(require("@sproutsocial/seeds-react-box"));
|
|
65
43
|
|
|
66
44
|
// src/styles.ts
|
|
67
|
-
var
|
|
45
|
+
var import_styled_components = __toESM(require("styled-components"));
|
|
68
46
|
var import_seeds_react_box = __toESM(require("@sproutsocial/seeds-react-box"));
|
|
69
|
-
var CollapsingBox = (0,
|
|
47
|
+
var CollapsingBox = (0, import_styled_components.default)(import_seeds_react_box.default)`
|
|
70
48
|
transition: max-height ${(p) => p.theme.duration.medium}
|
|
71
49
|
${(p) => p.theme.easing.ease_inout};
|
|
72
50
|
will-change: max-height;
|
|
@@ -106,7 +84,7 @@ var Collapsible = ({
|
|
|
106
84
|
collapsedHeight = 0,
|
|
107
85
|
openHeight
|
|
108
86
|
}) => {
|
|
109
|
-
const [id] = (0,
|
|
87
|
+
const [id] = (0, import_react.useState)(`Racine-collapsible-${idCounter++}`);
|
|
110
88
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
111
89
|
CollapsibleContext.Provider,
|
|
112
90
|
{
|
|
@@ -122,14 +100,12 @@ var Collapsible = ({
|
|
|
122
100
|
);
|
|
123
101
|
};
|
|
124
102
|
var determineMaxHeight = (isHidden, openHeight, computedHeight) => {
|
|
125
|
-
if (isHidden === void 0)
|
|
126
|
-
|
|
127
|
-
if (openHeight)
|
|
128
|
-
return openHeight;
|
|
103
|
+
if (isHidden === void 0) return void 0;
|
|
104
|
+
if (openHeight) return openHeight;
|
|
129
105
|
return computedHeight;
|
|
130
106
|
};
|
|
131
107
|
var Trigger = ({ children, ...rest }) => {
|
|
132
|
-
const { isOpen, id } = (0,
|
|
108
|
+
const { isOpen, id } = (0, import_react.useContext)(CollapsibleContext);
|
|
133
109
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(React.Fragment, { children: React.cloneElement(children, {
|
|
134
110
|
"aria-controls": id,
|
|
135
111
|
"aria-expanded": !!isOpen,
|
|
@@ -144,17 +120,17 @@ var Panel = ({ children, ...rest }) => {
|
|
|
144
120
|
offset = 0,
|
|
145
121
|
collapsedHeight,
|
|
146
122
|
openHeight
|
|
147
|
-
} = (0,
|
|
148
|
-
const ref = (0,
|
|
149
|
-
const measurement =
|
|
150
|
-
const [isHidden, setIsHidden] = (0,
|
|
123
|
+
} = (0, import_react.useContext)(CollapsibleContext);
|
|
124
|
+
const ref = (0, import_react.useRef)(null);
|
|
125
|
+
const measurement = (0, import_seeds_react_hooks.useMeasure)(ref);
|
|
126
|
+
const [isHidden, setIsHidden] = (0, import_react.useState)(void 0);
|
|
151
127
|
const maxHeight = determineMaxHeight(
|
|
152
128
|
isHidden,
|
|
153
129
|
openHeight,
|
|
154
130
|
// Round up to the nearest pixel to prevent subpixel rendering issues
|
|
155
131
|
Math.ceil(measurement.height + offset)
|
|
156
132
|
);
|
|
157
|
-
(0,
|
|
133
|
+
(0, import_react.useEffect)(() => {
|
|
158
134
|
if (!isOpen) {
|
|
159
135
|
const timeoutID = setTimeout(() => setIsHidden(!isOpen), 300);
|
|
160
136
|
return () => clearTimeout(timeoutID);
|
|
@@ -196,7 +172,7 @@ var Collapsible_default = Collapsible;
|
|
|
196
172
|
var React2 = require("react");
|
|
197
173
|
|
|
198
174
|
// src/index.ts
|
|
199
|
-
var
|
|
175
|
+
var index_default = Collapsible_default;
|
|
200
176
|
// Annotate the CommonJS export names for ESM import in node:
|
|
201
177
|
0 && (module.exports = {
|
|
202
178
|
Collapsible
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/Collapsible.tsx","../../seeds-react-hooks/src/useMeasure/useMeasure.ts","../../seeds-react-hooks/src/useSelect/useSelect.ts","../../seeds-react-hooks/src/useMultiselect/useMultiselect.ts","../../seeds-react-hooks/src/useMutationObserver/useMutationObserver.ts","../../seeds-react-hooks/src/useTextContent/useTextContent.ts","../../seeds-react-hooks/src/useWhyDidYouUpdate/useWhyDidYouUpdate.ts","../../seeds-react-hooks/src/useInteractiveColor/useInteractiveColor.ts","../src/styles.ts","../src/CollapsibleTypes.ts"],"sourcesContent":["import Collapsible from \"./Collapsible\";\n\nexport default Collapsible;\nexport { Collapsible };\nexport * from \"./CollapsibleTypes\";\n","import * as React from \"react\";\nimport { useState, useRef, useContext, useEffect } from \"react\";\nimport { useMeasure } from \"@sproutsocial/seeds-react-hooks\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport { CollapsingBox } from \"./styles\";\nimport type { TypeCollapsibleProps } from \"./CollapsibleTypes\";\n\nlet idCounter = 0;\n\ninterface TypeCollapsibleContext {\n isOpen?: boolean;\n id?: string;\n offset?: number;\n openHeight?: number;\n collapsedHeight?: number;\n}\n\nconst CollapsibleContext = React.createContext<TypeCollapsibleContext>({});\n\nconst Collapsible = ({\n children,\n isOpen = false,\n offset = 0,\n collapsedHeight = 0,\n openHeight,\n}: TypeCollapsibleProps) => {\n const [id] = useState(`Racine-collapsible-${idCounter++}`);\n return (\n <CollapsibleContext.Provider\n value={{\n isOpen,\n id,\n offset,\n collapsedHeight,\n openHeight,\n }}\n >\n {children}\n </CollapsibleContext.Provider>\n );\n};\n\nconst determineMaxHeight = (\n isHidden?: boolean,\n openHeight?: number,\n computedHeight?: number\n): number | undefined => {\n // If isHidden is undefined this is the first render. Return undefined so the max-height prop is not added\n // This is a hack to prevent css from animating if it begins in the open state\n // css animates when attribute values change (IE from 0 to another number)\n // css does not animate when simply adding an attribute to an HTML element\n if (isHidden === undefined) return undefined;\n // If the user has defined an explicit open height, return that as the max height\n if (openHeight) return openHeight;\n // Otherwise, fallback to the computed height\n return computedHeight;\n};\n\nconst Trigger = ({ children, ...rest }: { children: React.ReactElement }) => {\n const { isOpen, id } = useContext(CollapsibleContext);\n return (\n <React.Fragment>\n {React.cloneElement(children, {\n \"aria-controls\": id,\n \"aria-expanded\": !!isOpen,\n ...rest,\n })}\n </React.Fragment>\n );\n};\n\nTrigger.displayName = \"Collapsible.Trigger\";\n\nconst Panel = ({ children, ...rest }: { children: React.ReactNode }) => {\n const {\n isOpen,\n id,\n offset = 0,\n collapsedHeight,\n openHeight,\n } = useContext(CollapsibleContext);\n\n const ref = useRef<HTMLDivElement | null>(null);\n const measurement = useMeasure(ref);\n const [isHidden, setIsHidden] = useState<boolean | undefined>(undefined);\n const maxHeight = determineMaxHeight(\n isHidden,\n openHeight,\n // Round up to the nearest pixel to prevent subpixel rendering issues\n Math.ceil(measurement.height + offset)\n );\n\n /* We use the \"hidden\" attribute to remove the contents of the panel from the tab order of the page, but it interferes with the animation. This logic sets a slight timeout on setting the prop so that the animation has time to complete before the attribute is set. */\n useEffect(() => {\n if (!isOpen) {\n const timeoutID = setTimeout(() => setIsHidden(!isOpen), 300);\n return () => clearTimeout(timeoutID);\n } else {\n // Similar to the close animation, we need to delay setting hidden to run slightly async.\n // An issue occurs with the initial render isHidden logic that causes the animation to occur sporadically.\n // using this 0 second timeout just allows this component to initially render with an undefined max height,\n // Then go directly from undefined to the full max height, without a brief 0 value that triggers an animation\n const timeoutID = setTimeout(() => setIsHidden(!isOpen), 0);\n return () => clearTimeout(timeoutID);\n }\n }, [isOpen]);\n\n return (\n <CollapsingBox\n hasShadow={Boolean(collapsedHeight || (openHeight && openHeight > 0))}\n scrollable={isOpen}\n maxHeight={isOpen ? maxHeight : collapsedHeight}\n minHeight={collapsedHeight}\n data-qa-collapsible=\"\"\n data-qa-collapsible-isopen={isOpen === true}\n {...rest}\n >\n <Box\n width=\"100%\"\n hidden={isHidden && collapsedHeight === 0}\n aria-hidden={!isOpen}\n id={id}\n ref={ref}\n >\n {children}\n </Box>\n </CollapsingBox>\n );\n};\n\nPanel.displayName = \"Collapsible.Panel\";\n\nCollapsible.Trigger = Trigger;\nCollapsible.Panel = Panel;\n\nexport default Collapsible;\n","import { useState, useLayoutEffect, type RefObject } from \"react\";\n\ninterface DOMRectObject {\n x: number;\n y: number;\n width: number;\n height: number;\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\nconst initialBounds = Object.freeze({\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n});\n\nexport function useMeasure<TElement extends Element>(ref: RefObject<TElement>) {\n const [bounds, setContentRect] =\n useState<Readonly<DOMRectObject>>(initialBounds);\n\n useLayoutEffect(() => {\n const element = ref.current;\n\n if (\n !element ||\n // in non-browser environments (e.g. Jest tests) ResizeObserver is not defined\n !(\"ResizeObserver\" in window)\n ) {\n return;\n }\n\n const resizeObserver = new ResizeObserver(([entry]) => {\n if (!entry) return;\n const { x, y, width, height, top, right, bottom, left } =\n entry.contentRect;\n setContentRect({\n x,\n y,\n width,\n height,\n top,\n right,\n bottom,\n left,\n });\n });\n resizeObserver.observe(ref.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [ref]);\n\n return bounds;\n}\n","import { useState, useCallback } from \"react\";\n\ntype TypeSingleSelectProps<T extends string> = {\n initialValue?: T | \"\";\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChange?: (value: string | T) => any;\n};\n\nexport const useSelect = <T extends string>(\n {\n initialValue = \"\",\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: userOnChange = () => {},\n }: TypeSingleSelectProps<T> = {\n initialValue: \"\",\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: () => {},\n }\n) => {\n const [value, setValue] = useState<string | T>(initialValue);\n\n const onChange = useCallback(\n (newValue: string) => {\n if (newValue !== value) {\n setValue(newValue);\n userOnChange(newValue);\n }\n },\n [userOnChange, value]\n );\n\n return { value, onChange };\n};\n","import { useCallback, useEffect, useReducer, useRef } from \"react\";\n\ntype TypeMultiSelectProps<T extends string> = {\n initialValue?: T[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChange?: (value: Array<string | T>) => any;\n};\n\nconst valueReducer = (\n state: Set<string>,\n action: { type: string; value?: string }\n): Set<string> => {\n const newState = new Set(state);\n switch (action.type) {\n case \"reset\": {\n return new Set();\n }\n case \"toggle_item\":\n default: {\n if (action.value) {\n if (newState.has(action.value)) {\n newState.delete(action.value);\n } else {\n newState.add(action.value);\n }\n }\n return newState;\n }\n }\n};\n\nexport const useMultiselect = <T extends string>(\n {\n initialValue = [],\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: userOnChange = () => {},\n }: TypeMultiSelectProps<T> = {\n initialValue: [],\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: () => {},\n }\n) => {\n const [value, dispatch] = useReducer(valueReducer, new Set(initialValue));\n\n const getArrayValue = (value: Set<string | T>) =>\n Array.from<string | T>(value);\n\n const onChange = useCallback(\n (newValue: string) => {\n dispatch({ type: \"toggle_item\", value: newValue });\n },\n [dispatch]\n );\n\n const isFirstRun = useRef(true);\n\n useEffect(() => {\n if (isFirstRun.current) {\n isFirstRun.current = false;\n return;\n }\n userOnChange(getArrayValue(value));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [userOnChange, value]);\n\n const onClear = useCallback(() => {\n dispatch({ type: \"reset\" });\n }, [dispatch]);\n\n return { value: getArrayValue(value), onChange, onClear };\n};\n","import { canUseDOM } from \"@sproutsocial/seeds-react-utilities\";\nimport { useEffect, useMemo, useState } from \"react\";\n\ntype TypeMutationObserverInitRequired =\n | {\n childList: true;\n }\n | {\n attributes: true;\n }\n | {\n characterData: true;\n };\n\ntype TypeMutationObserverInit = {\n subtree?: boolean;\n attributeOldValue?: boolean;\n characterDataOldValue?: boolean;\n attributeFilter?: Array<string>;\n} & TypeMutationObserverInitRequired;\n\ntype TypeMutationObserverCallback = (\n mutationList?: MutationRecord[],\n observer?: MutationObserver\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n) => any;\n\nconst defaultCallback: TypeMutationObserverCallback = (mutationList) =>\n mutationList;\n\nexport function useMutationObserver(\n targetNode: Node | null,\n config: TypeMutationObserverInit,\n callback: TypeMutationObserverCallback = defaultCallback\n) {\n if (!canUseDOM()) {\n return;\n }\n /* eslint-disable-next-line */\n const [value, setValue] = useState(undefined);\n /* eslint-disable-next-line */\n const observer = useMemo(\n () =>\n new MutationObserver((mutationList, observer) => {\n const result = callback(mutationList, observer);\n setValue(result);\n }),\n [callback]\n );\n /* eslint-disable-next-line */\n useEffect(() => {\n if (targetNode) {\n observer.observe(targetNode, config);\n return () => {\n observer.disconnect();\n };\n }\n }, [targetNode, config, observer]);\n\n return value;\n}\n\nexport function useMutationObserverOnce(\n targetNode: Node | null,\n config: TypeMutationObserverInit,\n callback: TypeMutationObserverCallback\n) {\n const [isObserving, setObserving] = useState(true);\n const node = isObserving ? targetNode : null;\n const value = useMutationObserver(node, config, callback);\n if (value !== undefined && isObserving) {\n setObserving(false);\n }\n return value;\n}\n","import { useCallback, useState } from \"react\";\n\nexport type textContentRef = ((node: Node) => void) & { current?: string };\nexport function useTextContent(initial: string) {\n const [textContent, setTextContent] = useState(initial);\n\n const ref: textContentRef = useCallback((node: Node) => {\n if (node && node.textContent !== null) {\n setTextContent(node.textContent);\n }\n }, []);\n\n ref.current = textContent;\n return ref;\n}\n","import { useRef, useEffect } from \"react\";\n\nexport function useWhyDidYouUpdate(\n name: string,\n props: { [key: string]: any }\n) {\n // Get a mutable ref object where we can store props ...\n // ... for comparison next time this hook runs.\n const previousProps = useRef<typeof props>({});\n\n useEffect(() => {\n if (previousProps.current) {\n // Get all keys from previous and current props\n const allKeys = Object.keys({ ...previousProps.current, ...props });\n // Use this object to keep track of changed props\n const changesObj: typeof props = {};\n // Iterate through keys\n allKeys.forEach((key) => {\n // If previous is different from current\n\n if (previousProps.current[key] !== props[key]) {\n // Add to changesObj\n\n changesObj[key] = {\n from: previousProps.current[key],\n\n to: props[key],\n };\n }\n });\n\n // If changesObj not empty then output to console\n if (Object.keys(changesObj).length) {\n // eslint-disable-next-line no-console\n console.log(\"[why-did-you-update]\", name, changesObj);\n }\n }\n\n // Finally update previousProps with current props for next hook call\n previousProps.current = props;\n });\n}\n","import { darken, lighten } from \"polished\";\nimport { useTheme } from \"styled-components\";\nimport type { TypeTheme } from \"@sproutsocial/seeds-react-theme\";\n\n/**\n * The useInteractiveColor hook has context of theme mode (light or dark)\n * and can be used to lighten or darken a color dynamically\n *\n * note: colors are limited to our theme colors\n */\nconst useInteractiveColor = (themeColor: string): string => {\n // Throw error if used outside of a ThemeProvider (styled-components)\n if (!useTheme()) {\n throw new Error(\n \"useInteractiveColor() must be used within a Styled Components ThemeProvider\"\n );\n }\n\n // Get the current theme mode ie. 'light' or 'dark'\n const theme: TypeTheme = useTheme() as TypeTheme;\n const themeMode = theme.mode;\n\n // If the theme mode is dark, return a lightened version of the themeValue\n if (themeMode === \"dark\") {\n return lighten(0.2, themeColor);\n } else {\n // If the theme mode is light, return a darkened version of the themeValue\n return darken(0.2, themeColor);\n }\n};\n\nexport { useInteractiveColor };\n","import styled from \"styled-components\";\nimport Box from \"@sproutsocial/seeds-react-box\";\n\nexport const CollapsingBox = styled(Box)<{\n hasShadow?: boolean;\n scrollable?: boolean;\n}>`\n transition: max-height ${(p) => p.theme.duration.medium}\n ${(p) => p.theme.easing.ease_inout};\n will-change: max-height;\n position: relative;\n overflow: auto;\n ${({ hasShadow, scrollable }) =>\n hasShadow\n ? `background: /* Shadow covers */ linear-gradient(\n transparent 30%,\n rgba(255, 255, 255, 0)\n ),\n linear-gradient(rgba(255, 255, 255, 0), transparent 70%) 0 100%,\n /* Shadows */\n radial-gradient(\n farthest-side at 50% 0,\n rgb(39 51 51 / 5%),\n rgba(0, 0, 0, 0)\n ),\n radial-gradient(\n farthest-side at 50% 100%,\n rgb(39 51 51 / 5%),\n rgba(0, 0, 0, 0)\n )\n 0 100%;\n background-repeat: no-repeat;\n background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;\n background-attachment: local, local, scroll, scroll;\n ${scrollable ? `overflow: auto` : `overflow: hidden`};`\n : \"\"}\n`;\n","import * as React from \"react\";\n\n// The flow type is inexact but the underlying component does not accept any other props.\n// It might be worth extending the box props here for the refactor, but allowing it would provide no functionality right now.\nexport interface TypeCollapsibleProps {\n isOpen: boolean;\n children: React.ReactNode;\n\n /** If the children of the collapsible panel have a top or bottom margin, it will throw off the calculations for the height of the content. The total amount of vertical margin (in pixels) can be supplied to this prop to correct this. */\n offset?: number;\n collapsedHeight?: number;\n openHeight?: number;\n}\n"],"mappings":"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","names":["import_react","import_seeds_react_box","import_styled_components","styled","Box","Box","React"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Collapsible.tsx","../src/styles.ts","../src/CollapsibleTypes.ts"],"sourcesContent":["import Collapsible from \"./Collapsible\";\n\nexport default Collapsible;\nexport { Collapsible };\nexport * from \"./CollapsibleTypes\";\n","import * as React from \"react\";\nimport { useState, useRef, useContext, useEffect } from \"react\";\nimport { useMeasure } from \"@sproutsocial/seeds-react-hooks\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport { CollapsingBox } from \"./styles\";\nimport type {\n TypeCollapsibleProps,\n TypeCollapsiblePanelProps,\n TypeCollapsibleTriggerProps,\n} from \"./CollapsibleTypes\";\n\nlet idCounter = 0;\n\ninterface TypeCollapsibleContext {\n isOpen?: boolean;\n id?: string;\n offset?: number;\n openHeight?: number;\n collapsedHeight?: number;\n}\n\nconst CollapsibleContext = React.createContext<TypeCollapsibleContext>({});\n\nconst Collapsible = ({\n children,\n isOpen = false,\n offset = 0,\n collapsedHeight = 0,\n openHeight,\n}: TypeCollapsibleProps) => {\n const [id] = useState(`Racine-collapsible-${idCounter++}`);\n return (\n <CollapsibleContext.Provider\n value={{\n isOpen,\n id,\n offset,\n collapsedHeight,\n openHeight,\n }}\n >\n {children}\n </CollapsibleContext.Provider>\n );\n};\n\nconst determineMaxHeight = (\n isHidden?: boolean,\n openHeight?: number,\n computedHeight?: number\n): number | undefined => {\n // If isHidden is undefined this is the first render. Return undefined so the max-height prop is not added\n // This is a hack to prevent css from animating if it begins in the open state\n // css animates when attribute values change (IE from 0 to another number)\n // css does not animate when simply adding an attribute to an HTML element\n if (isHidden === undefined) return undefined;\n // If the user has defined an explicit open height, return that as the max height\n if (openHeight) return openHeight;\n // Otherwise, fallback to the computed height\n return computedHeight;\n};\n\nconst Trigger = ({ children, ...rest }: TypeCollapsibleTriggerProps) => {\n const { isOpen, id } = useContext(CollapsibleContext);\n return (\n <React.Fragment>\n {React.cloneElement(children, {\n \"aria-controls\": id,\n \"aria-expanded\": !!isOpen,\n ...rest,\n })}\n </React.Fragment>\n );\n};\n\nTrigger.displayName = \"Collapsible.Trigger\";\n\nconst Panel = ({ children, ...rest }: TypeCollapsiblePanelProps) => {\n const {\n isOpen,\n id,\n offset = 0,\n collapsedHeight,\n openHeight,\n } = useContext(CollapsibleContext);\n\n const ref = useRef<HTMLDivElement | null>(null);\n const measurement = useMeasure(ref);\n const [isHidden, setIsHidden] = useState<boolean | undefined>(undefined);\n const maxHeight = determineMaxHeight(\n isHidden,\n openHeight,\n // Round up to the nearest pixel to prevent subpixel rendering issues\n Math.ceil(measurement.height + offset)\n );\n\n /* We use the \"hidden\" attribute to remove the contents of the panel from the tab order of the page, but it interferes with the animation. This logic sets a slight timeout on setting the prop so that the animation has time to complete before the attribute is set. */\n useEffect(() => {\n if (!isOpen) {\n const timeoutID = setTimeout(() => setIsHidden(!isOpen), 300);\n return () => clearTimeout(timeoutID);\n } else {\n // Similar to the close animation, we need to delay setting hidden to run slightly async.\n // An issue occurs with the initial render isHidden logic that causes the animation to occur sporadically.\n // using this 0 second timeout just allows this component to initially render with an undefined max height,\n // Then go directly from undefined to the full max height, without a brief 0 value that triggers an animation\n const timeoutID = setTimeout(() => setIsHidden(!isOpen), 0);\n return () => clearTimeout(timeoutID);\n }\n }, [isOpen]);\n\n return (\n <CollapsingBox\n hasShadow={Boolean(collapsedHeight || (openHeight && openHeight > 0))}\n scrollable={isOpen}\n maxHeight={isOpen ? maxHeight : collapsedHeight}\n minHeight={collapsedHeight}\n data-qa-collapsible=\"\"\n data-qa-collapsible-isopen={isOpen === true}\n {...rest}\n >\n <Box\n width=\"100%\"\n hidden={isHidden && collapsedHeight === 0}\n aria-hidden={!isOpen}\n id={id}\n ref={ref}\n >\n {children}\n </Box>\n </CollapsingBox>\n );\n};\n\nPanel.displayName = \"Collapsible.Panel\";\n\nCollapsible.Trigger = Trigger;\nCollapsible.Panel = Panel;\n\nexport default Collapsible;\n","import styled from \"styled-components\";\nimport Box from \"@sproutsocial/seeds-react-box\";\n\nexport const CollapsingBox = styled(Box)<{\n hasShadow?: boolean;\n scrollable?: boolean;\n}>`\n transition: max-height ${(p) => p.theme.duration.medium}\n ${(p) => p.theme.easing.ease_inout};\n will-change: max-height;\n position: relative;\n overflow: auto;\n ${({ hasShadow, scrollable }) =>\n hasShadow\n ? `background: /* Shadow covers */ linear-gradient(\n transparent 30%,\n rgba(255, 255, 255, 0)\n ),\n linear-gradient(rgba(255, 255, 255, 0), transparent 70%) 0 100%,\n /* Shadows */\n radial-gradient(\n farthest-side at 50% 0,\n rgb(39 51 51 / 5%),\n rgba(0, 0, 0, 0)\n ),\n radial-gradient(\n farthest-side at 50% 100%,\n rgb(39 51 51 / 5%),\n rgba(0, 0, 0, 0)\n )\n 0 100%;\n background-repeat: no-repeat;\n background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;\n background-attachment: local, local, scroll, scroll;\n ${scrollable ? `overflow: auto` : `overflow: hidden`};`\n : \"\"}\n`;\n","import * as React from \"react\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\n\n// The flow type is inexact but the underlying component does not accept any other props.\n// It might be worth extending the box props here for the refactor, but allowing it would provide no functionality right now.\nexport interface TypeCollapsibleProps {\n isOpen: boolean;\n children: React.ReactNode;\n\n /** If the children of the collapsible panel have a top or bottom margin, it will throw off the calculations for the height of the content. The total amount of vertical margin (in pixels) can be supplied to this prop to correct this. */\n offset?: number;\n collapsedHeight?: number;\n openHeight?: number;\n}\n\nexport interface TypeCollapsibleTriggerProps\n extends Omit<TypeBoxProps, \"children\"> {\n // Children is required for the Trigger\n children: React.ReactElement;\n}\n\nexport interface TypeCollapsiblePanelProps extends TypeBoxProps {}\n"],"mappings":"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","names":["import_seeds_react_box","styled","Box","Box","React"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sproutsocial/seeds-react-collapsible",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Seeds React Collapsible",
|
|
5
5
|
"author": "Sprout Social, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"test:watch": "jest --watch --coverage=false"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@sproutsocial/seeds-react-box": "
|
|
21
|
+
"@sproutsocial/seeds-react-box": "^1.1.1",
|
|
22
|
+
"@sproutsocial/seeds-react-hooks": "^3.0.1"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"tsup": "^8.0.2",
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
"@sproutsocial/seeds-tsconfig": "*",
|
|
31
32
|
"@sproutsocial/seeds-testing": "*",
|
|
32
33
|
"@sproutsocial/seeds-react-testing-library": "*",
|
|
33
|
-
"@sproutsocial/seeds-react-button": "
|
|
34
|
+
"@sproutsocial/seeds-react-button": "^1.2.0"
|
|
34
35
|
},
|
|
35
36
|
"peerDependencies": {
|
|
36
37
|
"styled-components": "^5.2.3"
|
package/src/Collapsible.tsx
CHANGED
|
@@ -3,7 +3,11 @@ import { useState, useRef, useContext, useEffect } from "react";
|
|
|
3
3
|
import { useMeasure } from "@sproutsocial/seeds-react-hooks";
|
|
4
4
|
import Box from "@sproutsocial/seeds-react-box";
|
|
5
5
|
import { CollapsingBox } from "./styles";
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
TypeCollapsibleProps,
|
|
8
|
+
TypeCollapsiblePanelProps,
|
|
9
|
+
TypeCollapsibleTriggerProps,
|
|
10
|
+
} from "./CollapsibleTypes";
|
|
7
11
|
|
|
8
12
|
let idCounter = 0;
|
|
9
13
|
|
|
@@ -56,7 +60,7 @@ const determineMaxHeight = (
|
|
|
56
60
|
return computedHeight;
|
|
57
61
|
};
|
|
58
62
|
|
|
59
|
-
const Trigger = ({ children, ...rest }:
|
|
63
|
+
const Trigger = ({ children, ...rest }: TypeCollapsibleTriggerProps) => {
|
|
60
64
|
const { isOpen, id } = useContext(CollapsibleContext);
|
|
61
65
|
return (
|
|
62
66
|
<React.Fragment>
|
|
@@ -71,7 +75,7 @@ const Trigger = ({ children, ...rest }: { children: React.ReactElement }) => {
|
|
|
71
75
|
|
|
72
76
|
Trigger.displayName = "Collapsible.Trigger";
|
|
73
77
|
|
|
74
|
-
const Panel = ({ children, ...rest }:
|
|
78
|
+
const Panel = ({ children, ...rest }: TypeCollapsiblePanelProps) => {
|
|
75
79
|
const {
|
|
76
80
|
isOpen,
|
|
77
81
|
id,
|
package/src/CollapsibleTypes.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import type { TypeBoxProps } from "@sproutsocial/seeds-react-box";
|
|
2
3
|
|
|
3
4
|
// The flow type is inexact but the underlying component does not accept any other props.
|
|
4
5
|
// It might be worth extending the box props here for the refactor, but allowing it would provide no functionality right now.
|
|
@@ -11,3 +12,11 @@ export interface TypeCollapsibleProps {
|
|
|
11
12
|
collapsedHeight?: number;
|
|
12
13
|
openHeight?: number;
|
|
13
14
|
}
|
|
15
|
+
|
|
16
|
+
export interface TypeCollapsibleTriggerProps
|
|
17
|
+
extends Omit<TypeBoxProps, "children"> {
|
|
18
|
+
// Children is required for the Trigger
|
|
19
|
+
children: React.ReactElement;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TypeCollapsiblePanelProps extends TypeBoxProps {}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "@sproutsocial/seeds-react-testing-library";
|
|
8
8
|
import Box from "@sproutsocial/seeds-react-box";
|
|
9
9
|
import Button from "@sproutsocial/seeds-react-button";
|
|
10
|
-
import { Collapsible } from "../
|
|
10
|
+
import { Collapsible } from "../";
|
|
11
11
|
|
|
12
12
|
export interface TypeStatefulCollapseTestProps {
|
|
13
13
|
children: React.ReactNode;
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { render } from "@sproutsocial/seeds-react-testing-library";
|
|
3
3
|
import Box from "@sproutsocial/seeds-react-box";
|
|
4
4
|
import Button from "@sproutsocial/seeds-react-button";
|
|
5
|
-
import { Collapsible } from "../
|
|
5
|
+
import { Collapsible } from "../";
|
|
6
6
|
|
|
7
7
|
const toggle = jest.fn();
|
|
8
8
|
|