@drivenets/design-system 0.18.1 → 0.20.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/CHANGELOG.md +48 -0
- package/dist/index.cjs +17038 -10114
- package/dist/index.d.cts +1355 -444
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +1354 -443
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17073 -10172
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/package.json +41 -41
- package/src/components/ds-bot-button/ds-bot-button.figma.ts +38 -0
- package/src/components/ds-bot-button/ds-bot-button.module.scss +41 -0
- package/src/components/ds-bot-button/ds-bot-button.tsx +48 -0
- package/src/components/ds-bot-button/ds-bot-button.types.ts +33 -0
- package/src/components/ds-bot-button/index.ts +2 -0
- package/src/components/ds-bot-button/net-gen-logo.tsx +46 -0
- package/src/components/ds-bulk-actions/components/action-button.tsx +42 -0
- package/src/components/ds-bulk-actions/components/menu-entries.tsx +76 -0
- package/src/components/ds-bulk-actions/components/overflow.tsx +75 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.figma.ts +28 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.module.scss +199 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.tsx +220 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.types.ts +170 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.unit.test.ts +195 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.utils.ts +91 -0
- package/src/components/ds-bulk-actions/ds-bulk-actions.utils.unit.test.ts +134 -0
- package/src/components/ds-bulk-actions/hooks/compute-bulk-actions-overflow.ts +85 -0
- package/src/components/ds-bulk-actions/hooks/use-bulk-actions-overflow.ts +153 -0
- package/src/components/ds-bulk-actions/index.ts +18 -0
- package/src/components/ds-button-v3/ds-button-v3.module.scss +2 -2
- package/src/components/ds-catalog-layout/ds-catalog-layout.figma.ts +1 -1
- package/src/components/ds-checkbox/ds-checkbox.figma.ts +3 -0
- package/src/components/ds-checkbox/ds-checkbox.module.scss +102 -20
- package/src/components/ds-checkbox/ds-checkbox.tsx +30 -2
- package/src/components/ds-checkbox/ds-checkbox.types.ts +10 -0
- package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.module.scss +169 -0
- package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.tsx +92 -0
- package/src/components/ds-code-input/collapsed-code-field/index.ts +1 -0
- package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.module.scss +110 -0
- package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.tsx +61 -0
- package/src/components/ds-code-input/ds-code-input-panel/index.ts +1 -0
- package/src/components/ds-code-input/ds-code-input.module.scss +25 -0
- package/src/components/ds-code-input/ds-code-input.tsx +401 -0
- package/src/components/ds-code-input/ds-code-input.types.ts +166 -0
- package/src/components/ds-code-input/ds-code-input.utils.ts +94 -0
- package/src/components/ds-code-input/ds-code-input.utils.unit.test.ts +166 -0
- package/src/components/ds-code-input/index.ts +2 -0
- package/src/components/ds-code-input/use-collapsed-viewport.ts +289 -0
- package/src/components/ds-code-input/use-editor-host.ts +87 -0
- package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +2 -0
- package/src/components/ds-empty-state/ds-empty-state.module.scss +16 -0
- package/src/components/ds-empty-state/ds-empty-state.tsx +45 -0
- package/src/components/ds-empty-state/ds-empty-state.types.ts +38 -0
- package/src/components/ds-empty-state/index.ts +3 -0
- package/src/components/ds-file-upload/ds-file-upload.tsx +4 -1
- package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.types.ts +41 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-builder/index.ts +1 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-clear-all.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.types.ts +30 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/index.ts +1 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-pinned.ts +31 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.types.ts +22 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-query/index.ts +1 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-saved-filters.ts +19 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-search.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-summary.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-toolbar.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-view-switch.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-view.ts +11 -0
- package/src/components/ds-filters-bar/components/index.ts +11 -0
- package/src/components/ds-filters-bar/ds-filters-bar.context.ts +69 -0
- package/src/components/ds-filters-bar/ds-filters-bar.tsx +120 -0
- package/src/components/ds-filters-bar/ds-filters-bar.types.ts +440 -0
- package/src/components/ds-filters-bar/ds-filters-bar.unit.test.ts +216 -0
- package/src/components/ds-filters-bar/ds-filters-bar.utils.ts +141 -0
- package/src/components/ds-filters-bar/index.ts +1 -0
- package/src/components/ds-form-control/ds-form-control.module.scss +18 -0
- package/src/components/ds-form-control/ds-form-control.tsx +61 -19
- package/src/components/ds-form-control/ds-form-control.types.ts +2 -1
- package/src/components/ds-icon/custom-icons/index.ts +2 -0
- package/src/components/ds-icon/custom-icons/special-adv-filters.tsx +28 -0
- package/src/components/ds-illustration/ds-illustration.figma.ts +34 -0
- package/src/components/ds-illustration/ds-illustration.module.scss +6 -0
- package/src/components/ds-illustration/ds-illustration.tsx +29 -0
- package/src/components/ds-illustration/ds-illustration.types.ts +45 -0
- package/src/components/ds-illustration/illustrations/configuration.tsx +51 -0
- package/src/components/ds-illustration/illustrations/device.tsx +133 -0
- package/src/components/ds-illustration/illustrations/done.tsx +107 -0
- package/src/components/{ds-catalog-layout/catalog-layout-empty-illustration.tsx → ds-illustration/illustrations/empty-inbox.tsx} +10 -14
- package/src/components/ds-illustration/illustrations/error.tsx +86 -0
- package/src/components/ds-illustration/illustrations/go-filter.tsx +51 -0
- package/src/components/ds-illustration/illustrations/illustration-clouds.tsx +10 -0
- package/src/components/ds-illustration/illustrations/index.ts +39 -0
- package/src/components/ds-illustration/illustrations/no-connection.tsx +54 -0
- package/src/components/ds-illustration/illustrations/no-credit-card.tsx +48 -0
- package/src/components/ds-illustration/illustrations/no-device.tsx +61 -0
- package/src/components/ds-illustration/illustrations/no-documents.tsx +52 -0
- package/src/components/ds-illustration/illustrations/no-gps.tsx +57 -0
- package/src/components/ds-illustration/illustrations/no-images.tsx +82 -0
- package/src/components/ds-illustration/illustrations/no-items-cart.tsx +86 -0
- package/src/components/ds-illustration/illustrations/no-messages.tsx +51 -0
- package/src/components/ds-illustration/illustrations/no-search-result.tsx +76 -0
- package/src/components/ds-illustration/illustrations/no-tasks.tsx +61 -0
- package/src/components/ds-illustration/illustrations/site.tsx +80 -0
- package/src/components/ds-illustration/index.ts +3 -0
- package/src/components/ds-main-menu/components/expanded-item/expanded-item.tsx +108 -0
- package/src/components/ds-main-menu/components/expanded-item/expanded-item.types.ts +8 -0
- package/src/components/ds-main-menu/components/expanded-item/index.ts +1 -0
- package/src/components/ds-main-menu/components/menu-graphic/index.ts +1 -0
- package/src/components/ds-main-menu/components/menu-graphic/menu-graphic.tsx +16 -0
- package/src/components/ds-main-menu/components/menu-graphic/menu-graphic.types.ts +7 -0
- package/src/components/ds-main-menu/components/tile-link/index.ts +1 -0
- package/src/components/ds-main-menu/components/tile-link/tile-link.tsx +85 -0
- package/src/components/ds-main-menu/components/tile-link/tile-link.types.ts +8 -0
- package/src/components/ds-main-menu/components/utility-link/index.ts +1 -0
- package/src/components/ds-main-menu/components/utility-link/utility-link.tsx +37 -0
- package/src/components/ds-main-menu/components/utility-link/utility-link.types.ts +7 -0
- package/src/components/ds-main-menu/ds-main-menu.figma.ts +17 -0
- package/src/components/ds-main-menu/ds-main-menu.module.scss +349 -0
- package/src/components/ds-main-menu/ds-main-menu.tsx +102 -0
- package/src/components/ds-main-menu/ds-main-menu.types.ts +86 -0
- package/src/components/ds-main-menu/ds-main-menu.utils.ts +40 -0
- package/src/components/ds-main-menu/index.ts +9 -0
- package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +1 -1
- package/src/components/ds-pin-toggle/ds-pin-toggle.figma.ts +27 -0
- package/src/components/ds-pin-toggle/ds-pin-toggle.module.scss +41 -0
- package/src/components/ds-pin-toggle/ds-pin-toggle.tsx +43 -0
- package/src/components/ds-pin-toggle/ds-pin-toggle.types.ts +29 -0
- package/src/components/ds-pin-toggle/index.ts +2 -0
- package/src/components/ds-popover/components/popover-trigger/index.ts +1 -0
- package/src/components/ds-popover/components/popover-trigger/popover-trigger.tsx +15 -0
- package/src/components/ds-popover/ds-popover.context.ts +18 -0
- package/src/components/ds-popover/ds-popover.hover-intent.ts +65 -0
- package/src/components/ds-popover/ds-popover.tsx +164 -41
- package/src/components/ds-popover/ds-popover.types.ts +65 -0
- package/src/components/ds-popover/ds-popover.utils.ts +60 -0
- package/src/components/ds-popover/ds-popover.utils.unit.test.ts +25 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-delete-confirm.tsx +53 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-name-entry.tsx +64 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-name-modal.tsx +50 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.module.scss +68 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.tsx +83 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-row/index.ts +1 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-save.tsx +164 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.module.scss +21 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.tsx +237 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/index.ts +1 -0
- package/src/components/ds-saved-filters/components/index.ts +2 -0
- package/src/components/ds-saved-filters/ds-saved-filters.ts +6 -0
- package/src/components/ds-saved-filters/ds-saved-filters.types.ts +106 -0
- package/src/components/ds-saved-filters/ds-saved-filters.utils.ts +35 -0
- package/src/components/ds-saved-filters/hooks/index.ts +1 -0
- package/src/components/ds-saved-filters/hooks/use-picker-outside-dismiss.ts +42 -0
- package/src/components/ds-saved-filters/index.ts +2 -0
- package/src/components/ds-segment-group/ds-segment-group.module.scss +65 -37
- package/src/components/ds-segment-group/ds-segment-group.tsx +17 -5
- package/src/components/ds-segment-group/ds-segment-group.types.ts +20 -5
- package/src/components/ds-segment-group/figma/ds-segment-group-v2-item.figma.ts +109 -0
- package/src/components/ds-segment-group/figma/ds-segment-group-v2.figma.ts +31 -0
- package/src/components/ds-site-node/ds-site-node.tsx +2 -2
- package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +12 -2
- package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +2 -3
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +1 -6
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +6 -3
- package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +18 -7
- package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +27 -21
- package/src/components/ds-table/components/ds-table-header-label.module.scss +12 -0
- package/src/components/ds-table/components/ds-table-header-label.tsx +28 -0
- package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +0 -1
- package/src/components/ds-table/context/ds-table-context.tsx +8 -0
- package/src/components/ds-table/ds-table.module.scss +26 -16
- package/src/components/ds-table/ds-table.tsx +46 -14
- package/src/components/ds-table/ds-table.types.ts +33 -3
- package/src/components/ds-table/hooks/use-column-resize.ts +114 -11
- package/src/components/ds-table/hooks/use-scrollbar-spacer.ts +108 -0
- package/src/components/ds-table/hooks/use-scrollbar-spacer.unit.test.ts +26 -0
- package/src/components/ds-table/styles/_variables.scss +0 -2
- package/src/components/ds-table/utils/column-size.ts +274 -1
- package/src/components/ds-table/utils/column-size.unit.test.ts +157 -0
- package/src/components/ds-table/utils/constants.ts +13 -5
- package/src/components/ds-table/utils/table-columns.tsx +27 -13
- package/src/components/ds-table/utils/table-columns.unit.test.ts +44 -0
- package/src/components/ds-tag/ds-tag.figma.ts +23 -3
- package/src/components/ds-tag/ds-tag.module.scss +71 -7
- package/src/components/ds-tag/ds-tag.tsx +115 -14
- package/src/components/ds-tag/ds-tag.types.ts +70 -3
- package/src/components/ds-tag/ds-tag.unit.test.ts +23 -4
- package/src/components/ds-tag/index.ts +2 -2
- package/src/components/ds-text-input/ds-input-field-v02.figma.ts +2 -0
- package/src/components/ds-toggle/ds-toggle.module.scss +18 -0
- package/src/components/ds-toggle/ds-toggle.tsx +15 -9
- package/src/components/ds-toggle/ds-toggle.types.ts +6 -0
- package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.module.scss +69 -0
- package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.tsx +55 -0
- package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.types.ts +43 -0
- package/src/components/ds-toggle-filter-data/index.ts +1 -0
- package/src/components/ds-tooltip/ds-tooltip.module.scss +8 -0
- package/src/components/ds-tooltip/ds-tooltip.tsx +7 -3
- package/src/components/ds-tooltip/ds-tooltip.types.ts +17 -0
- package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.figma.ts +35 -0
- package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.module.scss +150 -0
- package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.tsx +161 -0
- package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.types.ts +117 -0
- package/src/components/ds-top-bar-navigation/index.ts +2 -0
- package/src/components/ds-tree/ds-tree.tsx +10 -10
- package/src/components/ds-tree/ds-tree.types.ts +40 -7
- package/src/index.ts +8 -0
- package/src/utils/use-is-horizontally-overflowing.ts +35 -0
- package/src/components/ds-site-node/hooks/use-is-overflowing.ts +0 -31
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +0 -112
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx +0 -67
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.types.ts +0 -41
- package/src/components/ds-table/components/ds-table-bulk-actions/index.ts +0 -2
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { KeyboardEvent, MouseEvent } from 'react';
|
|
2
|
+
import type { DsMainMenuItem, DsMainMenuProps } from './ds-main-menu.types';
|
|
3
|
+
|
|
4
|
+
export const isNonActivatable = (state: DsMainMenuItem['state']) =>
|
|
5
|
+
state === 'disabled' || state === 'comingSoon';
|
|
6
|
+
|
|
7
|
+
export const handleActivatableClick = (
|
|
8
|
+
event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>,
|
|
9
|
+
id: string,
|
|
10
|
+
href: string | undefined,
|
|
11
|
+
onItemSelect: DsMainMenuProps['onItemSelect'],
|
|
12
|
+
onClose: () => void,
|
|
13
|
+
) => {
|
|
14
|
+
if (!href) {
|
|
15
|
+
event.preventDefault();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
onItemSelect?.(id);
|
|
19
|
+
onClose();
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const handleActivatableKeyDown = (
|
|
23
|
+
event: KeyboardEvent<HTMLAnchorElement | HTMLButtonElement>,
|
|
24
|
+
id: string,
|
|
25
|
+
href: string | undefined,
|
|
26
|
+
onItemSelect: DsMainMenuProps['onItemSelect'],
|
|
27
|
+
onClose: () => void,
|
|
28
|
+
) => {
|
|
29
|
+
if (event.key !== 'Enter' && event.key !== ' ') {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
onItemSelect?.(id);
|
|
35
|
+
onClose();
|
|
36
|
+
|
|
37
|
+
if (href) {
|
|
38
|
+
(event.currentTarget as HTMLAnchorElement).click();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as DsMainMenu } from './ds-main-menu';
|
|
2
|
+
export type {
|
|
3
|
+
DsMainMenuItem,
|
|
4
|
+
DsMainMenuItemState,
|
|
5
|
+
DsMainMenuProps,
|
|
6
|
+
DsMainMenuUtilityLink,
|
|
7
|
+
DsMainMenuVariant,
|
|
8
|
+
} from './ds-main-menu.types';
|
|
9
|
+
export { COMING_SOON_TOOLTIP, mainMenuItemStates, mainMenuVariants } from './ds-main-menu.types';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=43101-8753
|
|
2
2
|
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-modal
|
|
3
3
|
// component=DsModal.Footer
|
|
4
4
|
//
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=42863-16495
|
|
2
|
+
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-pin-toggle
|
|
3
|
+
// component=DsPinToggle
|
|
4
|
+
import figma from 'figma';
|
|
5
|
+
|
|
6
|
+
const instance = figma.selectedInstance;
|
|
7
|
+
|
|
8
|
+
// `Part_PinnedIcon` is a `Part_*` node, which we normally do not mirror. It is
|
|
9
|
+
// mapped here because, unlike `Part_CheckboxBtnV3`, it has a real exported
|
|
10
|
+
// counterpart in code — the rule exists to stop us inventing components for
|
|
11
|
+
// markup, not to hide ones that exist.
|
|
12
|
+
const active = instance.getEnum('active', {
|
|
13
|
+
true: 'true',
|
|
14
|
+
false: 'false',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// `itemLabel` has no Figma source — the pin carries no text, and the name it
|
|
18
|
+
// needs comes from the row it sits in. The snippet emits a placeholder so the
|
|
19
|
+
// required prop is visible rather than silently missing.
|
|
20
|
+
const attrs = ['itemLabel="Label"', active === 'true' ? 'pinned' : ''].filter(Boolean).join(' ');
|
|
21
|
+
|
|
22
|
+
export default {
|
|
23
|
+
example: figma.code`<DsPinToggle ${attrs} />`,
|
|
24
|
+
imports: ["import { DsPinToggle } from '@drivenets/design-system';"],
|
|
25
|
+
id: 'ds-pin-toggle',
|
|
26
|
+
metadata: { nestable: true },
|
|
27
|
+
} satisfies figma.Template;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
$pin-border-radius: 2px;
|
|
2
|
+
$pin-focus-ring-width: 2px;
|
|
3
|
+
$transition-duration: 0.2s;
|
|
4
|
+
|
|
5
|
+
.root {
|
|
6
|
+
display: inline-flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
flex-shrink: 0;
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: var(--4xs);
|
|
12
|
+
border: none;
|
|
13
|
+
border-radius: $pin-border-radius;
|
|
14
|
+
background-color: transparent;
|
|
15
|
+
line-height: 0;
|
|
16
|
+
color: var(--icon-tertiary);
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
transition: color $transition-duration ease-in-out;
|
|
19
|
+
|
|
20
|
+
&:not(:disabled):hover {
|
|
21
|
+
color: var(--icon-secondary);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&[aria-pressed='true'] {
|
|
25
|
+
color: var(--icon-action);
|
|
26
|
+
|
|
27
|
+
&:not(:disabled):hover {
|
|
28
|
+
color: var(--icon-action-hover);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:focus-visible {
|
|
33
|
+
outline: $pin-focus-ring-width solid var(--border-action-primary);
|
|
34
|
+
outline-offset: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&:disabled {
|
|
38
|
+
color: var(--icon-disabled);
|
|
39
|
+
cursor: not-allowed;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import styles from './ds-pin-toggle.module.scss';
|
|
3
|
+
import type { DsPinToggleProps } from './ds-pin-toggle.types';
|
|
4
|
+
import { DsIcon } from '../ds-icon';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_LOCALE = Object.freeze({ pin: 'Pin' });
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Bare pushpin for "keep this at the top" affordances.
|
|
10
|
+
*/
|
|
11
|
+
const DsPinToggle = ({
|
|
12
|
+
ref,
|
|
13
|
+
className,
|
|
14
|
+
style,
|
|
15
|
+
itemLabel,
|
|
16
|
+
pinned = false,
|
|
17
|
+
disabled = false,
|
|
18
|
+
locale,
|
|
19
|
+
onPinnedChange,
|
|
20
|
+
onClick,
|
|
21
|
+
...rest
|
|
22
|
+
}: DsPinToggleProps) => (
|
|
23
|
+
<button
|
|
24
|
+
{...rest}
|
|
25
|
+
ref={ref}
|
|
26
|
+
type="button"
|
|
27
|
+
disabled={disabled}
|
|
28
|
+
aria-pressed={pinned}
|
|
29
|
+
aria-label={`${locale?.pin ?? DEFAULT_LOCALE.pin} ${itemLabel}`}
|
|
30
|
+
className={classNames(styles.root, className)}
|
|
31
|
+
style={style}
|
|
32
|
+
onClick={(event) => {
|
|
33
|
+
onClick?.(event);
|
|
34
|
+
onPinnedChange?.(!pinned);
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
<DsIcon icon="keep" size="tiny" variant="rounded" filled aria-hidden />
|
|
38
|
+
</button>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
DsPinToggle.displayName = 'DsPinToggle';
|
|
42
|
+
|
|
43
|
+
export default DsPinToggle;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, Ref } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface DsPinToggleProps extends Omit<
|
|
4
|
+
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
5
|
+
'aria-label' | 'aria-labelledby' | 'aria-pressed' | 'children' | 'type'
|
|
6
|
+
> {
|
|
7
|
+
ref?: Ref<HTMLButtonElement>;
|
|
8
|
+
|
|
9
|
+
itemLabel: string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @default false
|
|
13
|
+
*/
|
|
14
|
+
pinned?: boolean;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @default false
|
|
18
|
+
*/
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
|
|
21
|
+
locale?: {
|
|
22
|
+
/**
|
|
23
|
+
* @default 'Pin'
|
|
24
|
+
*/
|
|
25
|
+
pin?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
onPinnedChange?: (pinned: boolean) => void;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PopoverTrigger } from './popover-trigger';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Popover } from '@ark-ui/react/popover';
|
|
2
|
+
import { useHoverTriggerProps } from '../../ds-popover.hover-intent';
|
|
3
|
+
import type { DsPopoverTriggerProps } from '../../ds-popover.types';
|
|
4
|
+
|
|
5
|
+
export const PopoverTrigger = ({ children, className }: DsPopoverTriggerProps) => {
|
|
6
|
+
const hoverProps = useHoverTriggerProps();
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<Popover.Trigger asChild className={className} {...hoverProps}>
|
|
10
|
+
{children}
|
|
11
|
+
</Popover.Trigger>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
PopoverTrigger.displayName = 'DsPopover.Trigger';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
import type { HoverIntent } from './ds-popover.hover-intent';
|
|
3
|
+
|
|
4
|
+
export interface DsPopoverContextValue {
|
|
5
|
+
matchAnchorWidth: boolean;
|
|
6
|
+
registerAnchor: (el: HTMLElement | null) => void;
|
|
7
|
+
registerContentId: (id: string | undefined) => void;
|
|
8
|
+
hoverIntent: HoverIntent | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const DsPopoverContext = createContext<DsPopoverContextValue>({
|
|
12
|
+
matchAnchorWidth: false,
|
|
13
|
+
registerAnchor: () => undefined,
|
|
14
|
+
registerContentId: () => undefined,
|
|
15
|
+
hoverIntent: null,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const useDsPopoverContext = () => useContext(DsPopoverContext);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { FocusEvent, PointerEvent } from 'react';
|
|
2
|
+
import { usePopoverContext } from '@ark-ui/react/popover';
|
|
3
|
+
import { useDsPopoverContext } from './ds-popover.context';
|
|
4
|
+
import { isHoverPointer } from './ds-popover.utils';
|
|
5
|
+
|
|
6
|
+
export const DEFAULT_OPEN_DELAY_MS = 200;
|
|
7
|
+
export const DEFAULT_CLOSE_DELAY_MS = 150;
|
|
8
|
+
|
|
9
|
+
export interface HoverIntent {
|
|
10
|
+
openDelay: number;
|
|
11
|
+
closeDelay: number;
|
|
12
|
+
setFocusInPanel: (inPanel: boolean) => void;
|
|
13
|
+
/** True once if the trigger is about to be re-focused by Ark's close-time restore. */
|
|
14
|
+
consumeFocusRestore: () => boolean;
|
|
15
|
+
/** Owns one shared timer */
|
|
16
|
+
schedule: (action: () => void, delay: number) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const useHoverIntent = () => useDsPopoverContext().hoverIntent;
|
|
20
|
+
|
|
21
|
+
export const useHoverIntentProps = () => {
|
|
22
|
+
const intent = useHoverIntent();
|
|
23
|
+
const { setOpen } = usePopoverContext();
|
|
24
|
+
|
|
25
|
+
if (!intent) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const onPointerIntent = (event: PointerEvent, open: boolean, delay: number) => {
|
|
30
|
+
if (!isHoverPointer(event.pointerType)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
intent.schedule(() => setOpen(open), delay);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
onPointerEnter: (event: PointerEvent) => onPointerIntent(event, true, intent.openDelay),
|
|
39
|
+
onPointerLeave: (event: PointerEvent) => onPointerIntent(event, false, intent.closeDelay),
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const useHoverTriggerProps = () => {
|
|
44
|
+
const pointerProps = useHoverIntentProps();
|
|
45
|
+
const intent = useHoverIntent();
|
|
46
|
+
const { setOpen } = usePopoverContext();
|
|
47
|
+
|
|
48
|
+
if (!intent) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
...pointerProps,
|
|
54
|
+
onFocus: (event: FocusEvent<HTMLElement>) => {
|
|
55
|
+
// Closing with focus inside the panel makes Ark re-focus the trigger
|
|
56
|
+
if (intent.consumeFocusRestore()) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (event.target.matches(':focus-visible')) {
|
|
61
|
+
setOpen(true);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
};
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useLayoutEffect, useRef, useState, type FocusEvent } from 'react';
|
|
2
|
+
import { Popover, type PopoverRootProps } from '@ark-ui/react/popover';
|
|
2
3
|
import { Portal } from '@ark-ui/react/portal';
|
|
3
4
|
import classNames from 'classnames';
|
|
4
5
|
import { DsStack } from '../ds-stack';
|
|
5
6
|
import { DsTypography } from '../ds-typography';
|
|
7
|
+
import { PopoverTrigger } from './components/popover-trigger';
|
|
8
|
+
import { DsPopoverContext, useDsPopoverContext } from './ds-popover.context';
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_CLOSE_DELAY_MS,
|
|
11
|
+
DEFAULT_OPEN_DELAY_MS,
|
|
12
|
+
useHoverIntentProps,
|
|
13
|
+
} from './ds-popover.hover-intent';
|
|
14
|
+
import { invokeCloseAutoFocus, toFocusEl, toPlacement } from './ds-popover.utils';
|
|
6
15
|
import styles from './ds-popover.module.scss';
|
|
7
16
|
import type {
|
|
8
|
-
|
|
17
|
+
DsPopoverAnchorProps,
|
|
9
18
|
DsPopoverContentItemProps,
|
|
10
19
|
DsPopoverContentProps,
|
|
11
20
|
DsPopoverFooterProps,
|
|
@@ -13,14 +22,10 @@ import type {
|
|
|
13
22
|
DsPopoverPanelProps,
|
|
14
23
|
DsPopoverProps,
|
|
15
24
|
DsPopoverRootProps,
|
|
16
|
-
DsPopoverSide,
|
|
17
|
-
DsPopoverTriggerProps,
|
|
18
25
|
} from './ds-popover.types';
|
|
19
26
|
|
|
20
27
|
const DEFAULT_PANEL_WIDTH = 400;
|
|
21
|
-
|
|
22
|
-
export const toPlacement = (side: DsPopoverSide, align: DsPopoverAlign) =>
|
|
23
|
-
align === 'center' ? side : (`${side}-${align}` as const);
|
|
28
|
+
const MATCHED_PANEL_WIDTH = 'var(--reference-width)';
|
|
24
29
|
|
|
25
30
|
const DsPopoverRoot = ({
|
|
26
31
|
open,
|
|
@@ -29,47 +34,164 @@ const DsPopoverRoot = ({
|
|
|
29
34
|
align = 'center',
|
|
30
35
|
gutter = 8,
|
|
31
36
|
modal = false,
|
|
37
|
+
matchAnchorWidth = false,
|
|
38
|
+
restoreFocus,
|
|
39
|
+
openOn = 'click',
|
|
40
|
+
openDelay = DEFAULT_OPEN_DELAY_MS,
|
|
41
|
+
closeDelay = DEFAULT_CLOSE_DELAY_MS,
|
|
42
|
+
getAnchorElement,
|
|
32
43
|
children,
|
|
44
|
+
onOpenAutoFocus,
|
|
45
|
+
onCloseAutoFocus,
|
|
46
|
+
onInteractOutside,
|
|
33
47
|
onOpenChange,
|
|
34
|
-
}: DsPopoverRootProps) =>
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
onOpenChange={(details) => onOpenChange?.(details.open)}
|
|
41
|
-
>
|
|
42
|
-
{children}
|
|
43
|
-
</Popover.Root>
|
|
44
|
-
);
|
|
48
|
+
}: DsPopoverRootProps) => {
|
|
49
|
+
const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
|
50
|
+
const anchorRef = useRef<HTMLElement | null>(null);
|
|
51
|
+
const restoringFocus = useRef(false);
|
|
52
|
+
const [contentId, setContentId] = useState<string>();
|
|
53
|
+
const [focusInPanel, setFocusInPanel] = useState(false);
|
|
45
54
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
);
|
|
55
|
+
useEffect(() => () => clearTimeout(timer.current), []);
|
|
56
|
+
|
|
57
|
+
const schedule = (action: () => void, delay: number) => {
|
|
58
|
+
clearTimeout(timer.current);
|
|
59
|
+
timer.current = setTimeout(action, delay);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const isHover = openOn === 'hover';
|
|
63
|
+
|
|
64
|
+
const consumeFocusRestore = () => {
|
|
65
|
+
const restoring = restoringFocus.current;
|
|
66
|
+
restoringFocus.current = false;
|
|
67
|
+
|
|
68
|
+
return restoring;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const registerAnchor = (el: HTMLElement | null) => {
|
|
72
|
+
anchorRef.current = el;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const ownsCloseFocus = restoreFocus === false && Boolean(onCloseAutoFocus);
|
|
76
|
+
const initialFocusEl = toFocusEl(onOpenAutoFocus, 'openAutoFocus') as PopoverRootProps['initialFocusEl'];
|
|
77
|
+
const finalFocusEl = ownsCloseFocus
|
|
78
|
+
? undefined
|
|
79
|
+
: (toFocusEl(onCloseAutoFocus, 'closeAutoFocus') as PopoverRootProps['finalFocusEl']);
|
|
80
|
+
const arkRestoreFocus = ownsCloseFocus ? false : (restoreFocus ?? (!isHover || focusInPanel));
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<DsPopoverContext.Provider
|
|
84
|
+
value={{
|
|
85
|
+
matchAnchorWidth,
|
|
86
|
+
registerAnchor,
|
|
87
|
+
registerContentId: setContentId,
|
|
88
|
+
hoverIntent: isHover
|
|
89
|
+
? { openDelay, closeDelay, schedule, setFocusInPanel, consumeFocusRestore }
|
|
90
|
+
: null,
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<Popover.Root
|
|
94
|
+
open={open}
|
|
95
|
+
defaultOpen={defaultOpen}
|
|
96
|
+
modal={modal}
|
|
97
|
+
ids={contentId ? { content: contentId } : undefined}
|
|
98
|
+
// Trigger focus is lost when open on hover
|
|
99
|
+
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
100
|
+
autoFocus={!isHover}
|
|
101
|
+
restoreFocus={arkRestoreFocus}
|
|
102
|
+
positioning={{
|
|
103
|
+
placement: toPlacement(side, align),
|
|
104
|
+
gutter,
|
|
105
|
+
sameWidth: matchAnchorWidth,
|
|
106
|
+
...(getAnchorElement ? { getAnchorElement } : {}),
|
|
107
|
+
}}
|
|
108
|
+
initialFocusEl={initialFocusEl}
|
|
109
|
+
finalFocusEl={finalFocusEl}
|
|
110
|
+
persistentElements={[() => anchorRef.current]}
|
|
111
|
+
onInteractOutside={
|
|
112
|
+
onInteractOutside
|
|
113
|
+
? (event) => {
|
|
114
|
+
const dsEvent = new Event('interactOutside', { cancelable: true });
|
|
115
|
+
onInteractOutside(dsEvent);
|
|
116
|
+
|
|
117
|
+
if (dsEvent.defaultPrevented) {
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
: undefined
|
|
122
|
+
}
|
|
123
|
+
onOpenChange={(details) => {
|
|
124
|
+
if (!details.open && focusInPanel) {
|
|
125
|
+
restoringFocus.current = true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!details.open && ownsCloseFocus && onCloseAutoFocus) {
|
|
129
|
+
invokeCloseAutoFocus(onCloseAutoFocus);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
onOpenChange?.(details.open);
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
{children}
|
|
136
|
+
</Popover.Root>
|
|
137
|
+
</DsPopoverContext.Provider>
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const DsPopoverAnchor = ({ children, className }: DsPopoverAnchorProps) => {
|
|
142
|
+
const { registerAnchor } = useDsPopoverContext();
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<Popover.Anchor asChild className={className} ref={registerAnchor}>
|
|
146
|
+
{children}
|
|
147
|
+
</Popover.Anchor>
|
|
148
|
+
);
|
|
149
|
+
};
|
|
51
150
|
|
|
52
151
|
const DsPopoverPanel = ({
|
|
53
|
-
width
|
|
152
|
+
width,
|
|
54
153
|
className,
|
|
55
154
|
style,
|
|
56
155
|
children,
|
|
57
156
|
ref,
|
|
157
|
+
id,
|
|
58
158
|
'aria-label': ariaLabel,
|
|
59
|
-
}: DsPopoverPanelProps) =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
)
|
|
159
|
+
}: DsPopoverPanelProps) => {
|
|
160
|
+
const { matchAnchorWidth, registerContentId, hoverIntent } = useDsPopoverContext();
|
|
161
|
+
const hoverProps = useHoverIntentProps();
|
|
162
|
+
const resolvedWidth = width ?? (matchAnchorWidth ? MATCHED_PANEL_WIDTH : DEFAULT_PANEL_WIDTH);
|
|
163
|
+
|
|
164
|
+
useLayoutEffect(() => {
|
|
165
|
+
registerContentId(id);
|
|
166
|
+
|
|
167
|
+
return () => registerContentId(undefined);
|
|
168
|
+
}, [id, registerContentId]);
|
|
169
|
+
|
|
170
|
+
const onFocus = () => hoverIntent?.setFocusInPanel(true);
|
|
171
|
+
const onBlur = (event: FocusEvent<HTMLDivElement>) => {
|
|
172
|
+
if (!event.currentTarget.contains(event.relatedTarget)) {
|
|
173
|
+
hoverIntent?.setFocusInPanel(false);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<Portal>
|
|
179
|
+
<Popover.Positioner>
|
|
180
|
+
<Popover.Content
|
|
181
|
+
{...hoverProps}
|
|
182
|
+
onFocus={onFocus}
|
|
183
|
+
onBlur={onBlur}
|
|
184
|
+
ref={ref}
|
|
185
|
+
aria-label={ariaLabel}
|
|
186
|
+
className={classNames(styles.panel, className)}
|
|
187
|
+
style={{ ...style, width: resolvedWidth }}
|
|
188
|
+
>
|
|
189
|
+
{children}
|
|
190
|
+
</Popover.Content>
|
|
191
|
+
</Popover.Positioner>
|
|
192
|
+
</Portal>
|
|
193
|
+
);
|
|
194
|
+
};
|
|
73
195
|
|
|
74
196
|
const DsPopoverHeader = ({ icon, className, style, children }: DsPopoverHeaderProps) => (
|
|
75
197
|
<div className={classNames(styles.header, className)} style={style}>
|
|
@@ -141,14 +263,14 @@ const DsPopoverLegacy = ({
|
|
|
141
263
|
align = 'center',
|
|
142
264
|
}: DsPopoverProps) => (
|
|
143
265
|
<DsPopoverRoot side={side} align={align}>
|
|
144
|
-
<
|
|
266
|
+
<PopoverTrigger>{trigger}</PopoverTrigger>
|
|
145
267
|
<DsPopoverPanel className={className}>{children}</DsPopoverPanel>
|
|
146
268
|
</DsPopoverRoot>
|
|
147
269
|
);
|
|
148
270
|
|
|
149
271
|
DsPopoverLegacy.displayName = 'DsPopover';
|
|
150
272
|
DsPopoverRoot.displayName = 'DsPopover.Root';
|
|
151
|
-
|
|
273
|
+
DsPopoverAnchor.displayName = 'DsPopover.Anchor';
|
|
152
274
|
DsPopoverPanel.displayName = 'DsPopover.Panel';
|
|
153
275
|
DsPopoverHeader.displayName = 'DsPopover.Header';
|
|
154
276
|
DsPopoverContent.displayName = 'DsPopover.Content';
|
|
@@ -157,7 +279,8 @@ DsPopoverFooter.displayName = 'DsPopover.Footer';
|
|
|
157
279
|
|
|
158
280
|
export const DsPopover = Object.assign(DsPopoverLegacy, {
|
|
159
281
|
Root: DsPopoverRoot,
|
|
160
|
-
Trigger:
|
|
282
|
+
Trigger: PopoverTrigger,
|
|
283
|
+
Anchor: DsPopoverAnchor,
|
|
161
284
|
Panel: DsPopoverPanel,
|
|
162
285
|
Header: DsPopoverHeader,
|
|
163
286
|
Content: DsPopoverContent,
|
|
@@ -6,6 +6,9 @@ export type DsPopoverSide = (typeof popoverSides)[number];
|
|
|
6
6
|
export const popoverAligns = ['start', 'center', 'end'] as const;
|
|
7
7
|
export type DsPopoverAlign = (typeof popoverAligns)[number];
|
|
8
8
|
|
|
9
|
+
export const popoverOpenTriggers = ['click', 'hover'] as const;
|
|
10
|
+
export type DsPopoverOpenTrigger = (typeof popoverOpenTriggers)[number];
|
|
11
|
+
|
|
9
12
|
export interface DsPopoverRootProps {
|
|
10
13
|
/** Controlled open state. Pair with `onOpenChange`. */
|
|
11
14
|
open?: boolean;
|
|
@@ -31,7 +34,62 @@ export interface DsPopoverRootProps {
|
|
|
31
34
|
* @default false
|
|
32
35
|
*/
|
|
33
36
|
modal?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Size the panel to the positioning reference (Anchor, or the trigger).
|
|
39
|
+
* An explicit `DsPopover.Panel` `width` wins over this.
|
|
40
|
+
* @default false
|
|
41
|
+
*/
|
|
42
|
+
matchAnchorWidth?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Whether to restore focus to the previously focused element on close.
|
|
45
|
+
* @default true
|
|
46
|
+
*/
|
|
47
|
+
restoreFocus?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Trigger open method - click / hover
|
|
50
|
+
* @default 'click'
|
|
51
|
+
*/
|
|
52
|
+
openOn?: DsPopoverOpenTrigger;
|
|
53
|
+
/**
|
|
54
|
+
* Milliseconds the pointer must rest on the trigger before the panel opens.
|
|
55
|
+
* Ignored unless `openOn` is `'hover'`.
|
|
56
|
+
* @default 200
|
|
57
|
+
*/
|
|
58
|
+
openDelay?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Milliseconds after the pointer leaves the trigger or the panel before it
|
|
61
|
+
* closes. Ignored unless `openOn` is `'hover'`.
|
|
62
|
+
* @default 150
|
|
63
|
+
*/
|
|
64
|
+
closeDelay?: number;
|
|
65
|
+
/**
|
|
66
|
+
* Returns the element the panel should position against.
|
|
67
|
+
* When provided, the panel anchors to this element instead of the trigger —
|
|
68
|
+
* useful when the trigger lives in one place (e.g. a sidebar) and the panel
|
|
69
|
+
* should appear relative to a different reference point. Prefer `DsPopover.Anchor`
|
|
70
|
+
* when the reference is in the tree.
|
|
71
|
+
*/
|
|
72
|
+
getAnchorElement?: () => HTMLElement | null;
|
|
34
73
|
children: ReactNode;
|
|
74
|
+
/**
|
|
75
|
+
* Called when focus is about to move into the panel as it opens.
|
|
76
|
+
* Call `event.preventDefault()` to stop the default move to the first focusable
|
|
77
|
+
* control; focus another node in the same handler if needed.
|
|
78
|
+
*/
|
|
79
|
+
onOpenAutoFocus?: (event: Event) => void;
|
|
80
|
+
/**
|
|
81
|
+
* Called when focus is about to leave the panel as it closes.
|
|
82
|
+
* Call `event.preventDefault()` to stop restoring focus to the trigger;
|
|
83
|
+
* focus another node in the same handler if needed.
|
|
84
|
+
* Still runs when `restoreFocus` is false.
|
|
85
|
+
*/
|
|
86
|
+
onCloseAutoFocus?: (event: Event) => void;
|
|
87
|
+
/**
|
|
88
|
+
* Called when the user interacts outside the panel.
|
|
89
|
+
* Call `event.preventDefault()` to keep the panel open. Clicks on
|
|
90
|
+
* `DsPopover.Anchor` already do not dismiss.
|
|
91
|
+
*/
|
|
92
|
+
onInteractOutside?: (event: Event) => void;
|
|
35
93
|
/** Fires when the popover opens or closes. */
|
|
36
94
|
onOpenChange?: (open: boolean) => void;
|
|
37
95
|
}
|
|
@@ -42,6 +100,12 @@ export interface DsPopoverTriggerProps {
|
|
|
42
100
|
className?: string;
|
|
43
101
|
}
|
|
44
102
|
|
|
103
|
+
export interface DsPopoverAnchorProps {
|
|
104
|
+
/** Single element the panel positions against. Wraps the trigger when they share a field. */
|
|
105
|
+
children: ReactNode;
|
|
106
|
+
className?: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
45
109
|
export interface DsPopoverPanelProps {
|
|
46
110
|
/**
|
|
47
111
|
* Panel width in pixels.
|
|
@@ -52,6 +116,7 @@ export interface DsPopoverPanelProps {
|
|
|
52
116
|
style?: CSSProperties;
|
|
53
117
|
children: ReactNode;
|
|
54
118
|
ref?: Ref<HTMLDivElement>;
|
|
119
|
+
id?: string;
|
|
55
120
|
'aria-label'?: string;
|
|
56
121
|
}
|
|
57
122
|
|