@homecode/ui 5.14.1 → 5.15.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/dist/esm/index.js CHANGED
@@ -20,6 +20,8 @@ export { Draggable } from './src/components/Draggable/Draggable.js';
20
20
  export { DropZone } from './src/components/DropZone/DropZone.js';
21
21
  export { Expand } from './src/components/Expand/Expand.js';
22
22
  export { Flex } from './src/components/Flex/Flex.js';
23
+ export { FoldedLine } from './src/components/FoldedLine/FoldedLine.js';
24
+ export { FoldedLineAction, FoldedLineActionsProvider, useFoldedLineActionsRegister } from './src/components/FoldedLine/FoldedLineActionsContext.js';
23
25
  export { Form } from './src/components/Form/Form.js';
24
26
  export { FormattedText } from './src/components/FormattedText/FormattedText.js';
25
27
  export { Gap } from './src/components/Gap/Gap.js';
@@ -49,10 +49,12 @@ import '../../tools/queryParams.js';
49
49
  import '../Draggable/Draggable.styl.js';
50
50
  import '../DropZone/DropZone.styl.js';
51
51
  import '../Flex/Flex.styl.js';
52
+ import '../Gap/Gap.styl.js';
53
+ import '../FoldedLine/FoldedLine.styl.js';
54
+ import '../FoldedLine/FoldedLineActionsContext.js';
52
55
  import '../Form/Form.styl.js';
53
56
  import '../Form/Validator.js';
54
57
  import '../Form/SubmitButtons/SubmitButtons.styl.js';
55
- import '../Gap/Gap.styl.js';
56
58
  import '../ProgressCircular/ProgressCircular.styl.js';
57
59
  import '../Slider/Slider.styl.js';
58
60
  import '../VideoPlayer/VideoPlayer.styl.js';
@@ -0,0 +1,60 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import cn from 'classnames';
3
+ import { useState, useRef, useCallback, useEffect } from 'react';
4
+ import { Button } from '../Button/Button.js';
5
+ import { Gap } from '../Gap/Gap.js';
6
+ import { Icon } from '../Icon/Icon.js';
7
+ import { Scroll } from '../Scroll/Scroll.js';
8
+ import S from './FoldedLine.styl.js';
9
+ import { FoldedLineActionsProvider } from './FoldedLineActionsContext.js';
10
+
11
+ function FoldedLine({ trigger, children, defaultFolded = true, className, folded: foldedProp, onFoldedChange, onScrollInnerRef, hideFoldButton = false, showChevron = true, onScrollInner, contentScrolling = true, variant = 'default', }) {
12
+ const [uncontrolledFolded, setUncontrolledFolded] = useState(defaultFolded);
13
+ const isControlled = foldedProp !== undefined;
14
+ const isFolded = isControlled ? foldedProp : uncontrolledFolded;
15
+ const setFolded = (next) => {
16
+ if (isControlled)
17
+ onFoldedChange?.(next);
18
+ else
19
+ setUncontrolledFolded(next);
20
+ };
21
+ const [actions, setActions] = useState(new Map());
22
+ const triggerRef = useRef(null);
23
+ const isClear = variant === 'clear';
24
+ const registerAction = useCallback((id, node) => {
25
+ setActions(prev => {
26
+ const next = new Map(prev);
27
+ if (node == null)
28
+ next.delete(id);
29
+ else
30
+ next.set(id, node);
31
+ return next;
32
+ });
33
+ }, []);
34
+ useEffect(() => {
35
+ if (isFolded)
36
+ setActions(new Map());
37
+ }, [isFolded]);
38
+ const isInteractiveTarget = (target) => {
39
+ if (!(target instanceof Element))
40
+ return false;
41
+ return Boolean(target.closest('a[href], button, [role="button"], input, textarea, select, label'));
42
+ };
43
+ const handleClick = (e) => {
44
+ if (!children || isInteractiveTarget(e.target))
45
+ return;
46
+ if (isClear || isFolded) {
47
+ setFolded(!isFolded);
48
+ return;
49
+ }
50
+ if (triggerRef.current?.contains(e.target)) {
51
+ setFolded(true);
52
+ }
53
+ };
54
+ return (jsxs("div", { className: cn(S.root, isFolded && S.folded, Boolean(children) && S.hasChildren, isClear && S.clear, className), onClick: handleClick, children: [jsxs("div", { ref: triggerRef, className: cn(Boolean(children) && S.triggerInteractive), children: [typeof trigger === 'function' ? trigger(isFolded) : trigger, jsx(Gap, {}), showChevron && jsx(Icon, { type: "chevronDown", className: S.chevron })] }), !isFolded && children && (jsx("div", { className: S.content, children: jsxs(FoldedLineActionsProvider, { registerAction: registerAction, children: [contentScrolling ? (jsx(Scroll, { y: true, size: "s", autoHide: true, className: S.scroll, innerClassName: S.scrollInner, offset: { y: { before: 4, after: 14 } }, onInnerRef: onScrollInnerRef, onScroll: onScrollInner, children: children })) : (jsx("div", { className: S.scrollInner, children: children })), jsxs("div", { className: S.actions, children: [Array.from(actions.values()), !hideFoldButton && (jsxs(Button, { size: "xs", onClick: e => {
55
+ e.stopPropagation();
56
+ setFolded(true);
57
+ }, children: [jsx(Icon, { type: "chevronUp", size: "s" }), "Fold"] }))] })] }) }))] }));
58
+ }
59
+
60
+ export { FoldedLine };
@@ -0,0 +1,7 @@
1
+ import styleInject from '../../../node_modules/style-inject/dist/style-inject.es.js';
2
+
3
+ var css_248z = ".FoldedLine_root__lhi-1{border-radius:12px;display:flex;flex-direction:column;min-height:1rem;padding-left:calc(4px + var(--p-2));position:relative;transition:.1s ease-out;transition-property:border-radius,margin,background-color}.FoldedLine_root__lhi-1:before{background:var(--accent-color);border-radius:2px;bottom:2px;content:\"\";height:calc(100% - 4px);left:6px;opacity:.1;pointer-events:none;position:absolute;top:2px;width:4px}.FoldedLine_root__lhi-1:hover:before,.FoldedLine_root__lhi-1:not(.FoldedLine_folded__6zX8d):before{opacity:.7}.FoldedLine_root__lhi-1.FoldedLine_folded__6zX8d{border-radius:0;margin:0}.FoldedLine_root__lhi-1.FoldedLine_folded__6zX8d .FoldedLine_scroll__X7T5v{height:1em}.FoldedLine_root__lhi-1.FoldedLine_hasChildren__8hCZA.FoldedLine_folded__6zX8d{cursor:pointer}.FoldedLine_root__lhi-1.FoldedLine_hasChildren__8hCZA:not(.FoldedLine_folded__6zX8d) .FoldedLine_triggerInteractive__sCLnl{border-top-left-radius:inherit;border-top-right-radius:inherit}.FoldedLine_root__lhi-1.FoldedLine_clear__t47RA{border-radius:inherit;min-height:0;padding:0}.FoldedLine_root__lhi-1.FoldedLine_clear__t47RA:before{content:none}.FoldedLine_root__lhi-1.FoldedLine_clear__t47RA.FoldedLine_hasChildren__8hCZA{cursor:pointer}.FoldedLine_root__lhi-1.FoldedLine_clear__t47RA.FoldedLine_folded__6zX8d{-webkit-line-clamp:unset;-webkit-box-orient:unset;line-clamp:none;display:flex;overflow:visible;text-overflow:clip}.FoldedLine_root__lhi-1.FoldedLine_clear__t47RA .FoldedLine_triggerInteractive__sCLnl{opacity:1}.FoldedLine_chevron__zxu20{margin-right:var(--p-2);opacity:.3;transition:transform .1s ease-out,opacity .1s ease-out}.FoldedLine_root__lhi-1:hover .FoldedLine_chevron__zxu20{opacity:1}.FoldedLine_folded__6zX8d .FoldedLine_chevron__zxu20{transform:rotate(-90deg)}.FoldedLine_lineCompleted__sEfzm:before{background:var(--active-color)}.FoldedLine_lineInProgress__VcwjJ:before{animation:FoldedLine_foldedLinePulse__y-Dle 1.4s ease-in-out infinite}.FoldedLine_triggerInteractive__sCLnl{align-items:center;cursor:pointer;display:flex;gap:var(--p-1);opacity:.8;transition:background-color .1s ease-out}.FoldedLine_triggerInteractive__sCLnl:hover{opacity:1}.FoldedLine_triggerInteractive__sCLnl:hover:before{background-color:var(--accent-color-alpha-900)}.FoldedLine_header__gyQpI{cursor:pointer;height:100%;position:absolute;transition:opacity .1s ease-out;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%;z-index:1}.FoldedLine_folded__6zX8d{-webkit-line-clamp:1;-webkit-box-orient:vertical;line-clamp:1;display:-webkit-box;overflow:hidden;text-overflow:ellipsis;transition:opacity .1s ease-out}.FoldedLine_folded__6zX8d:before{opacity:.4}.FoldedLine_folded__6zX8d .FoldedLine_scroll__X7T5v{max-height:1.5em}.FoldedLine_content__POIox{position:relative}.FoldedLine_scroll__X7T5v{max-height:50vh}.FoldedLine_scrollInner__1BGOP>div{-webkit-user-select:text;-moz-user-select:text;user-select:text}.FoldedLine_actions__SmF3b{align-items:center;bottom:0;display:flex;flex-shrink:0;flex-wrap:wrap;gap:var(--p-1);justify-content:flex-end;padding:0 var(--p-5) var(--p-3) var(--p-2);position:absolute;right:0}.FoldedLine_actions__SmF3b>button{-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}@keyframes FoldedLine_foldedLinePulse__y-Dle{0%,to{background-color:var(--accent-color-alpha-400)}50%{background-color:var(--active-color)}}";
4
+ var S = {"root":"FoldedLine_root__lhi-1","folded":"FoldedLine_folded__6zX8d","scroll":"FoldedLine_scroll__X7T5v","hasChildren":"FoldedLine_hasChildren__8hCZA","triggerInteractive":"FoldedLine_triggerInteractive__sCLnl","clear":"FoldedLine_clear__t47RA","chevron":"FoldedLine_chevron__zxu20","lineCompleted":"FoldedLine_lineCompleted__sEfzm","lineInProgress":"FoldedLine_lineInProgress__VcwjJ","foldedLinePulse":"FoldedLine_foldedLinePulse__y-Dle","header":"FoldedLine_header__gyQpI","content":"FoldedLine_content__POIox","scrollInner":"FoldedLine_scrollInner__1BGOP","actions":"FoldedLine_actions__SmF3b"};
5
+ styleInject(css_248z);
6
+
7
+ export { S as default };
@@ -0,0 +1,25 @@
1
+ import { jsx, Fragment } from 'react/jsx-runtime';
2
+ import { createContext, useContext, useId, useEffect } from 'react';
3
+
4
+ const FoldedLineActionsContext = createContext(null);
5
+ function useFoldedLineActionsRegister() {
6
+ return useContext(FoldedLineActionsContext);
7
+ }
8
+ function FoldedLineActionsProvider({ registerAction, children, }) {
9
+ return (jsx(FoldedLineActionsContext.Provider, { value: registerAction, children: children }));
10
+ }
11
+ function FoldedLineAction({ children }) {
12
+ const register = useFoldedLineActionsRegister();
13
+ const id = useId();
14
+ useEffect(() => {
15
+ if (!register)
16
+ return undefined;
17
+ register(id, children);
18
+ return () => register(id, null);
19
+ }, [register, id, children]);
20
+ if (!register)
21
+ return jsx(Fragment, { children: children });
22
+ return null;
23
+ }
24
+
25
+ export { FoldedLineAction, FoldedLineActionsProvider, useFoldedLineActionsRegister };
@@ -51,10 +51,12 @@ import '../Dialogue/Dialogue.js';
51
51
  import { Draggable } from '../Draggable/Draggable.js';
52
52
  import '../DropZone/DropZone.styl.js';
53
53
  import '../Flex/Flex.styl.js';
54
+ import '../Gap/Gap.styl.js';
55
+ import '../FoldedLine/FoldedLine.styl.js';
56
+ import '../FoldedLine/FoldedLineActionsContext.js';
54
57
  import '../Form/Form.styl.js';
55
58
  import '../Form/Validator.js';
56
59
  import '../Form/SubmitButtons/SubmitButtons.styl.js';
57
- import '../Gap/Gap.styl.js';
58
60
  import '../ProgressCircular/ProgressCircular.styl.js';
59
61
  import '../Slider/Slider.styl.js';
60
62
  import '../VideoPlayer/VideoPlayer.styl.js';
@@ -1,6 +1,7 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { useContext, useEffect, useMemo } from 'react';
3
3
  import { useStore } from 'justorm/react';
4
+ import { parseQueryParams } from '../../tools/queryParams.js';
4
5
  import STORE from './store.js';
5
6
  import Context from './context.js';
6
7
  import { parsePath, replaceParamsInPath } from './Router.helpers.js';
@@ -17,7 +18,9 @@ const Router = (props) => {
17
18
  const fullPath = ctx.basePath + basePath;
18
19
  useEffect(() => {
19
20
  const onPopState = () => {
20
- STORE.go(window.location.pathname, STORE.query, { replace: true });
21
+ STORE.go(window.location.pathname, parseQueryParams(), {
22
+ replace: true,
23
+ });
21
24
  };
22
25
  window.addEventListener('popstate', onPopState);
23
26
  return () => window.removeEventListener('popstate', onPopState);
@@ -0,0 +1,3 @@
1
+ import * as T from './FoldedLine.types';
2
+ export type FoldedLineProps = T.Props;
3
+ export declare function FoldedLine({ trigger, children, defaultFolded, className, folded: foldedProp, onFoldedChange, onScrollInnerRef, hideFoldButton, showChevron, onScrollInner, contentScrolling, variant, }: FoldedLineProps): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { ReactNode, UIEvent } from 'react';
2
+ export type Props = {
3
+ trigger: ReactNode | ((isFolded: boolean) => ReactNode);
4
+ children?: ReactNode;
5
+ defaultFolded?: boolean;
6
+ className?: string;
7
+ folded?: boolean;
8
+ hideFoldButton?: boolean;
9
+ onFoldedChange?: (folded: boolean) => void;
10
+ onScrollInnerRef?: (el: HTMLDivElement | null) => void;
11
+ onScrollInner?: (e: UIEvent<HTMLDivElement>) => void;
12
+ contentScrolling?: boolean;
13
+ variant?: 'default' | 'clear';
14
+ showChevron?: boolean;
15
+ };
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+ type RegisterAction = (id: string, node: ReactNode | null) => void;
3
+ export declare function useFoldedLineActionsRegister(): RegisterAction | null;
4
+ export declare function FoldedLineActionsProvider({ registerAction, children, }: {
5
+ registerAction: RegisterAction;
6
+ children: ReactNode;
7
+ }): import("react").JSX.Element;
8
+ export declare function FoldedLineAction({ children }: {
9
+ children: ReactNode;
10
+ }): import("react").JSX.Element;
11
+ export {};
@@ -0,0 +1,3 @@
1
+ export { FoldedLine } from './FoldedLine';
2
+ export type { FoldedLineProps } from './FoldedLine';
3
+ export { FoldedLineAction, FoldedLineActionsProvider, useFoldedLineActionsRegister, } from './FoldedLineActionsContext';
@@ -16,6 +16,7 @@ export * from './Draggable/Draggable';
16
16
  export * from './DropZone/DropZone';
17
17
  export * from './Expand/Expand';
18
18
  export * from './Flex/Flex';
19
+ export * from './FoldedLine';
19
20
  export * from './Form/Form';
20
21
  export * from './FormattedText';
21
22
  export * from './Gap/Gap';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homecode/ui",
3
- "version": "5.14.1",
3
+ "version": "5.15.0",
4
4
  "description": "React UI components library",
5
5
  "scripts": {
6
6
  "tests": "jest",