@rio-cloud/rio-uikit 1.5.3 → 1.6.1
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/.DS_Store +0 -0
- package/OnboardingDialog.d.ts +2 -0
- package/OnboardingDialog.js +2 -0
- package/components/applicationLayout/ApplicationLayoutBody.js +1 -1
- package/components/assetTree/AssetTree.js +1 -3
- package/components/assetTree/Tree.d.ts +1 -0
- package/components/assetTree/Tree.js +2 -2
- package/components/dialog/Dialog.d.ts +4 -2
- package/components/dialog/Dialog.js +3 -1
- package/components/dialog/OnboardingDialog.d.ts +44 -0
- package/components/dialog/OnboardingDialog.js +12 -0
- package/components/dialog/SplitDialog.d.ts +7 -1
- package/components/onboarding/OnboardingTip.d.ts +1 -0
- package/components/onboarding/OnboardingTip.js +1 -0
- package/components/selects/Multiselect.js +20 -5
- package/components/selects/MultiselectToggleFilter.d.ts +0 -1
- package/components/selects/MultiselectToggleFilter.js +2 -2
- package/hooks/useOnboarding.d.ts +128 -0
- package/hooks/useOnboarding.js +75 -0
- package/lib/es/OnboardingDialog.d.ts +2 -0
- package/lib/es/OnboardingDialog.js +7 -0
- package/lib/es/components/applicationLayout/ApplicationLayoutBody.js +1 -1
- package/lib/es/components/assetTree/AssetTree.js +1 -3
- package/lib/es/components/assetTree/Tree.d.ts +1 -0
- package/lib/es/components/assetTree/Tree.js +2 -2
- package/lib/es/components/dialog/Dialog.d.ts +4 -2
- package/lib/es/components/dialog/Dialog.js +3 -1
- package/lib/es/components/dialog/OnboardingDialog.d.ts +44 -0
- package/lib/es/components/dialog/OnboardingDialog.js +14 -0
- package/lib/es/components/dialog/SplitDialog.d.ts +7 -1
- package/lib/es/components/onboarding/OnboardingTip.d.ts +1 -0
- package/lib/es/components/onboarding/OnboardingTip.js +1 -0
- package/lib/es/components/selects/Multiselect.js +20 -5
- package/lib/es/components/selects/MultiselectToggleFilter.d.ts +0 -1
- package/lib/es/components/selects/MultiselectToggleFilter.js +2 -2
- package/lib/es/hooks/useOnboarding.d.ts +128 -0
- package/lib/es/hooks/useOnboarding.js +80 -0
- package/lib/es/useOnboarding.d.ts +1 -0
- package/lib/es/useOnboarding.js +4 -0
- package/lib/es/version.json +1 -1
- package/package.json +3 -2
- package/useOnboarding.d.ts +1 -0
- package/useOnboarding.js +1 -0
- package/version.json +1 -1
package/.DS_Store
CHANGED
|
Binary file
|
|
@@ -54,6 +54,6 @@ const ApplicationLayoutBody = forwardRef((props, ref) => {
|
|
|
54
54
|
const innerClasses = classNames('module-content', innerClassName && innerClassName);
|
|
55
55
|
const offsetThreshold = window.innerHeight * 0.1;
|
|
56
56
|
const scrollToTopClasses = classNames('scroll-to-top', offset > offsetThreshold && 'in');
|
|
57
|
-
return (_jsxs(React.Fragment, { children: [_jsxs("div", Object.assign({}, remainingProps, { ref: layoutBodyRef, className: classes }, { children: [_jsxs("div", Object.assign({ className: 'module-content-wrapper' }, { children: [navigation && navigation, banner && banner, _jsx(SmoothScrollbars, Object.assign({ ref: moduleContentRef, slideIn: !forceScrollbar, largeTrack: true, trackOffset: true, className: innerClasses, onScroll: handleScroll }, { children: children }))] })), enableScrollToTop && (_jsx("span", Object.assign({ className: scrollToTopClasses }, { children: _jsx("button", Object.assign({ type: 'button', className: 'btn btn-primary btn-icon-only', onClick: handleToTop }, { children: _jsx("span", { className: 'rioglyph rioglyph-arrow-up' }) })) })))] })), bottomBar && bottomBar] }));
|
|
57
|
+
return (_jsxs(React.Fragment, { children: [_jsxs("div", Object.assign({}, remainingProps, { ref: layoutBodyRef, className: classes }, { children: [_jsxs("div", Object.assign({ className: 'module-content-wrapper' }, { children: [navigation && navigation, banner && banner, _jsx(SmoothScrollbars, Object.assign({ ref: moduleContentRef, slideIn: !forceScrollbar, largeTrack: true, trackOffset: true, className: innerClasses, onScroll: handleScroll }, { children: _jsx("div", Object.assign({ className: 'scrollbar-content' }, { children: children })) }))] })), enableScrollToTop && (_jsx("span", Object.assign({ className: scrollToTopClasses }, { children: _jsx("button", Object.assign({ type: 'button', className: 'btn btn-primary btn-icon-only', onClick: handleToTop }, { children: _jsx("span", { className: 'rioglyph rioglyph-arrow-up' }) })) })))] })), bottomBar && bottomBar] }));
|
|
58
58
|
});
|
|
59
59
|
export default ApplicationLayoutBody;
|
|
@@ -26,9 +26,7 @@ const getCurrentCategoryElement = (children, currentCategoryId) => {
|
|
|
26
26
|
};
|
|
27
27
|
const renderTreesOffscreen = (children, categoryId) => {
|
|
28
28
|
return React.Children.map(children, child => {
|
|
29
|
-
const offscreenClasses = classNames('TreeOffscreenWrapper',
|
|
30
|
-
'flex flex-column', // set this one to flex as well, so children can use flex grow to use remaining space
|
|
31
|
-
child && child.props.id !== categoryId && 'position-offscreen pointer-events-none');
|
|
29
|
+
const offscreenClasses = classNames('TreeOffscreenWrapper', child && child.props.id !== categoryId && 'position-offscreen pointer-events-none');
|
|
32
30
|
return _jsx("div", Object.assign({ className: offscreenClasses }, { children: child }));
|
|
33
31
|
});
|
|
34
32
|
};
|
|
@@ -38,7 +38,7 @@ const filterProps = omit([
|
|
|
38
38
|
]);
|
|
39
39
|
const customCompare = (prevProps, nextProps) => isEqual(filterProps(prevProps), filterProps(nextProps));
|
|
40
40
|
const Tree = React.memo((props) => {
|
|
41
|
-
const { groups = [], items = [], selectedGroups = [], selectedItems = [], onSelectionChange = noop, hasMultiselect = true, showRadioButtons = false, hideSearch = false, hideTreeHead, summary, hideSummary = false, search, searchPlaceholder = 'Type here to filter by name', onSearchChange = noop, className, scrollHeight, expandedGroups, onExpandGroupsChange = noop, showEmptyGroups = true, treeOptions = [], treeOptionsTooltip, disableAnimation = false } = props, remainingProps = __rest(props, ["groups", "items", "selectedGroups", "selectedItems", "onSelectionChange", "hasMultiselect", "showRadioButtons", "hideSearch", "hideTreeHead", "summary", "hideSummary", "search", "searchPlaceholder", "onSearchChange", "className", "scrollHeight", "expandedGroups", "onExpandGroupsChange", "showEmptyGroups", "treeOptions", "treeOptionsTooltip", "disableAnimation"]);
|
|
41
|
+
const { groups = [], items = [], selectedGroups = [], selectedItems = [], onSelectionChange = noop, hasMultiselect = true, showRadioButtons = false, hideSearch = false, hideTreeHead, treeHeaderContent, summary, hideSummary = false, search, searchPlaceholder = 'Type here to filter by name', onSearchChange = noop, className, scrollHeight, expandedGroups, onExpandGroupsChange = noop, showEmptyGroups = true, treeOptions = [], treeOptionsTooltip, disableAnimation = false } = props, remainingProps = __rest(props, ["groups", "items", "selectedGroups", "selectedItems", "onSelectionChange", "hasMultiselect", "showRadioButtons", "hideSearch", "hideTreeHead", "treeHeaderContent", "summary", "hideSummary", "search", "searchPlaceholder", "onSearchChange", "className", "scrollHeight", "expandedGroups", "onExpandGroupsChange", "showEmptyGroups", "treeOptions", "treeOptionsTooltip", "disableAnimation"]);
|
|
42
42
|
const [state, dispatch] = useReducer(treeReducer, {
|
|
43
43
|
groupedItems: [],
|
|
44
44
|
flatItems: [],
|
|
@@ -273,7 +273,7 @@ const Tree = React.memo((props) => {
|
|
|
273
273
|
const showSearch = !hideSearch;
|
|
274
274
|
const showSummary = !hideSummary;
|
|
275
275
|
const hasCustomSearch = !isNil(search);
|
|
276
|
-
return (_jsxs("div", Object.assign({}, remainingProps, { className: treeClassNames, ref: treeRef }, { children: [_jsxs("div", Object.assign({ className: 'TreeHeader' }, { children: [showSearch && !hasCustomSearch && (_jsx(TreeSearch, { value: state.searchValue, onChange: handleSearchChange, placeholder: searchPlaceholder })), hasCustomSearch && search, showTreeHead && (_jsxs("div", Object.assign({ className: treeHeadClasses }, { children: [showSelectAll && (_jsx("div", Object.assign({ className: 'border border-right-only hidden-empty padding-right-10 margin-right-2' }, { children: _jsx(TreeSelectAll, { isChecked: state.allChecked, isEnabled: hasMultiselect, isIndeterminate: isIndeterminate, onSelect: handleSelectAll }) }))), _jsx("div", Object.assign({ className: 'display-flex justify-content-between align-items-start width-100pct' }, { children: showSummary
|
|
276
|
+
return (_jsxs("div", Object.assign({}, remainingProps, { className: treeClassNames, ref: treeRef }, { children: [_jsxs("div", Object.assign({ className: 'TreeHeader' }, { children: [treeHeaderContent, showSearch && !hasCustomSearch && (_jsx(TreeSearch, { value: state.searchValue, onChange: handleSearchChange, placeholder: searchPlaceholder })), hasCustomSearch && search, showTreeHead && (_jsxs("div", Object.assign({ className: treeHeadClasses }, { children: [showSelectAll && (_jsx("div", Object.assign({ className: 'border border-right-only hidden-empty padding-right-10 margin-right-2' }, { children: _jsx(TreeSelectAll, { isChecked: state.allChecked, isEnabled: hasMultiselect, isIndeterminate: isIndeterminate, onSelect: handleSelectAll }) }))), _jsx("div", Object.assign({ className: 'display-flex justify-content-between align-items-start width-100pct' }, { children: showSummary
|
|
277
277
|
? summary || (_jsx(TreeSummary, { children: map((typeCounter) => (_jsx(TypeCounter, { type: typeCounter, icon: `${typeCounter}`, value: state.assetCounts[typeCounter], onClick: handleFilterByType, isActive: state.typeFilter.includes(typeCounter), hasFilter: isFilterActive, enableActivity: enableActivity }, typeCounter)))(state.visibleTypeCounters) }))
|
|
278
278
|
: null })), _jsx(TreeOptions, { treeOptions: treeOptions, treeOptionsTooltip: treeOptionsTooltip })] })))] })), _jsx(TreeRoot, Object.assign({ maxHeight: scrollHeight, disableAnimation: disableAnimation }, { children: content }))] })));
|
|
279
279
|
}, customCompare);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { type PropsWithChildren } from 'react';
|
|
2
|
-
export type DialogSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full' | 'fullwidth' | 'fullheight' | 'fullscreen';
|
|
2
|
+
export type DialogSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full' | 'fullwidth' | 'fullheight' | 'fullheight-lg' | 'fullheight-xl' | 'fullscreen';
|
|
3
3
|
export type BaseDialogProps = {
|
|
4
4
|
/**
|
|
5
5
|
* Opens the dialog when set to `true`.
|
|
@@ -30,7 +30,7 @@ export type BaseDialogProps = {
|
|
|
30
30
|
*
|
|
31
31
|
* By default, the dialog has a medium size, and thus it can be omitted.
|
|
32
32
|
*
|
|
33
|
-
* Possible values are: `xs`, `sm`, `lg` `xl` `fullwidth` `fullheight` `fullscreen`
|
|
33
|
+
* Possible values are: `xs`, `sm`, `lg` `xl` `fullwidth` `fullheight` 'fullheight-lg' 'fullheight-xl' `fullscreen`
|
|
34
34
|
*
|
|
35
35
|
* @default 'md'
|
|
36
36
|
*/
|
|
@@ -109,6 +109,8 @@ declare const Dialog: {
|
|
|
109
109
|
SIZE_FULL: "full";
|
|
110
110
|
SIZE_FULL_WIDTH: "fullwidth";
|
|
111
111
|
SIZE_FULL_HEIGHT: "fullheight";
|
|
112
|
+
SIZE_FULL_HEIGHT_LG: "fullheight-lg";
|
|
113
|
+
SIZE_FULL_HEIGHT_XL: "fullheight-xl";
|
|
112
114
|
SIZE_FULL_SCREEN: "fullscreen";
|
|
113
115
|
};
|
|
114
116
|
export default Dialog;
|
|
@@ -85,7 +85,7 @@ const Dialog = (props) => {
|
|
|
85
85
|
const modalClasses = classNames('modal', 'show', className);
|
|
86
86
|
const isSmallestDialog = bsSize === 'xs';
|
|
87
87
|
const hasChildren = !!children;
|
|
88
|
-
const modalDialogClasses = classNames(MODAL_DIALOG_CLASS, useOverflow && 'modal-overflow', bsSize === 'xs' && 'modal-xs', bsSize === 'sm' && 'modal-sm', bsSize === 'lg' && 'modal-lg', bsSize === 'xl' && 'modal-xl', bsSize === 'full' && 'modal-full-width', bsSize === 'fullwidth' && 'modal-full-width', bsSize === 'fullheight' && 'modal-full-height', bsSize === 'fullscreen' && 'modal-fullscreen');
|
|
88
|
+
const modalDialogClasses = classNames(MODAL_DIALOG_CLASS, useOverflow && 'modal-overflow', bsSize === 'xs' && 'modal-xs', bsSize === 'sm' && 'modal-sm', bsSize === 'lg' && 'modal-lg', bsSize === 'xl' && 'modal-xl', bsSize === 'full' && 'modal-full-width', bsSize === 'fullwidth' && 'modal-full-width', bsSize === 'fullheight' && 'modal-full-height', bsSize === 'fullheight-lg' && 'modal-full-height modal-lg', bsSize === 'fullheight-xl' && 'modal-full-height modal-xl', bsSize === 'fullscreen' && 'modal-fullscreen');
|
|
89
89
|
const spring = {
|
|
90
90
|
type: 'spring',
|
|
91
91
|
damping: 33,
|
|
@@ -124,5 +124,7 @@ Dialog.SIZE_XL = 'xl';
|
|
|
124
124
|
Dialog.SIZE_FULL = 'full';
|
|
125
125
|
Dialog.SIZE_FULL_WIDTH = 'fullwidth';
|
|
126
126
|
Dialog.SIZE_FULL_HEIGHT = 'fullheight';
|
|
127
|
+
Dialog.SIZE_FULL_HEIGHT_LG = 'fullheight-lg';
|
|
128
|
+
Dialog.SIZE_FULL_HEIGHT_XL = 'fullheight-xl';
|
|
127
129
|
Dialog.SIZE_FULL_SCREEN = 'fullscreen';
|
|
128
130
|
export default Dialog;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type BaseDialogProps } from './Dialog';
|
|
3
|
+
export type OnboardingDialogProps = Pick<BaseDialogProps, 'show' | 'onClose' | 'className'> & {
|
|
4
|
+
/**
|
|
5
|
+
* The source URL of the image to be displayed in the dialog.
|
|
6
|
+
*/
|
|
7
|
+
imageSrc: string;
|
|
8
|
+
/**
|
|
9
|
+
* Alternative text for the image, used for accessibility.
|
|
10
|
+
*/
|
|
11
|
+
imageAlt?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The title of the dialog. A welcome message.
|
|
14
|
+
*/
|
|
15
|
+
title?: string | React.ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* A short description of the service and it's onboarding.
|
|
18
|
+
*/
|
|
19
|
+
description?: string | React.ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* Hint text for restarting the onboarding process.
|
|
22
|
+
*/
|
|
23
|
+
onboardingRestartHint?: string | React.ReactNode;
|
|
24
|
+
/**
|
|
25
|
+
* Detailed description for restarting the onboarding process.
|
|
26
|
+
*/
|
|
27
|
+
onboardingRestartDescription?: string | React.ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* Text for the skip button
|
|
30
|
+
*/
|
|
31
|
+
skipButtonText?: string | React.ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* Text for the button to start the tour
|
|
34
|
+
*/
|
|
35
|
+
tourButtonText?: string | React.ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Callback function to be called when the tour starts.
|
|
38
|
+
*
|
|
39
|
+
* @returns
|
|
40
|
+
*/
|
|
41
|
+
onStartTour: () => void;
|
|
42
|
+
};
|
|
43
|
+
declare const OnboardingDialog: (props: OnboardingDialogProps) => import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
export default OnboardingDialog;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { __rest } from "tslib";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
// @ts-ignore-next-line importsNotUsedAsValues
|
|
4
|
+
import 'react';
|
|
5
|
+
import Dialog from './Dialog';
|
|
6
|
+
import Button from '../button/Button';
|
|
7
|
+
const OnboardingDialog = (props) => {
|
|
8
|
+
const { className, show, onClose, onStartTour, imageSrc, imageAlt, title = 'Welcome to the RIO UIKIT!', description = (_jsxs("span", { children: [_jsx("span", Object.assign({ className: 'text-medium' }, { children: "Get started with a quick tour to explore UIKIT and discover its key features." })), ' ', _jsx("span", { children: "It's the easiest way to get familiar with the service and make the most of your experience." })] })), onboardingRestartHint = 'Not ready to start now?', onboardingRestartDescription = 'You can always start the onboarding tour later from the service info option in the application header.', skipButtonText = 'Skip', tourButtonText = 'Start the tour' } = props, remainingProps = __rest(props, ["className", "show", "onClose", "onStartTour", "imageSrc", "imageAlt", "title", "description", "onboardingRestartHint", "onboardingRestartDescription", "skipButtonText", "tourButtonText"]);
|
|
9
|
+
const dialogClassName = `${className ? className : ''} onboarding-dialog`;
|
|
10
|
+
return (_jsx(Dialog, Object.assign({}, remainingProps, { show: show, bsSize: 'sm', onClose: onClose, onEsc: onClose, bodyClassName: 'padding-0', className: dialogClassName, showCloseButton: false, body: _jsxs("div", { children: [_jsx("div", { children: _jsx("img", { className: 'img-responsive rounded-top-right-extra-large rounded-top-left-extra-large', src: imageSrc, alt: imageAlt }) }), _jsxs("div", Object.assign({ className: 'position-relative padding-20 margin-top-10 margin-x-20' }, { children: [_jsx("div", Object.assign({ className: 'margin-bottom-15 margin-right-20 text-color-darkest' }, { children: _jsx("div", Object.assign({ className: 'text-size-h3 text-medium' }, { children: title })) })), _jsx("div", Object.assign({ className: 'text-color-darker' }, { children: _jsx("p", Object.assign({ className: 'text-size-16' }, { children: description })) })), _jsxs("div", Object.assign({ className: 'text-color-darker' }, { children: [_jsx("div", Object.assign({ className: 'margin-top-25 text-medium' }, { children: onboardingRestartHint })), _jsx("p", Object.assign({ className: 'text-color-darker padding-bottom-15 margin-top-5' }, { children: onboardingRestartDescription }))] })), _jsxs("div", Object.assign({ className: 'btn-toolbar justify-content-end margin-y-20' }, { children: [_jsx(Button, Object.assign({ bsStyle: Button.MUTED, onClick: onClose }, { children: skipButtonText })), _jsx(Button, Object.assign({ bsStyle: Button.PRIMARY, onClick: onStartTour }, { children: tourButtonText }))] }))] }))] }) })));
|
|
11
|
+
};
|
|
12
|
+
export default OnboardingDialog;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type BaseDialogProps } from './Dialog';
|
|
3
|
-
export type SplitDialogProps = BaseDialogProps & {
|
|
3
|
+
export type SplitDialogProps = Omit<BaseDialogProps, 'useOverflow'> & {
|
|
4
4
|
/**
|
|
5
5
|
* The content to be shown on the left side.
|
|
6
6
|
*/
|
|
@@ -31,6 +31,12 @@ export type SplitDialogProps = BaseDialogProps & {
|
|
|
31
31
|
* If the function returns `false`, the dialog will not be closed
|
|
32
32
|
*/
|
|
33
33
|
onCloseValidation?: () => boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Enables the modal body to overflow and use inline scrolling if needed.
|
|
36
|
+
*
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
useOverflow?: boolean;
|
|
34
40
|
};
|
|
35
41
|
declare const SplitDialog: (props: SplitDialogProps) => import("react/jsx-runtime").JSX.Element;
|
|
36
42
|
export default SplitDialog;
|
|
@@ -10,6 +10,7 @@ import { TEXT_ALIGNMENT } from '../../values/TextAlignment';
|
|
|
10
10
|
import { PLACEMENT } from '../../values/Placement';
|
|
11
11
|
const OnboardingTip = (props) => {
|
|
12
12
|
const { id, show = false, placement = PLACEMENT.BOTTOM, title, content, onHide = noop, children, textAlignment = TEXT_ALIGNMENT.LEFT, useInDialog = false, showCloseIcon = true, clickflow = false, previousButton, nextButton, className = '', width, preventOverflow = true, popperConfig, } = props;
|
|
13
|
+
// DEPRECATED in favor of driver.js - see useOnboardingTour hook
|
|
13
14
|
const clickFlowWithButtons = (_jsxs("div", Object.assign({ className: 'display-flex flex-column gap-25' }, { children: [_jsx("div", { children: content }), _jsxs("div", Object.assign({ className: `btn-toolbar justify-content-between ${showCloseIcon ? 'margin-right--25' : ''}` }, { children: [previousButton ? (_jsxs("div", Object.assign({ className: 'position-relative' }, { children: [_jsx("div", { className: 'bg-black opacity-10 rounded display-absolute inset-0' }), _jsxs(Button, Object.assign({ bsStyle: Button.SECONDARY, variant: Button.VARIANT_OUTLINE, className: 'border-none text-color-white', onClick: previousButton.onClick }, { children: [previousButton.iconName && (_jsx("span", { className: `rioglyph ${previousButton.iconName} text-color-white` })), _jsx("span", { children: previousButton.text })] }))] }))) : (_jsx("div", {})), nextButton ? (_jsxs(Button, Object.assign({ bsStyle: Button.SECONDARY, variant: Button.VARIANT_OUTLINE, className: 'border-color-white text-color-white', onClick: nextButton.onClick, iconRight: true }, { children: [_jsx("span", { children: nextButton.text }), nextButton.iconName && _jsx("span", { className: `rioglyph ${nextButton.iconName} text-color-white` })] }))) : (_jsx("div", {}))] }))] })));
|
|
14
15
|
const tooltipWrapperClasses = classNames(useInDialog && 'z-index-max', className && className, clickflow && 'onboarding-clickflow');
|
|
15
16
|
const overlay = (_jsx(Tooltip, Object.assign({ className: tooltipWrapperClasses, tooltipStyle: Tooltip.STYLE_ONBOARDING, id: id, onClick: onHide, width: width, textAlignment: textAlignment, allowOnTouch: true }, { children: _jsxs("div", Object.assign({ className: 'display-flex' }, { children: [_jsxs("div", Object.assign({ className: 'display-flex flex-column flex-1-1' }, { children: [title && _jsx("div", Object.assign({ className: 'tooltip-title' }, { children: title })), content && _jsx("div", Object.assign({ className: 'tooltip-content' }, { children: clickflow ? clickFlowWithButtons : content }))] })), showCloseIcon && _jsx("span", { className: 'tooltip-close rioglyph rioglyph-remove' })] })) })));
|
|
@@ -27,7 +27,12 @@ const Multiselect = (props) => {
|
|
|
27
27
|
const [keyboardUsed, setKeyboardUsed] = useState(false);
|
|
28
28
|
const refToggle = useRef(null);
|
|
29
29
|
const refMultiSelectWrapper = useRef(null);
|
|
30
|
-
const ref = useClickOutside(
|
|
30
|
+
const ref = useClickOutside(event => {
|
|
31
|
+
// Check if the click is truly outside the multiselect wrapper
|
|
32
|
+
if (refMultiSelectWrapper.current && !refMultiSelectWrapper.current.contains(event.target)) {
|
|
33
|
+
closeMenu();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
31
36
|
const mergedSelectRefs = useMergeRefs(refMultiSelectWrapper, ref);
|
|
32
37
|
const updateSelectedItems = (optionsToCheck, selectedItem) => {
|
|
33
38
|
if (selectedItem) {
|
|
@@ -61,7 +66,7 @@ const Multiselect = (props) => {
|
|
|
61
66
|
const updateDOMValues = (updatedItemDOMValues = []) => {
|
|
62
67
|
setItemDOMValues(updatedItemDOMValues);
|
|
63
68
|
};
|
|
64
|
-
const renderFilter = () => (_jsx(MultiselectToggleFilter, { isActive: isFilterActive || !!filterValue, selectedItemIds: selectedItemIds, filterValue: filterValue, onFilterChange: handleFilterChange
|
|
69
|
+
const renderFilter = () => (_jsx(MultiselectToggleFilter, { isActive: isFilterActive || !!filterValue, selectedItemIds: selectedItemIds, filterValue: filterValue, onFilterChange: handleFilterChange }));
|
|
65
70
|
const renderSelection = () => {
|
|
66
71
|
if (counterMessage || renderCounterMessage) {
|
|
67
72
|
return (_jsx(MultiselectToggleCounter, { selectedAmount: selectedItemIds.length, counterMessage: counterMessage, customRenderer: renderCounterMessage }));
|
|
@@ -89,12 +94,18 @@ const Multiselect = (props) => {
|
|
|
89
94
|
event.preventDefault();
|
|
90
95
|
const targetFilterValue = event.currentTarget.value;
|
|
91
96
|
const optionsFiltered = filterOptions(itemDOMValues, targetFilterValue, options);
|
|
92
|
-
|
|
97
|
+
if (!isOpen) {
|
|
98
|
+
// Ensure dropdown stays open when filtering
|
|
99
|
+
setIsOpen(true);
|
|
100
|
+
}
|
|
93
101
|
const newFocusedItemIndex = optionsFiltered.length > 0 ? 0 : -1;
|
|
94
102
|
setIsFilterActive(true);
|
|
95
103
|
setFilterValue(targetFilterValue);
|
|
96
104
|
setFilteredOptions(optionsFiltered);
|
|
97
|
-
|
|
105
|
+
if (optionsFiltered.length > 0) {
|
|
106
|
+
// Only set keyboard used if there are filtered options
|
|
107
|
+
setKeyboardUsed(true);
|
|
108
|
+
}
|
|
98
109
|
setFocusedItemIndex(newFocusedItemIndex);
|
|
99
110
|
};
|
|
100
111
|
const onOptionChange = (currentSelectedItem) => {
|
|
@@ -103,6 +114,7 @@ const Multiselect = (props) => {
|
|
|
103
114
|
return;
|
|
104
115
|
}
|
|
105
116
|
const updatedSelectedItems = currentSelectedItem ? updateSelection(currentSelectedItem.id) : selectedItemIds;
|
|
117
|
+
// Reset everything after an option was selected. Remove the filter again.
|
|
106
118
|
setSelectedItemIds(updatedSelectedItems);
|
|
107
119
|
setIsFilterActive(false);
|
|
108
120
|
setFilterValue('');
|
|
@@ -157,7 +169,7 @@ const Multiselect = (props) => {
|
|
|
157
169
|
// type submit by default in HTML. In order to differentiate between real click and a synthetic event
|
|
158
170
|
// caused by they keyboard, use the event details. A synthetic event is always 0.
|
|
159
171
|
const isKeyboardUsed = event.detail === 0;
|
|
160
|
-
setIsOpen(!
|
|
172
|
+
setIsOpen(prevValue => !prevValue);
|
|
161
173
|
setKeyboardUsed(isKeyboardUsed);
|
|
162
174
|
};
|
|
163
175
|
const closeMenu = () => {
|
|
@@ -165,6 +177,9 @@ const Multiselect = (props) => {
|
|
|
165
177
|
if (isOpen) {
|
|
166
178
|
setIsOpen(false);
|
|
167
179
|
setIsFilterActive(false);
|
|
180
|
+
setFilterValue('');
|
|
181
|
+
setFilteredOptions(options);
|
|
182
|
+
setFocusedItemIndex(DEFAULT_FOCUSED_ITEM_INDEX);
|
|
168
183
|
setKeyboardUsed(false);
|
|
169
184
|
(_a = refToggle === null || refToggle === void 0 ? void 0 : refToggle.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
170
185
|
}
|
|
@@ -3,7 +3,6 @@ export type MultiselectToggleFilterProps = {
|
|
|
3
3
|
isActive: boolean;
|
|
4
4
|
selectedItemIds?: string[];
|
|
5
5
|
filterValue: string;
|
|
6
|
-
multiline: boolean;
|
|
7
6
|
onFilterChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
8
7
|
};
|
|
9
8
|
declare const MultiselectToggleFilter: (props: MultiselectToggleFilterProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,10 +4,10 @@ import 'react';
|
|
|
4
4
|
import classNames from 'classnames';
|
|
5
5
|
import isEmpty from 'lodash/fp/isEmpty';
|
|
6
6
|
const MultiselectToggleFilter = (props) => {
|
|
7
|
-
const { isActive, selectedItemIds, filterValue,
|
|
7
|
+
const { isActive, selectedItemIds, filterValue, onFilterChange } = props;
|
|
8
8
|
const inputClasses = classNames('multiselect-filter-input', isEmpty(selectedItemIds) && 'multiselect-filter-input-placeholder', isActive && 'multiselect-filter-input-active');
|
|
9
9
|
return (_jsx("input", { type: 'text', role: 'searchbox', className: inputClasses,
|
|
10
10
|
// biome-ignore lint/a11y/noAutofocus: autofocus is intentionally set to allow instant typing to filter
|
|
11
|
-
autoFocus: true, onChange: onFilterChange,
|
|
11
|
+
autoFocus: true, onChange: onFilterChange, value: filterValue }));
|
|
12
12
|
};
|
|
13
13
|
export default MultiselectToggleFilter;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { type PopoverDOM as DriverPopoverDOM, type State as DriverState, type AllowedButtons } from 'driver.js';
|
|
2
|
+
import 'driver.js/dist/driver.css';
|
|
3
|
+
export type PopoverDOM = DriverPopoverDOM;
|
|
4
|
+
export type OnboardingState = DriverState;
|
|
5
|
+
export type OnboardingStep = {
|
|
6
|
+
element?: string | Element;
|
|
7
|
+
onHighlightStarted?: (element: Element | undefined, step: OnboardingStep, opts: any) => void;
|
|
8
|
+
onHighlighted?: (element: Element | undefined, step: OnboardingStep, opts: any) => void;
|
|
9
|
+
onDeselected?: (element: Element | undefined, step: OnboardingStep, opts: any) => void;
|
|
10
|
+
popover?: {
|
|
11
|
+
title?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
side?: 'top' | 'right' | 'bottom' | 'left' | 'over';
|
|
14
|
+
align?: 'start' | 'center' | 'end';
|
|
15
|
+
showButtons?: ('next' | 'previous' | 'close')[];
|
|
16
|
+
disableButtons?: ('next' | 'previous' | 'close')[];
|
|
17
|
+
popoverClass?: string;
|
|
18
|
+
doneBtnText?: string;
|
|
19
|
+
nextBtnText?: string;
|
|
20
|
+
prevBtnText?: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
type OnboardingOptions = {
|
|
24
|
+
/**
|
|
25
|
+
* Array of steps to highlight. You should pass this when you want to setup a product tour.
|
|
26
|
+
*/
|
|
27
|
+
steps?: OnboardingStep[];
|
|
28
|
+
/**
|
|
29
|
+
* Text for the previous button in the onboarding popover.
|
|
30
|
+
*/
|
|
31
|
+
prevBtnText?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Text for the next button in the onboarding popover.
|
|
34
|
+
*/
|
|
35
|
+
nextBtnText?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Text for the done button in the onboarding popover.
|
|
38
|
+
*/
|
|
39
|
+
doneBtnText?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Indicates whether to show progress in the onboarding popover.
|
|
42
|
+
*
|
|
43
|
+
* @default true
|
|
44
|
+
*/
|
|
45
|
+
showProgress?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Whether to allow closing the popover by clicking on the backdrop.
|
|
48
|
+
* This should be disabled during the tour to ensure th user completes the tour.
|
|
49
|
+
* But can be enabled on the last step.
|
|
50
|
+
*
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
53
|
+
allowClose?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Distance between the highlighted element and the cutout.
|
|
56
|
+
*
|
|
57
|
+
* @default 10
|
|
58
|
+
*/
|
|
59
|
+
stagePadding?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Radius of the cutout around the highlighted element.
|
|
62
|
+
*
|
|
63
|
+
* @default 5
|
|
64
|
+
*/
|
|
65
|
+
stageRadius?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Whether to allow keyboard navigation.
|
|
68
|
+
*
|
|
69
|
+
* @default true
|
|
70
|
+
*/
|
|
71
|
+
allowKeyboardControl?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Array of buttons to show in the popover. Defaults to ["next", "previous", "close"]
|
|
74
|
+
* for product tours and [] for single element highlighting.
|
|
75
|
+
*/
|
|
76
|
+
showButtons?: AllowedButtons[];
|
|
77
|
+
/**
|
|
78
|
+
* Array of buttons to disable. This is useful when you want to show some of the buttons, but disable some of them.
|
|
79
|
+
*/
|
|
80
|
+
disableButtons?: AllowedButtons[];
|
|
81
|
+
/**
|
|
82
|
+
* Option to disable the backdrop. Note, the backdrop element is still there but with 100% opacity
|
|
83
|
+
* and the close on the backdrop is disabled.
|
|
84
|
+
*
|
|
85
|
+
* @default false
|
|
86
|
+
*/
|
|
87
|
+
noBackdrop?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Additional classes set on the popover itself.
|
|
90
|
+
*/
|
|
91
|
+
popoverClass?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Callback triggered when the onboarding popover renders.
|
|
94
|
+
*
|
|
95
|
+
* @param popover
|
|
96
|
+
* @param state
|
|
97
|
+
* @returns
|
|
98
|
+
*/
|
|
99
|
+
onPopoverRender?: (popover: PopoverDOM, state: OnboardingState) => void;
|
|
100
|
+
};
|
|
101
|
+
export declare const useOnboardingTour: (params: OnboardingOptions) => {
|
|
102
|
+
run: () => void;
|
|
103
|
+
destroy: () => void;
|
|
104
|
+
onboardingState: () => DriverState;
|
|
105
|
+
refresh: () => void;
|
|
106
|
+
isActive: () => boolean;
|
|
107
|
+
moveNext: () => void;
|
|
108
|
+
movePrevious: () => void;
|
|
109
|
+
moveTo: (stepNumber: number) => void;
|
|
110
|
+
hasNextStep: () => false | import("driver.js").DriveStep;
|
|
111
|
+
hasPreviousStep: () => false | import("driver.js").DriveStep;
|
|
112
|
+
isFirstStep: () => boolean;
|
|
113
|
+
isLastStep: () => boolean;
|
|
114
|
+
getActiveIndex: () => number | undefined;
|
|
115
|
+
};
|
|
116
|
+
type OnboardingTipOptions = {
|
|
117
|
+
allowClose?: boolean;
|
|
118
|
+
stagePadding?: number;
|
|
119
|
+
stageRadius?: number;
|
|
120
|
+
noBackdrop?: boolean;
|
|
121
|
+
};
|
|
122
|
+
export declare const useOnboardingTip: (options?: OnboardingTipOptions) => {
|
|
123
|
+
highlight: (step: OnboardingStep) => void;
|
|
124
|
+
destroy: () => void;
|
|
125
|
+
refresh: () => void;
|
|
126
|
+
isActive: () => boolean;
|
|
127
|
+
};
|
|
128
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { driver, } from 'driver.js';
|
|
2
|
+
import 'driver.js/dist/driver.css';
|
|
3
|
+
export const useOnboardingTour = (params) => {
|
|
4
|
+
const { steps, prevBtnText = 'Previous', nextBtnText = 'Next', doneBtnText = 'Done', showProgress = true, allowClose = false, stagePadding = 10, stageRadius = 5, allowKeyboardControl = true, showButtons, disableButtons, popoverClass, noBackdrop = false, onPopoverRender = () => { }, } = params;
|
|
5
|
+
const handlePopoverRender = (popover, options) => {
|
|
6
|
+
onPopoverRender(popover, options.state);
|
|
7
|
+
};
|
|
8
|
+
const isSingleStep = !steps || steps.length === 1;
|
|
9
|
+
// This configuration is applied to all steps.
|
|
10
|
+
// Driver.js allows also for individual customization for single steps too.
|
|
11
|
+
const driverInstance = driver({
|
|
12
|
+
steps,
|
|
13
|
+
prevBtnText,
|
|
14
|
+
nextBtnText,
|
|
15
|
+
doneBtnText,
|
|
16
|
+
showProgress: isSingleStep ? false : showProgress,
|
|
17
|
+
showButtons: isSingleStep ? ['close'] : showButtons,
|
|
18
|
+
allowClose,
|
|
19
|
+
overlayOpacity: noBackdrop ? 0 : 0.5,
|
|
20
|
+
disableButtons,
|
|
21
|
+
progressText: '{{current}} / {{total}}',
|
|
22
|
+
overlayColor: 'var(--color-black)',
|
|
23
|
+
stagePadding,
|
|
24
|
+
stageRadius,
|
|
25
|
+
allowKeyboardControl,
|
|
26
|
+
popoverClass,
|
|
27
|
+
onPopoverRender: handlePopoverRender,
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
// Use for onboarding tours. Pass in all steps as "steps" parameter for initial configuration.
|
|
31
|
+
run: () => driverInstance.drive(),
|
|
32
|
+
// Use for single element highlights -> see useOnboardingTip
|
|
33
|
+
// highlight: (step: OnboardingStep) => driverInstance.highlight(step),
|
|
34
|
+
// Cleanup the onboarding
|
|
35
|
+
destroy: () => driverInstance.destroy(),
|
|
36
|
+
// Gets the current state of the onboarding with the current step
|
|
37
|
+
onboardingState: () => driverInstance.getState(),
|
|
38
|
+
// Recalculate and redraw the highlight
|
|
39
|
+
refresh: () => driverInstance.refresh(),
|
|
40
|
+
// Is the tour or highlight currently active
|
|
41
|
+
isActive: () => driverInstance.isActive(),
|
|
42
|
+
// Move to the next step
|
|
43
|
+
moveNext: () => driverInstance.moveNext(),
|
|
44
|
+
// Move to the previous step
|
|
45
|
+
movePrevious: () => driverInstance.movePrevious(),
|
|
46
|
+
// Move to the step 4
|
|
47
|
+
moveTo: (stepNumber) => driverInstance.moveTo(stepNumber),
|
|
48
|
+
// Is there a next step
|
|
49
|
+
hasNextStep: () => driverInstance.hasNextStep(),
|
|
50
|
+
// Is there a previous step
|
|
51
|
+
hasPreviousStep: () => driverInstance.hasPreviousStep(),
|
|
52
|
+
// Is the current step the first step
|
|
53
|
+
isFirstStep: () => driverInstance.isFirstStep(),
|
|
54
|
+
// Is the current step the last step
|
|
55
|
+
isLastStep: () => driverInstance.isLastStep(),
|
|
56
|
+
// Gets the active step index
|
|
57
|
+
getActiveIndex: () => driverInstance.getActiveIndex(),
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export const useOnboardingTip = (options) => {
|
|
61
|
+
const { allowClose = true, stagePadding = 0, stageRadius = 5, noBackdrop = true } = options || {};
|
|
62
|
+
const driverInstance = driver({
|
|
63
|
+
allowClose,
|
|
64
|
+
stagePadding,
|
|
65
|
+
stageRadius,
|
|
66
|
+
overlayOpacity: noBackdrop ? 0 : 0.5,
|
|
67
|
+
overlayColor: undefined,
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
highlight: (step) => driverInstance.highlight(step),
|
|
71
|
+
destroy: () => driverInstance.destroy(),
|
|
72
|
+
refresh: () => driverInstance.refresh(),
|
|
73
|
+
isActive: () => driverInstance.isActive(),
|
|
74
|
+
};
|
|
75
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
var OnboardingDialog_1 = require("./components/dialog/OnboardingDialog");
|
|
6
|
+
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return tslib_1.__importDefault(OnboardingDialog_1).default; } });
|
|
7
|
+
tslib_1.__exportStar(require("./components/dialog/OnboardingDialog"), exports);
|
|
@@ -56,6 +56,6 @@ const ApplicationLayoutBody = (0, react_1.forwardRef)((props, ref) => {
|
|
|
56
56
|
const innerClasses = (0, classnames_1.default)('module-content', innerClassName && innerClassName);
|
|
57
57
|
const offsetThreshold = window.innerHeight * 0.1;
|
|
58
58
|
const scrollToTopClasses = (0, classnames_1.default)('scroll-to-top', offset > offsetThreshold && 'in');
|
|
59
|
-
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({}, remainingProps, { ref: layoutBodyRef, className: classes }, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'module-content-wrapper' }, { children: [navigation && navigation, banner && banner, (0, jsx_runtime_1.jsx)(SmoothScrollbars_1.default, Object.assign({ ref: moduleContentRef, slideIn: !forceScrollbar, largeTrack: true, trackOffset: true, className: innerClasses, onScroll: handleScroll }, { children: children }))] })), enableScrollToTop && ((0, jsx_runtime_1.jsx)("span", Object.assign({ className: scrollToTopClasses }, { children: (0, jsx_runtime_1.jsx)("button", Object.assign({ type: 'button', className: 'btn btn-primary btn-icon-only', onClick: handleToTop }, { children: (0, jsx_runtime_1.jsx)("span", { className: 'rioglyph rioglyph-arrow-up' }) })) })))] })), bottomBar && bottomBar] }));
|
|
59
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({}, remainingProps, { ref: layoutBodyRef, className: classes }, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'module-content-wrapper' }, { children: [navigation && navigation, banner && banner, (0, jsx_runtime_1.jsx)(SmoothScrollbars_1.default, Object.assign({ ref: moduleContentRef, slideIn: !forceScrollbar, largeTrack: true, trackOffset: true, className: innerClasses, onScroll: handleScroll }, { children: (0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'scrollbar-content' }, { children: children })) }))] })), enableScrollToTop && ((0, jsx_runtime_1.jsx)("span", Object.assign({ className: scrollToTopClasses }, { children: (0, jsx_runtime_1.jsx)("button", Object.assign({ type: 'button', className: 'btn btn-primary btn-icon-only', onClick: handleToTop }, { children: (0, jsx_runtime_1.jsx)("span", { className: 'rioglyph rioglyph-arrow-up' }) })) })))] })), bottomBar && bottomBar] }));
|
|
60
60
|
});
|
|
61
61
|
exports.default = ApplicationLayoutBody;
|
|
@@ -28,9 +28,7 @@ const getCurrentCategoryElement = (children, currentCategoryId) => {
|
|
|
28
28
|
};
|
|
29
29
|
const renderTreesOffscreen = (children, categoryId) => {
|
|
30
30
|
return react_1.default.Children.map(children, child => {
|
|
31
|
-
const offscreenClasses = (0, classnames_1.default)('TreeOffscreenWrapper',
|
|
32
|
-
'flex flex-column', // set this one to flex as well, so children can use flex grow to use remaining space
|
|
33
|
-
child && child.props.id !== categoryId && 'position-offscreen pointer-events-none');
|
|
31
|
+
const offscreenClasses = (0, classnames_1.default)('TreeOffscreenWrapper', child && child.props.id !== categoryId && 'position-offscreen pointer-events-none');
|
|
34
32
|
return (0, jsx_runtime_1.jsx)("div", Object.assign({ className: offscreenClasses }, { children: child }));
|
|
35
33
|
});
|
|
36
34
|
};
|
|
@@ -43,7 +43,7 @@ const filterProps = (0, omit_1.default)([
|
|
|
43
43
|
]);
|
|
44
44
|
const customCompare = (prevProps, nextProps) => (0, isEqual_1.default)(filterProps(prevProps), filterProps(nextProps));
|
|
45
45
|
const Tree = react_1.default.memo((props) => {
|
|
46
|
-
const { groups = [], items = [], selectedGroups = [], selectedItems = [], onSelectionChange = noop_1.default, hasMultiselect = true, showRadioButtons = false, hideSearch = false, hideTreeHead, summary, hideSummary = false, search, searchPlaceholder = 'Type here to filter by name', onSearchChange = noop_1.default, className, scrollHeight, expandedGroups, onExpandGroupsChange = noop_1.default, showEmptyGroups = true, treeOptions = [], treeOptionsTooltip, disableAnimation = false } = props, remainingProps = tslib_1.__rest(props, ["groups", "items", "selectedGroups", "selectedItems", "onSelectionChange", "hasMultiselect", "showRadioButtons", "hideSearch", "hideTreeHead", "summary", "hideSummary", "search", "searchPlaceholder", "onSearchChange", "className", "scrollHeight", "expandedGroups", "onExpandGroupsChange", "showEmptyGroups", "treeOptions", "treeOptionsTooltip", "disableAnimation"]);
|
|
46
|
+
const { groups = [], items = [], selectedGroups = [], selectedItems = [], onSelectionChange = noop_1.default, hasMultiselect = true, showRadioButtons = false, hideSearch = false, hideTreeHead, treeHeaderContent, summary, hideSummary = false, search, searchPlaceholder = 'Type here to filter by name', onSearchChange = noop_1.default, className, scrollHeight, expandedGroups, onExpandGroupsChange = noop_1.default, showEmptyGroups = true, treeOptions = [], treeOptionsTooltip, disableAnimation = false } = props, remainingProps = tslib_1.__rest(props, ["groups", "items", "selectedGroups", "selectedItems", "onSelectionChange", "hasMultiselect", "showRadioButtons", "hideSearch", "hideTreeHead", "treeHeaderContent", "summary", "hideSummary", "search", "searchPlaceholder", "onSearchChange", "className", "scrollHeight", "expandedGroups", "onExpandGroupsChange", "showEmptyGroups", "treeOptions", "treeOptionsTooltip", "disableAnimation"]);
|
|
47
47
|
const [state, dispatch] = (0, react_1.useReducer)(treeReducer_1.treeReducer, {
|
|
48
48
|
groupedItems: [],
|
|
49
49
|
flatItems: [],
|
|
@@ -278,7 +278,7 @@ const Tree = react_1.default.memo((props) => {
|
|
|
278
278
|
const showSearch = !hideSearch;
|
|
279
279
|
const showSummary = !hideSummary;
|
|
280
280
|
const hasCustomSearch = !(0, isNil_1.default)(search);
|
|
281
|
-
return ((0, jsx_runtime_1.jsxs)("div", Object.assign({}, remainingProps, { className: treeClassNames, ref: treeRef }, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'TreeHeader' }, { children: [showSearch && !hasCustomSearch && ((0, jsx_runtime_1.jsx)(TreeSearch_1.default, { value: state.searchValue, onChange: handleSearchChange, placeholder: searchPlaceholder })), hasCustomSearch && search, showTreeHead && ((0, jsx_runtime_1.jsxs)("div", Object.assign({ className: treeHeadClasses }, { children: [showSelectAll && ((0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'border border-right-only hidden-empty padding-right-10 margin-right-2' }, { children: (0, jsx_runtime_1.jsx)(TreeSelectAll_1.default, { isChecked: state.allChecked, isEnabled: hasMultiselect, isIndeterminate: isIndeterminate, onSelect: handleSelectAll }) }))), (0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'display-flex justify-content-between align-items-start width-100pct' }, { children: showSummary
|
|
281
|
+
return ((0, jsx_runtime_1.jsxs)("div", Object.assign({}, remainingProps, { className: treeClassNames, ref: treeRef }, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'TreeHeader' }, { children: [treeHeaderContent, showSearch && !hasCustomSearch && ((0, jsx_runtime_1.jsx)(TreeSearch_1.default, { value: state.searchValue, onChange: handleSearchChange, placeholder: searchPlaceholder })), hasCustomSearch && search, showTreeHead && ((0, jsx_runtime_1.jsxs)("div", Object.assign({ className: treeHeadClasses }, { children: [showSelectAll && ((0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'border border-right-only hidden-empty padding-right-10 margin-right-2' }, { children: (0, jsx_runtime_1.jsx)(TreeSelectAll_1.default, { isChecked: state.allChecked, isEnabled: hasMultiselect, isIndeterminate: isIndeterminate, onSelect: handleSelectAll }) }))), (0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'display-flex justify-content-between align-items-start width-100pct' }, { children: showSummary
|
|
282
282
|
? summary || ((0, jsx_runtime_1.jsx)(TreeSummary_1.default, { children: (0, map_1.default)((typeCounter) => ((0, jsx_runtime_1.jsx)(TypeCounter_1.default, { type: typeCounter, icon: `${typeCounter}`, value: state.assetCounts[typeCounter], onClick: handleFilterByType, isActive: state.typeFilter.includes(typeCounter), hasFilter: isFilterActive, enableActivity: enableActivity }, typeCounter)))(state.visibleTypeCounters) }))
|
|
283
283
|
: null })), (0, jsx_runtime_1.jsx)(TreeOptions_1.default, { treeOptions: treeOptions, treeOptionsTooltip: treeOptionsTooltip })] })))] })), (0, jsx_runtime_1.jsx)(TreeRoot_1.default, Object.assign({ maxHeight: scrollHeight, disableAnimation: disableAnimation }, { children: content }))] })));
|
|
284
284
|
}, customCompare);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { type PropsWithChildren } from 'react';
|
|
2
|
-
export type DialogSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full' | 'fullwidth' | 'fullheight' | 'fullscreen';
|
|
2
|
+
export type DialogSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full' | 'fullwidth' | 'fullheight' | 'fullheight-lg' | 'fullheight-xl' | 'fullscreen';
|
|
3
3
|
export type BaseDialogProps = {
|
|
4
4
|
/**
|
|
5
5
|
* Opens the dialog when set to `true`.
|
|
@@ -30,7 +30,7 @@ export type BaseDialogProps = {
|
|
|
30
30
|
*
|
|
31
31
|
* By default, the dialog has a medium size, and thus it can be omitted.
|
|
32
32
|
*
|
|
33
|
-
* Possible values are: `xs`, `sm`, `lg` `xl` `fullwidth` `fullheight` `fullscreen`
|
|
33
|
+
* Possible values are: `xs`, `sm`, `lg` `xl` `fullwidth` `fullheight` 'fullheight-lg' 'fullheight-xl' `fullscreen`
|
|
34
34
|
*
|
|
35
35
|
* @default 'md'
|
|
36
36
|
*/
|
|
@@ -109,6 +109,8 @@ declare const Dialog: {
|
|
|
109
109
|
SIZE_FULL: "full";
|
|
110
110
|
SIZE_FULL_WIDTH: "fullwidth";
|
|
111
111
|
SIZE_FULL_HEIGHT: "fullheight";
|
|
112
|
+
SIZE_FULL_HEIGHT_LG: "fullheight-lg";
|
|
113
|
+
SIZE_FULL_HEIGHT_XL: "fullheight-xl";
|
|
112
114
|
SIZE_FULL_SCREEN: "fullscreen";
|
|
113
115
|
};
|
|
114
116
|
export default Dialog;
|
|
@@ -87,7 +87,7 @@ const Dialog = (props) => {
|
|
|
87
87
|
const modalClasses = (0, classnames_1.default)('modal', 'show', className);
|
|
88
88
|
const isSmallestDialog = bsSize === 'xs';
|
|
89
89
|
const hasChildren = !!children;
|
|
90
|
-
const modalDialogClasses = (0, classnames_1.default)(MODAL_DIALOG_CLASS, useOverflow && 'modal-overflow', bsSize === 'xs' && 'modal-xs', bsSize === 'sm' && 'modal-sm', bsSize === 'lg' && 'modal-lg', bsSize === 'xl' && 'modal-xl', bsSize === 'full' && 'modal-full-width', bsSize === 'fullwidth' && 'modal-full-width', bsSize === 'fullheight' && 'modal-full-height', bsSize === 'fullscreen' && 'modal-fullscreen');
|
|
90
|
+
const modalDialogClasses = (0, classnames_1.default)(MODAL_DIALOG_CLASS, useOverflow && 'modal-overflow', bsSize === 'xs' && 'modal-xs', bsSize === 'sm' && 'modal-sm', bsSize === 'lg' && 'modal-lg', bsSize === 'xl' && 'modal-xl', bsSize === 'full' && 'modal-full-width', bsSize === 'fullwidth' && 'modal-full-width', bsSize === 'fullheight' && 'modal-full-height', bsSize === 'fullheight-lg' && 'modal-full-height modal-lg', bsSize === 'fullheight-xl' && 'modal-full-height modal-xl', bsSize === 'fullscreen' && 'modal-fullscreen');
|
|
91
91
|
const spring = {
|
|
92
92
|
type: 'spring',
|
|
93
93
|
damping: 33,
|
|
@@ -126,5 +126,7 @@ Dialog.SIZE_XL = 'xl';
|
|
|
126
126
|
Dialog.SIZE_FULL = 'full';
|
|
127
127
|
Dialog.SIZE_FULL_WIDTH = 'fullwidth';
|
|
128
128
|
Dialog.SIZE_FULL_HEIGHT = 'fullheight';
|
|
129
|
+
Dialog.SIZE_FULL_HEIGHT_LG = 'fullheight-lg';
|
|
130
|
+
Dialog.SIZE_FULL_HEIGHT_XL = 'fullheight-xl';
|
|
129
131
|
Dialog.SIZE_FULL_SCREEN = 'fullscreen';
|
|
130
132
|
exports.default = Dialog;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type BaseDialogProps } from './Dialog';
|
|
3
|
+
export type OnboardingDialogProps = Pick<BaseDialogProps, 'show' | 'onClose' | 'className'> & {
|
|
4
|
+
/**
|
|
5
|
+
* The source URL of the image to be displayed in the dialog.
|
|
6
|
+
*/
|
|
7
|
+
imageSrc: string;
|
|
8
|
+
/**
|
|
9
|
+
* Alternative text for the image, used for accessibility.
|
|
10
|
+
*/
|
|
11
|
+
imageAlt?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The title of the dialog. A welcome message.
|
|
14
|
+
*/
|
|
15
|
+
title?: string | React.ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* A short description of the service and it's onboarding.
|
|
18
|
+
*/
|
|
19
|
+
description?: string | React.ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* Hint text for restarting the onboarding process.
|
|
22
|
+
*/
|
|
23
|
+
onboardingRestartHint?: string | React.ReactNode;
|
|
24
|
+
/**
|
|
25
|
+
* Detailed description for restarting the onboarding process.
|
|
26
|
+
*/
|
|
27
|
+
onboardingRestartDescription?: string | React.ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* Text for the skip button
|
|
30
|
+
*/
|
|
31
|
+
skipButtonText?: string | React.ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* Text for the button to start the tour
|
|
34
|
+
*/
|
|
35
|
+
tourButtonText?: string | React.ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Callback function to be called when the tour starts.
|
|
38
|
+
*
|
|
39
|
+
* @returns
|
|
40
|
+
*/
|
|
41
|
+
onStartTour: () => void;
|
|
42
|
+
};
|
|
43
|
+
declare const OnboardingDialog: (props: OnboardingDialogProps) => import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
export default OnboardingDialog;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
// @ts-ignore-next-line importsNotUsedAsValues
|
|
6
|
+
require("react");
|
|
7
|
+
const Dialog_1 = tslib_1.__importDefault(require("./Dialog"));
|
|
8
|
+
const Button_1 = tslib_1.__importDefault(require("../button/Button"));
|
|
9
|
+
const OnboardingDialog = (props) => {
|
|
10
|
+
const { className, show, onClose, onStartTour, imageSrc, imageAlt, title = 'Welcome to the RIO UIKIT!', description = ((0, jsx_runtime_1.jsxs)("span", { children: [(0, jsx_runtime_1.jsx)("span", Object.assign({ className: 'text-medium' }, { children: "Get started with a quick tour to explore UIKIT and discover its key features." })), ' ', (0, jsx_runtime_1.jsx)("span", { children: "It's the easiest way to get familiar with the service and make the most of your experience." })] })), onboardingRestartHint = 'Not ready to start now?', onboardingRestartDescription = 'You can always start the onboarding tour later from the service info option in the application header.', skipButtonText = 'Skip', tourButtonText = 'Start the tour' } = props, remainingProps = tslib_1.__rest(props, ["className", "show", "onClose", "onStartTour", "imageSrc", "imageAlt", "title", "description", "onboardingRestartHint", "onboardingRestartDescription", "skipButtonText", "tourButtonText"]);
|
|
11
|
+
const dialogClassName = `${className ? className : ''} onboarding-dialog`;
|
|
12
|
+
return ((0, jsx_runtime_1.jsx)(Dialog_1.default, Object.assign({}, remainingProps, { show: show, bsSize: 'sm', onClose: onClose, onEsc: onClose, bodyClassName: 'padding-0', className: dialogClassName, showCloseButton: false, body: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("img", { className: 'img-responsive rounded-top-right-extra-large rounded-top-left-extra-large', src: imageSrc, alt: imageAlt }) }), (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'position-relative padding-20 margin-top-10 margin-x-20' }, { children: [(0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'margin-bottom-15 margin-right-20 text-color-darkest' }, { children: (0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'text-size-h3 text-medium' }, { children: title })) })), (0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'text-color-darker' }, { children: (0, jsx_runtime_1.jsx)("p", Object.assign({ className: 'text-size-16' }, { children: description })) })), (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'text-color-darker' }, { children: [(0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'margin-top-25 text-medium' }, { children: onboardingRestartHint })), (0, jsx_runtime_1.jsx)("p", Object.assign({ className: 'text-color-darker padding-bottom-15 margin-top-5' }, { children: onboardingRestartDescription }))] })), (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'btn-toolbar justify-content-end margin-y-20' }, { children: [(0, jsx_runtime_1.jsx)(Button_1.default, Object.assign({ bsStyle: Button_1.default.MUTED, onClick: onClose }, { children: skipButtonText })), (0, jsx_runtime_1.jsx)(Button_1.default, Object.assign({ bsStyle: Button_1.default.PRIMARY, onClick: onStartTour }, { children: tourButtonText }))] }))] }))] }) })));
|
|
13
|
+
};
|
|
14
|
+
exports.default = OnboardingDialog;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type BaseDialogProps } from './Dialog';
|
|
3
|
-
export type SplitDialogProps = BaseDialogProps & {
|
|
3
|
+
export type SplitDialogProps = Omit<BaseDialogProps, 'useOverflow'> & {
|
|
4
4
|
/**
|
|
5
5
|
* The content to be shown on the left side.
|
|
6
6
|
*/
|
|
@@ -31,6 +31,12 @@ export type SplitDialogProps = BaseDialogProps & {
|
|
|
31
31
|
* If the function returns `false`, the dialog will not be closed
|
|
32
32
|
*/
|
|
33
33
|
onCloseValidation?: () => boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Enables the modal body to overflow and use inline scrolling if needed.
|
|
36
|
+
*
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
useOverflow?: boolean;
|
|
34
40
|
};
|
|
35
41
|
declare const SplitDialog: (props: SplitDialogProps) => import("react/jsx-runtime").JSX.Element;
|
|
36
42
|
export default SplitDialog;
|
|
@@ -13,6 +13,7 @@ const TextAlignment_1 = require("../../values/TextAlignment");
|
|
|
13
13
|
const Placement_1 = require("../../values/Placement");
|
|
14
14
|
const OnboardingTip = (props) => {
|
|
15
15
|
const { id, show = false, placement = Placement_1.PLACEMENT.BOTTOM, title, content, onHide = noop_1.default, children, textAlignment = TextAlignment_1.TEXT_ALIGNMENT.LEFT, useInDialog = false, showCloseIcon = true, clickflow = false, previousButton, nextButton, className = '', width, preventOverflow = true, popperConfig, } = props;
|
|
16
|
+
// DEPRECATED in favor of driver.js - see useOnboardingTour hook
|
|
16
17
|
const clickFlowWithButtons = ((0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'display-flex flex-column gap-25' }, { children: [(0, jsx_runtime_1.jsx)("div", { children: content }), (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: `btn-toolbar justify-content-between ${showCloseIcon ? 'margin-right--25' : ''}` }, { children: [previousButton ? ((0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'position-relative' }, { children: [(0, jsx_runtime_1.jsx)("div", { className: 'bg-black opacity-10 rounded display-absolute inset-0' }), (0, jsx_runtime_1.jsxs)(Button_1.default, Object.assign({ bsStyle: Button_1.default.SECONDARY, variant: Button_1.default.VARIANT_OUTLINE, className: 'border-none text-color-white', onClick: previousButton.onClick }, { children: [previousButton.iconName && ((0, jsx_runtime_1.jsx)("span", { className: `rioglyph ${previousButton.iconName} text-color-white` })), (0, jsx_runtime_1.jsx)("span", { children: previousButton.text })] }))] }))) : ((0, jsx_runtime_1.jsx)("div", {})), nextButton ? ((0, jsx_runtime_1.jsxs)(Button_1.default, Object.assign({ bsStyle: Button_1.default.SECONDARY, variant: Button_1.default.VARIANT_OUTLINE, className: 'border-color-white text-color-white', onClick: nextButton.onClick, iconRight: true }, { children: [(0, jsx_runtime_1.jsx)("span", { children: nextButton.text }), nextButton.iconName && (0, jsx_runtime_1.jsx)("span", { className: `rioglyph ${nextButton.iconName} text-color-white` })] }))) : ((0, jsx_runtime_1.jsx)("div", {}))] }))] })));
|
|
17
18
|
const tooltipWrapperClasses = (0, classnames_1.default)(useInDialog && 'z-index-max', className && className, clickflow && 'onboarding-clickflow');
|
|
18
19
|
const overlay = ((0, jsx_runtime_1.jsx)(Tooltip_1.default, Object.assign({ className: tooltipWrapperClasses, tooltipStyle: Tooltip_1.default.STYLE_ONBOARDING, id: id, onClick: onHide, width: width, textAlignment: textAlignment, allowOnTouch: true }, { children: (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'display-flex' }, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'display-flex flex-column flex-1-1' }, { children: [title && (0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'tooltip-title' }, { children: title })), content && (0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'tooltip-content' }, { children: clickflow ? clickFlowWithButtons : content }))] })), showCloseIcon && (0, jsx_runtime_1.jsx)("span", { className: 'tooltip-close rioglyph rioglyph-remove' })] })) })));
|
|
@@ -29,7 +29,12 @@ const Multiselect = (props) => {
|
|
|
29
29
|
const [keyboardUsed, setKeyboardUsed] = (0, react_1.useState)(false);
|
|
30
30
|
const refToggle = (0, react_1.useRef)(null);
|
|
31
31
|
const refMultiSelectWrapper = (0, react_1.useRef)(null);
|
|
32
|
-
const ref = (0, useClickOutside_1.default)(
|
|
32
|
+
const ref = (0, useClickOutside_1.default)(event => {
|
|
33
|
+
// Check if the click is truly outside the multiselect wrapper
|
|
34
|
+
if (refMultiSelectWrapper.current && !refMultiSelectWrapper.current.contains(event.target)) {
|
|
35
|
+
closeMenu();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
33
38
|
const mergedSelectRefs = (0, useMergeRefs_1.default)(refMultiSelectWrapper, ref);
|
|
34
39
|
const updateSelectedItems = (optionsToCheck, selectedItem) => {
|
|
35
40
|
if (selectedItem) {
|
|
@@ -63,7 +68,7 @@ const Multiselect = (props) => {
|
|
|
63
68
|
const updateDOMValues = (updatedItemDOMValues = []) => {
|
|
64
69
|
setItemDOMValues(updatedItemDOMValues);
|
|
65
70
|
};
|
|
66
|
-
const renderFilter = () => ((0, jsx_runtime_1.jsx)(MultiselectToggleFilter_1.default, { isActive: isFilterActive || !!filterValue, selectedItemIds: selectedItemIds, filterValue: filterValue, onFilterChange: handleFilterChange
|
|
71
|
+
const renderFilter = () => ((0, jsx_runtime_1.jsx)(MultiselectToggleFilter_1.default, { isActive: isFilterActive || !!filterValue, selectedItemIds: selectedItemIds, filterValue: filterValue, onFilterChange: handleFilterChange }));
|
|
67
72
|
const renderSelection = () => {
|
|
68
73
|
if (counterMessage || renderCounterMessage) {
|
|
69
74
|
return ((0, jsx_runtime_1.jsx)(MultiselectToggleCounter_1.default, { selectedAmount: selectedItemIds.length, counterMessage: counterMessage, customRenderer: renderCounterMessage }));
|
|
@@ -91,12 +96,18 @@ const Multiselect = (props) => {
|
|
|
91
96
|
event.preventDefault();
|
|
92
97
|
const targetFilterValue = event.currentTarget.value;
|
|
93
98
|
const optionsFiltered = (0, BaseSelectDropdown_1.filterOptions)(itemDOMValues, targetFilterValue, options);
|
|
94
|
-
|
|
99
|
+
if (!isOpen) {
|
|
100
|
+
// Ensure dropdown stays open when filtering
|
|
101
|
+
setIsOpen(true);
|
|
102
|
+
}
|
|
95
103
|
const newFocusedItemIndex = optionsFiltered.length > 0 ? 0 : -1;
|
|
96
104
|
setIsFilterActive(true);
|
|
97
105
|
setFilterValue(targetFilterValue);
|
|
98
106
|
setFilteredOptions(optionsFiltered);
|
|
99
|
-
|
|
107
|
+
if (optionsFiltered.length > 0) {
|
|
108
|
+
// Only set keyboard used if there are filtered options
|
|
109
|
+
setKeyboardUsed(true);
|
|
110
|
+
}
|
|
100
111
|
setFocusedItemIndex(newFocusedItemIndex);
|
|
101
112
|
};
|
|
102
113
|
const onOptionChange = (currentSelectedItem) => {
|
|
@@ -105,6 +116,7 @@ const Multiselect = (props) => {
|
|
|
105
116
|
return;
|
|
106
117
|
}
|
|
107
118
|
const updatedSelectedItems = currentSelectedItem ? updateSelection(currentSelectedItem.id) : selectedItemIds;
|
|
119
|
+
// Reset everything after an option was selected. Remove the filter again.
|
|
108
120
|
setSelectedItemIds(updatedSelectedItems);
|
|
109
121
|
setIsFilterActive(false);
|
|
110
122
|
setFilterValue('');
|
|
@@ -159,7 +171,7 @@ const Multiselect = (props) => {
|
|
|
159
171
|
// type submit by default in HTML. In order to differentiate between real click and a synthetic event
|
|
160
172
|
// caused by they keyboard, use the event details. A synthetic event is always 0.
|
|
161
173
|
const isKeyboardUsed = event.detail === 0;
|
|
162
|
-
setIsOpen(!
|
|
174
|
+
setIsOpen(prevValue => !prevValue);
|
|
163
175
|
setKeyboardUsed(isKeyboardUsed);
|
|
164
176
|
};
|
|
165
177
|
const closeMenu = () => {
|
|
@@ -167,6 +179,9 @@ const Multiselect = (props) => {
|
|
|
167
179
|
if (isOpen) {
|
|
168
180
|
setIsOpen(false);
|
|
169
181
|
setIsFilterActive(false);
|
|
182
|
+
setFilterValue('');
|
|
183
|
+
setFilteredOptions(options);
|
|
184
|
+
setFocusedItemIndex(DEFAULT_FOCUSED_ITEM_INDEX);
|
|
170
185
|
setKeyboardUsed(false);
|
|
171
186
|
(_a = refToggle === null || refToggle === void 0 ? void 0 : refToggle.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
172
187
|
}
|
|
@@ -3,7 +3,6 @@ export type MultiselectToggleFilterProps = {
|
|
|
3
3
|
isActive: boolean;
|
|
4
4
|
selectedItemIds?: string[];
|
|
5
5
|
filterValue: string;
|
|
6
|
-
multiline: boolean;
|
|
7
6
|
onFilterChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
8
7
|
};
|
|
9
8
|
declare const MultiselectToggleFilter: (props: MultiselectToggleFilterProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -7,10 +7,10 @@ require("react");
|
|
|
7
7
|
const classnames_1 = tslib_1.__importDefault(require("classnames"));
|
|
8
8
|
const isEmpty_1 = tslib_1.__importDefault(require("lodash/fp/isEmpty"));
|
|
9
9
|
const MultiselectToggleFilter = (props) => {
|
|
10
|
-
const { isActive, selectedItemIds, filterValue,
|
|
10
|
+
const { isActive, selectedItemIds, filterValue, onFilterChange } = props;
|
|
11
11
|
const inputClasses = (0, classnames_1.default)('multiselect-filter-input', (0, isEmpty_1.default)(selectedItemIds) && 'multiselect-filter-input-placeholder', isActive && 'multiselect-filter-input-active');
|
|
12
12
|
return ((0, jsx_runtime_1.jsx)("input", { type: 'text', role: 'searchbox', className: inputClasses,
|
|
13
13
|
// biome-ignore lint/a11y/noAutofocus: autofocus is intentionally set to allow instant typing to filter
|
|
14
|
-
autoFocus: true, onChange: onFilterChange,
|
|
14
|
+
autoFocus: true, onChange: onFilterChange, value: filterValue }));
|
|
15
15
|
};
|
|
16
16
|
exports.default = MultiselectToggleFilter;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { type PopoverDOM as DriverPopoverDOM, type State as DriverState, type AllowedButtons } from 'driver.js';
|
|
2
|
+
import 'driver.js/dist/driver.css';
|
|
3
|
+
export type PopoverDOM = DriverPopoverDOM;
|
|
4
|
+
export type OnboardingState = DriverState;
|
|
5
|
+
export type OnboardingStep = {
|
|
6
|
+
element?: string | Element;
|
|
7
|
+
onHighlightStarted?: (element: Element | undefined, step: OnboardingStep, opts: any) => void;
|
|
8
|
+
onHighlighted?: (element: Element | undefined, step: OnboardingStep, opts: any) => void;
|
|
9
|
+
onDeselected?: (element: Element | undefined, step: OnboardingStep, opts: any) => void;
|
|
10
|
+
popover?: {
|
|
11
|
+
title?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
side?: 'top' | 'right' | 'bottom' | 'left' | 'over';
|
|
14
|
+
align?: 'start' | 'center' | 'end';
|
|
15
|
+
showButtons?: ('next' | 'previous' | 'close')[];
|
|
16
|
+
disableButtons?: ('next' | 'previous' | 'close')[];
|
|
17
|
+
popoverClass?: string;
|
|
18
|
+
doneBtnText?: string;
|
|
19
|
+
nextBtnText?: string;
|
|
20
|
+
prevBtnText?: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
type OnboardingOptions = {
|
|
24
|
+
/**
|
|
25
|
+
* Array of steps to highlight. You should pass this when you want to setup a product tour.
|
|
26
|
+
*/
|
|
27
|
+
steps?: OnboardingStep[];
|
|
28
|
+
/**
|
|
29
|
+
* Text for the previous button in the onboarding popover.
|
|
30
|
+
*/
|
|
31
|
+
prevBtnText?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Text for the next button in the onboarding popover.
|
|
34
|
+
*/
|
|
35
|
+
nextBtnText?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Text for the done button in the onboarding popover.
|
|
38
|
+
*/
|
|
39
|
+
doneBtnText?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Indicates whether to show progress in the onboarding popover.
|
|
42
|
+
*
|
|
43
|
+
* @default true
|
|
44
|
+
*/
|
|
45
|
+
showProgress?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Whether to allow closing the popover by clicking on the backdrop.
|
|
48
|
+
* This should be disabled during the tour to ensure th user completes the tour.
|
|
49
|
+
* But can be enabled on the last step.
|
|
50
|
+
*
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
53
|
+
allowClose?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Distance between the highlighted element and the cutout.
|
|
56
|
+
*
|
|
57
|
+
* @default 10
|
|
58
|
+
*/
|
|
59
|
+
stagePadding?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Radius of the cutout around the highlighted element.
|
|
62
|
+
*
|
|
63
|
+
* @default 5
|
|
64
|
+
*/
|
|
65
|
+
stageRadius?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Whether to allow keyboard navigation.
|
|
68
|
+
*
|
|
69
|
+
* @default true
|
|
70
|
+
*/
|
|
71
|
+
allowKeyboardControl?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Array of buttons to show in the popover. Defaults to ["next", "previous", "close"]
|
|
74
|
+
* for product tours and [] for single element highlighting.
|
|
75
|
+
*/
|
|
76
|
+
showButtons?: AllowedButtons[];
|
|
77
|
+
/**
|
|
78
|
+
* Array of buttons to disable. This is useful when you want to show some of the buttons, but disable some of them.
|
|
79
|
+
*/
|
|
80
|
+
disableButtons?: AllowedButtons[];
|
|
81
|
+
/**
|
|
82
|
+
* Option to disable the backdrop. Note, the backdrop element is still there but with 100% opacity
|
|
83
|
+
* and the close on the backdrop is disabled.
|
|
84
|
+
*
|
|
85
|
+
* @default false
|
|
86
|
+
*/
|
|
87
|
+
noBackdrop?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Additional classes set on the popover itself.
|
|
90
|
+
*/
|
|
91
|
+
popoverClass?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Callback triggered when the onboarding popover renders.
|
|
94
|
+
*
|
|
95
|
+
* @param popover
|
|
96
|
+
* @param state
|
|
97
|
+
* @returns
|
|
98
|
+
*/
|
|
99
|
+
onPopoverRender?: (popover: PopoverDOM, state: OnboardingState) => void;
|
|
100
|
+
};
|
|
101
|
+
export declare const useOnboardingTour: (params: OnboardingOptions) => {
|
|
102
|
+
run: () => void;
|
|
103
|
+
destroy: () => void;
|
|
104
|
+
onboardingState: () => DriverState;
|
|
105
|
+
refresh: () => void;
|
|
106
|
+
isActive: () => boolean;
|
|
107
|
+
moveNext: () => void;
|
|
108
|
+
movePrevious: () => void;
|
|
109
|
+
moveTo: (stepNumber: number) => void;
|
|
110
|
+
hasNextStep: () => false | import("driver.js").DriveStep;
|
|
111
|
+
hasPreviousStep: () => false | import("driver.js").DriveStep;
|
|
112
|
+
isFirstStep: () => boolean;
|
|
113
|
+
isLastStep: () => boolean;
|
|
114
|
+
getActiveIndex: () => number | undefined;
|
|
115
|
+
};
|
|
116
|
+
type OnboardingTipOptions = {
|
|
117
|
+
allowClose?: boolean;
|
|
118
|
+
stagePadding?: number;
|
|
119
|
+
stageRadius?: number;
|
|
120
|
+
noBackdrop?: boolean;
|
|
121
|
+
};
|
|
122
|
+
export declare const useOnboardingTip: (options?: OnboardingTipOptions) => {
|
|
123
|
+
highlight: (step: OnboardingStep) => void;
|
|
124
|
+
destroy: () => void;
|
|
125
|
+
refresh: () => void;
|
|
126
|
+
isActive: () => boolean;
|
|
127
|
+
};
|
|
128
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useOnboardingTip = exports.useOnboardingTour = void 0;
|
|
4
|
+
const driver_js_1 = require("driver.js");
|
|
5
|
+
require("driver.js/dist/driver.css");
|
|
6
|
+
const useOnboardingTour = (params) => {
|
|
7
|
+
const { steps, prevBtnText = 'Previous', nextBtnText = 'Next', doneBtnText = 'Done', showProgress = true, allowClose = false, stagePadding = 10, stageRadius = 5, allowKeyboardControl = true, showButtons, disableButtons, popoverClass, noBackdrop = false, onPopoverRender = () => { }, } = params;
|
|
8
|
+
const handlePopoverRender = (popover, options) => {
|
|
9
|
+
onPopoverRender(popover, options.state);
|
|
10
|
+
};
|
|
11
|
+
const isSingleStep = !steps || steps.length === 1;
|
|
12
|
+
// This configuration is applied to all steps.
|
|
13
|
+
// Driver.js allows also for individual customization for single steps too.
|
|
14
|
+
const driverInstance = (0, driver_js_1.driver)({
|
|
15
|
+
steps,
|
|
16
|
+
prevBtnText,
|
|
17
|
+
nextBtnText,
|
|
18
|
+
doneBtnText,
|
|
19
|
+
showProgress: isSingleStep ? false : showProgress,
|
|
20
|
+
showButtons: isSingleStep ? ['close'] : showButtons,
|
|
21
|
+
allowClose,
|
|
22
|
+
overlayOpacity: noBackdrop ? 0 : 0.5,
|
|
23
|
+
disableButtons,
|
|
24
|
+
progressText: '{{current}} / {{total}}',
|
|
25
|
+
overlayColor: 'var(--color-black)',
|
|
26
|
+
stagePadding,
|
|
27
|
+
stageRadius,
|
|
28
|
+
allowKeyboardControl,
|
|
29
|
+
popoverClass,
|
|
30
|
+
onPopoverRender: handlePopoverRender,
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
// Use for onboarding tours. Pass in all steps as "steps" parameter for initial configuration.
|
|
34
|
+
run: () => driverInstance.drive(),
|
|
35
|
+
// Use for single element highlights -> see useOnboardingTip
|
|
36
|
+
// highlight: (step: OnboardingStep) => driverInstance.highlight(step),
|
|
37
|
+
// Cleanup the onboarding
|
|
38
|
+
destroy: () => driverInstance.destroy(),
|
|
39
|
+
// Gets the current state of the onboarding with the current step
|
|
40
|
+
onboardingState: () => driverInstance.getState(),
|
|
41
|
+
// Recalculate and redraw the highlight
|
|
42
|
+
refresh: () => driverInstance.refresh(),
|
|
43
|
+
// Is the tour or highlight currently active
|
|
44
|
+
isActive: () => driverInstance.isActive(),
|
|
45
|
+
// Move to the next step
|
|
46
|
+
moveNext: () => driverInstance.moveNext(),
|
|
47
|
+
// Move to the previous step
|
|
48
|
+
movePrevious: () => driverInstance.movePrevious(),
|
|
49
|
+
// Move to the step 4
|
|
50
|
+
moveTo: (stepNumber) => driverInstance.moveTo(stepNumber),
|
|
51
|
+
// Is there a next step
|
|
52
|
+
hasNextStep: () => driverInstance.hasNextStep(),
|
|
53
|
+
// Is there a previous step
|
|
54
|
+
hasPreviousStep: () => driverInstance.hasPreviousStep(),
|
|
55
|
+
// Is the current step the first step
|
|
56
|
+
isFirstStep: () => driverInstance.isFirstStep(),
|
|
57
|
+
// Is the current step the last step
|
|
58
|
+
isLastStep: () => driverInstance.isLastStep(),
|
|
59
|
+
// Gets the active step index
|
|
60
|
+
getActiveIndex: () => driverInstance.getActiveIndex(),
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
exports.useOnboardingTour = useOnboardingTour;
|
|
64
|
+
const useOnboardingTip = (options) => {
|
|
65
|
+
const { allowClose = true, stagePadding = 0, stageRadius = 5, noBackdrop = true } = options || {};
|
|
66
|
+
const driverInstance = (0, driver_js_1.driver)({
|
|
67
|
+
allowClose,
|
|
68
|
+
stagePadding,
|
|
69
|
+
stageRadius,
|
|
70
|
+
overlayOpacity: noBackdrop ? 0 : 0.5,
|
|
71
|
+
overlayColor: undefined,
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
highlight: (step) => driverInstance.highlight(step),
|
|
75
|
+
destroy: () => driverInstance.destroy(),
|
|
76
|
+
refresh: () => driverInstance.refresh(),
|
|
77
|
+
isActive: () => driverInstance.isActive(),
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
exports.useOnboardingTip = useOnboardingTip;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './hooks/useOnboarding';
|
package/lib/es/version.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rio-cloud/rio-uikit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "The RIO UIKIT component library",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@vitejs/plugin-react": "4.3.1",
|
|
67
67
|
"@vitest/coverage-c8": "0.33.0",
|
|
68
68
|
"autoprefixer": "10.4.20",
|
|
69
|
-
"backstopjs": "6.3.
|
|
69
|
+
"backstopjs": "6.3.25",
|
|
70
70
|
"copyfiles": "2.4.1",
|
|
71
71
|
"dotenv": "16.4.5",
|
|
72
72
|
"eslint": "8.55.0",
|
|
@@ -111,6 +111,7 @@
|
|
|
111
111
|
"@formkit/auto-animate": "0.8.2",
|
|
112
112
|
"@popperjs/core": "2.11.8",
|
|
113
113
|
"classnames": "2.5.1",
|
|
114
|
+
"driver.js": "^1.3.1",
|
|
114
115
|
"events": "3.3.0",
|
|
115
116
|
"framer-motion": "4.1.17",
|
|
116
117
|
"iframe-resizer-react": "1.1.0",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './hooks/useOnboarding';
|
package/useOnboarding.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './hooks/useOnboarding';
|
package/version.json
CHANGED