forstok-ui-lib 3.0.1 → 4.0.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.
@@ -0,0 +1,92 @@
1
+ import { type ReactNode, isValidElement, useEffect, useRef, useCallback } from 'react';
2
+ import ReactTooltip from 'react-tooltip';
3
+ import { useStateWithCallbackLazy } from 'use-state-with-callback';
4
+ import ButtonComponent from '../button/button';
5
+ import IconComponent from '../icon/icon';
6
+ import { PopupPanel, PopupOverlay, PopupContainer, PopupWrapper, PopupHeaderWrapper, PopupHeader, PopupBody, PopupFooter, PopupQuestionWrapper } from './popup.styles';
7
+ import type { TPopup } from './popup.typed';
8
+
9
+ type TPopupOtherProp = {
10
+ children: ReactNode[]
11
+ 'data-qa-id'?: string
12
+ }
13
+
14
+ let timerHandle:ReturnType<typeof setTimeout>
15
+ const PopupComponent = ({ children, body, mode, height, width, isOpen, evTooglePopup, ...props }: TPopup & TPopupOtherProp) => {
16
+ const [isOpenWrapper, setOpenWrapper] = useStateWithCallbackLazy<boolean>(false);
17
+
18
+ const popupContainerRef = useRef<HTMLElement | null>(null);
19
+ useEffect(() => { ReactTooltip.rebuild(); }, [])
20
+
21
+ const evOpen = useCallback((isOpen: boolean) => {
22
+ timerHandle = setTimeout(() => {
23
+ setOpenWrapper(isOpen, () => {
24
+ const popupContainerEl = (popupContainerRef && popupContainerRef.current) ? popupContainerRef.current : null;
25
+ popupContainerEl && (popupContainerEl.scrollTop = 0);
26
+ });
27
+ clearTimeout(timerHandle);
28
+ }, 1);
29
+ }, [setOpenWrapper])
30
+
31
+ const initialize = () => {
32
+ evOpen(isOpen);
33
+ }
34
+ const initRef = useRef({ init: initialize });
35
+
36
+ useEffect(() => {
37
+ const { init } = initRef.current;
38
+ init();
39
+
40
+ return(() => {
41
+ clearTimeout(timerHandle);
42
+ })
43
+ }, [])
44
+
45
+ useEffect(()=> {
46
+ evOpen(isOpen);
47
+ }, [isOpen, evOpen])
48
+
49
+ let newChildren = [...children];
50
+ const HeadChildrenFilter = newChildren?.filter((child: any) => isValidElement(child) && (child.props as any)['aria-label'] === 'head') || null;
51
+ const SubHeadChildrenFilter = newChildren?.filter((child: any) => isValidElement(child) && (child.props as any)['aria-label'] === 'sub-head') || null;
52
+ const BodyChildrenFilter = newChildren?.filter((child: any) => isValidElement(child) && (child.props as any)['aria-label'] === 'body') || null;
53
+ const FootChildrenFilter = newChildren?.filter((child: any) => isValidElement(child) && (child.props as any)['aria-label'] === 'foot') || null;
54
+ const LeftFootChildrenFilter = newChildren?.filter((child: any) => isValidElement(child) && (child.props as any)['aria-label'] === 'left-foot') || null;
55
+ const SubHeadChildrenEl = SubHeadChildrenFilter.length ? <aside>{(SubHeadChildrenFilter[0] && isValidElement(SubHeadChildrenFilter[0])) ? (SubHeadChildrenFilter[0].props as any).children : null}</aside> : null;
56
+ const popupHeaderQaId = HeadChildrenFilter.length ? (HeadChildrenFilter[0] && isValidElement(HeadChildrenFilter[0])) ? (HeadChildrenFilter[0].props as any)['data-qa-id'] : '' : '';
57
+
58
+ return (
59
+ <PopupPanel $isOpen={isOpen}>
60
+ <PopupOverlay />
61
+ <PopupContainer ref={popupContainerRef} {...props['data-qa-id'] && { 'data-qa-id': props['data-qa-id'] }}>
62
+ {
63
+ mode === 'question' ? (
64
+ <PopupWrapper $isOpen={isOpenWrapper}>
65
+ <PopupQuestionWrapper $width={width}>
66
+ <IconComponent $name='warning' $width='30px' />
67
+ <PopupHeaderWrapper $mode={mode}>
68
+ { HeadChildrenFilter.length ? <PopupHeader>{(HeadChildrenFilter[0] && isValidElement(HeadChildrenFilter[0])) ? (HeadChildrenFilter[0].props as any).children : null}</PopupHeader> : null }
69
+ {SubHeadChildrenEl}
70
+ { FootChildrenFilter.length ? <PopupFooter>{(FootChildrenFilter[0] && isValidElement(FootChildrenFilter[0])) ? <aside data-type={(FootChildrenFilter[0].props as any).type || ""}>{(FootChildrenFilter[0].props as any).children}</aside> : null}</PopupFooter> : null }
71
+ </PopupHeaderWrapper>
72
+ </PopupQuestionWrapper>
73
+ </PopupWrapper>
74
+ ) : (
75
+ <>
76
+ <PopupHeaderWrapper $width={width} $isOpen={isOpenWrapper} $mode={mode}>
77
+ {HeadChildrenFilter.length ? <><ButtonComponent $mode='round-close' onClick={evTooglePopup}></ButtonComponent><PopupHeader data-qa-id={popupHeaderQaId}>{(HeadChildrenFilter[0] && isValidElement(HeadChildrenFilter[0])) && (HeadChildrenFilter[0].props as any).children}</PopupHeader></> : null}
78
+ </PopupHeaderWrapper>
79
+ <PopupWrapper $width={width} $isOpen={isOpenWrapper} $withHeader={HeadChildrenFilter.length ? true : false}>
80
+ {SubHeadChildrenEl}
81
+ { BodyChildrenFilter.length ? <PopupBody $mode={body} $width={width} $height={height}>{(BodyChildrenFilter[0] && isValidElement(BodyChildrenFilter[0])) && (BodyChildrenFilter[0].props as any).children}</PopupBody> : null }
82
+ { FootChildrenFilter.length ? <PopupFooter $isLeft={LeftFootChildrenFilter.length ? true : false}><aside>{(LeftFootChildrenFilter && LeftFootChildrenFilter.length && LeftFootChildrenFilter[0] && isValidElement(LeftFootChildrenFilter[0])) ? (LeftFootChildrenFilter[0].props as any).children : null}</aside>{(FootChildrenFilter[0] && isValidElement(FootChildrenFilter[0])) ? <aside data-type={(FootChildrenFilter[0].props as any).type || ""}>{mode && mode === 'info' ? <ButtonComponent $mode='white' $size='small' onClick={evTooglePopup}>Close</ButtonComponent> : <ButtonComponent $mode='white' $size='small' onClick={evTooglePopup} data-qa-id='popup-button-cancel'>Cancel</ButtonComponent>}{(FootChildrenFilter[0].props as any).children}</aside> : null}</PopupFooter> : null }
83
+ </PopupWrapper>
84
+ </>
85
+ )
86
+ }
87
+ </PopupContainer>
88
+ </PopupPanel>
89
+ )
90
+ }
91
+
92
+ export default PopupComponent
@@ -0,0 +1,33 @@
1
+ import type { MouseEvent } from 'react'
2
+
3
+ import type { TMessageFunction } from '../message/message.typed'
4
+
5
+ export type TPopupOpenFunction = (e: MouseEvent, el?: string) => void
6
+
7
+ export type TPopupFunctionParam = {
8
+ mode?: string | null
9
+ path?: string | null
10
+ action?: string | null
11
+ detail?: any
12
+ }
13
+
14
+ export type TPopupFunction = (props?: TPopupFunctionParam) => void
15
+
16
+ export type TPopupFunctionGroup = {
17
+ evTooglePopup?: TPopupFunction
18
+ evCreateMessage?: TMessageFunction
19
+ evForcePopUp?: TPopupFunction
20
+ }
21
+
22
+ export type TPopupContainer = {
23
+ isOpen: boolean
24
+ } & Required<TPopupFunctionParam> & Required<TPopupFunctionGroup>
25
+
26
+
27
+ export type TPopup = {
28
+ isOpen: boolean
29
+ body?: string
30
+ height?: string|number
31
+ width?: string|number
32
+ type?: string
33
+ } & TPopupFunctionParam & TPopupFunctionGroup
@@ -0,0 +1,37 @@
1
+ import { useState, useLayoutEffect, type ReactNode } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+
4
+ function createWrapperAndAppendToBody(wrapperId: string) {
5
+ const wrapperElement = document.createElement('div');
6
+ wrapperElement.setAttribute('id', wrapperId);
7
+ document.body.appendChild(wrapperElement);
8
+ return wrapperElement;
9
+ }
10
+
11
+ function ReactPortalComponent({ children, wrapperId='popup-portal' }:{ children: ReactNode, wrapperId?: string }) {
12
+ const [wrapperElement, setWrapperElement] = useState<Element | DocumentFragment>();
13
+
14
+ useLayoutEffect(() => {
15
+ let element = document.getElementById(wrapperId);
16
+ let systemCreated = false;
17
+ if (!element) {
18
+ systemCreated = true;
19
+ element = createWrapperAndAppendToBody(wrapperId);
20
+ }
21
+ setWrapperElement(element);
22
+
23
+ return () => {
24
+ if (systemCreated && (element && element.parentNode)) {
25
+ element.parentNode.removeChild(element);
26
+ }
27
+ }
28
+ }, [wrapperId])
29
+
30
+ if (typeof wrapperElement === 'undefined') {
31
+ return <></>;
32
+ }
33
+
34
+ return createPortal(children, wrapperElement);
35
+ }
36
+
37
+ export default ReactPortalComponent;
@@ -1,3 +1,8 @@
1
+ import { TObject } from './base.typed';
2
+ import { TCloseDropdownFunction } from '../components/dropdown/dropdown.typed';
3
+ import { TMessageFunction, TMessageQuestionFunction } from '../components/message/message.typed';
4
+ import { TPopupFunction, TPopupOpenFunction } from '../components/popup/popup.typed';
5
+
1
6
  export type TUser = {
2
7
  profile_id: number,
3
8
  profile_name: string,
@@ -15,4 +20,17 @@ export type TUser = {
15
20
  shopee_partner_key: string
16
21
  },
17
22
  has_access_orderV1: boolean
23
+ }
24
+ export type THierarchy = {
25
+ evTooglePopup?: TPopupFunction
26
+ evCreateMessage?: TMessageFunction
27
+ evForcePopUp?: TPopupFunction
28
+ isOpenPopup: boolean
29
+ modePopup: string
30
+ pathPopup: string
31
+ actionPopup: string
32
+ detailPopup?: TObject
33
+ evOpenPopup?: TPopupOpenFunction
34
+ evCloseDropdown?: TCloseDropdownFunction
35
+ evCreateMessageQuestion?: TMessageQuestionFunction
18
36
  }