@dxos/react-ui-menu 0.7.5-main.937ce75

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.
Files changed (55) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +3 -0
  3. package/dist/lib/browser/index.mjs +231 -0
  4. package/dist/lib/browser/index.mjs.map +7 -0
  5. package/dist/lib/browser/meta.json +1 -0
  6. package/dist/lib/node/index.cjs +248 -0
  7. package/dist/lib/node/index.cjs.map +7 -0
  8. package/dist/lib/node/meta.json +1 -0
  9. package/dist/lib/node-esm/index.mjs +233 -0
  10. package/dist/lib/node-esm/index.mjs.map +7 -0
  11. package/dist/lib/node-esm/meta.json +1 -0
  12. package/dist/types/src/components/ActionLabel.d.ts +7 -0
  13. package/dist/types/src/components/ActionLabel.d.ts.map +1 -0
  14. package/dist/types/src/components/DropdownMenu.d.ts +19 -0
  15. package/dist/types/src/components/DropdownMenu.d.ts.map +1 -0
  16. package/dist/types/src/components/Toolbar/Toolbar.d.ts +4 -0
  17. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  18. package/dist/types/src/components/Toolbar/ToolbarDropdownMenu.d.ts +4 -0
  19. package/dist/types/src/components/Toolbar/ToolbarDropdownMenu.d.ts.map +1 -0
  20. package/dist/types/src/components/Toolbar/ToolbarToggleGroup.d.ts +4 -0
  21. package/dist/types/src/components/Toolbar/ToolbarToggleGroup.d.ts.map +1 -0
  22. package/dist/types/src/components/Toolbar/defs.d.ts +30 -0
  23. package/dist/types/src/components/Toolbar/defs.d.ts.map +1 -0
  24. package/dist/types/src/components/Toolbar/index.d.ts +3 -0
  25. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  26. package/dist/types/src/components/index.d.ts +3 -0
  27. package/dist/types/src/components/index.d.ts.map +1 -0
  28. package/dist/types/src/defs.d.ts +18 -0
  29. package/dist/types/src/defs.d.ts.map +1 -0
  30. package/dist/types/src/index.d.ts +3 -0
  31. package/dist/types/src/index.d.ts.map +1 -0
  32. package/dist/types/src/testing/Menus.stories.d.ts +22 -0
  33. package/dist/types/src/testing/Menus.stories.d.ts.map +1 -0
  34. package/dist/types/src/testing/index.d.ts +26 -0
  35. package/dist/types/src/testing/index.d.ts.map +1 -0
  36. package/dist/types/src/translations.d.ts +8 -0
  37. package/dist/types/src/translations.d.ts.map +1 -0
  38. package/dist/types/src/util.d.ts +4 -0
  39. package/dist/types/src/util.d.ts.map +1 -0
  40. package/dist/types/tsconfig.tsbuildinfo +1 -0
  41. package/package.json +52 -0
  42. package/src/components/ActionLabel.tsx +30 -0
  43. package/src/components/DropdownMenu.tsx +104 -0
  44. package/src/components/Toolbar/Toolbar.tsx +71 -0
  45. package/src/components/Toolbar/ToolbarDropdownMenu.tsx +36 -0
  46. package/src/components/Toolbar/ToolbarToggleGroup.tsx +39 -0
  47. package/src/components/Toolbar/defs.ts +36 -0
  48. package/src/components/Toolbar/index.ts +6 -0
  49. package/src/components/index.ts +6 -0
  50. package/src/defs.ts +23 -0
  51. package/src/index.ts +6 -0
  52. package/src/testing/Menus.stories.tsx +55 -0
  53. package/src/testing/index.ts +92 -0
  54. package/src/translations.ts +13 -0
  55. package/src/util.ts +14 -0
@@ -0,0 +1,104 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useControllableState } from '@radix-ui/react-use-controllable-state';
6
+ import React, { type MouseEvent, type MutableRefObject, type PropsWithChildren, useCallback } from 'react';
7
+
8
+ import { DropdownMenu as NaturalDropdownMenu, Icon } from '@dxos/react-ui';
9
+
10
+ import { ActionLabel } from './ActionLabel';
11
+ import { type MenuAction, type MenuProps } from '../defs';
12
+
13
+ export type DropdownMenuProps = MenuProps &
14
+ PropsWithChildren<
15
+ Partial<{
16
+ defaultMenuOpen: boolean;
17
+ menuOpen: boolean;
18
+ onMenuOpenChange: (nextOpen: boolean) => void;
19
+ suppressNextTooltip?: MutableRefObject<boolean>;
20
+ }>
21
+ >;
22
+
23
+ const DropdownMenuItem = ({
24
+ action,
25
+ onClick,
26
+ }: {
27
+ action: MenuAction;
28
+ onClick: (action: MenuAction, event: MouseEvent) => void;
29
+ }) => {
30
+ const handleClick = useCallback((event: MouseEvent) => onClick(action, event), [action, onClick]);
31
+ return (
32
+ <NaturalDropdownMenu.Item
33
+ onClick={handleClick}
34
+ classNames='gap-2'
35
+ disabled={action.properties?.disabled}
36
+ {...(action.properties?.testId && { 'data-testid': action.properties.testId })}
37
+ >
38
+ {action.properties?.icon && <Icon icon={action.properties!.icon} size={4} />}
39
+ <ActionLabel action={action} />
40
+ </NaturalDropdownMenu.Item>
41
+ );
42
+ };
43
+
44
+ const DropdownMenuRoot = ({
45
+ defaultMenuOpen,
46
+ actions,
47
+ menuOpen,
48
+ suppressNextTooltip,
49
+ onMenuOpenChange,
50
+ onAction,
51
+ children,
52
+ }: DropdownMenuProps) => {
53
+ const [optionsMenuOpen, setOptionsMenuOpen] = useControllableState({
54
+ prop: menuOpen,
55
+ defaultProp: defaultMenuOpen,
56
+ onChange: onMenuOpenChange,
57
+ });
58
+
59
+ const handleActionClick = useCallback(
60
+ (action: MenuAction, event: MouseEvent) => {
61
+ if (action.properties?.disabled) {
62
+ return;
63
+ }
64
+ event.stopPropagation();
65
+ // TODO(thure): Why does Dialog’s modal-ness cause issues if we don’t explicitly close the menu here?
66
+ if (suppressNextTooltip) {
67
+ suppressNextTooltip.current = true;
68
+ }
69
+ setOptionsMenuOpen(false);
70
+ onAction?.(action);
71
+ },
72
+ [onAction],
73
+ );
74
+
75
+ return (
76
+ <NaturalDropdownMenu.Root
77
+ {...{
78
+ open: optionsMenuOpen,
79
+ onOpenChange: (nextOpen: boolean) => {
80
+ if (!nextOpen && suppressNextTooltip) {
81
+ suppressNextTooltip.current = true;
82
+ }
83
+ return setOptionsMenuOpen(nextOpen);
84
+ },
85
+ }}
86
+ >
87
+ {children}
88
+ <NaturalDropdownMenu.Portal>
89
+ <NaturalDropdownMenu.Content>
90
+ <NaturalDropdownMenu.Viewport>
91
+ {actions?.map((action) => <DropdownMenuItem key={action.id} action={action} onClick={handleActionClick} />)}
92
+ </NaturalDropdownMenu.Viewport>
93
+ <NaturalDropdownMenu.Arrow />
94
+ </NaturalDropdownMenu.Content>
95
+ </NaturalDropdownMenu.Portal>
96
+ </NaturalDropdownMenu.Root>
97
+ );
98
+ };
99
+
100
+ export const DropdownMenu = {
101
+ Root: DropdownMenuRoot,
102
+ Trigger: NaturalDropdownMenu.Trigger,
103
+ VirtualTrigger: NaturalDropdownMenu.VirtualTrigger,
104
+ };
@@ -0,0 +1,71 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import { Toolbar as NaturalToolbar } from '@dxos/react-ui';
8
+
9
+ import { ToolbarDropdownMenu } from './ToolbarDropdownMenu';
10
+ import { ToolbarToggleGroup } from './ToolbarToggleGroup';
11
+ import { isMenu, isSeparator, type ToolbarProps } from './defs';
12
+ import { type MenuAction } from '../../defs';
13
+ import { ActionLabel } from '../ActionLabel';
14
+
15
+ const ToolbarItem = ({
16
+ iconSize,
17
+ action,
18
+ onAction,
19
+ }: Pick<ToolbarProps, 'iconSize' | 'onAction'> & {
20
+ action: MenuAction;
21
+ }) => {
22
+ const handleClick = useCallback(() => {
23
+ onAction?.(action);
24
+ }, [action, onAction]);
25
+ const { icon, iconOnly = true, disabled, testId } = action.properties;
26
+ return (
27
+ <NaturalToolbar.IconButton
28
+ key={action.id}
29
+ iconOnly={iconOnly}
30
+ variant='ghost'
31
+ icon={icon}
32
+ size={iconSize}
33
+ label={<ActionLabel action={action} />}
34
+ disabled={disabled}
35
+ onClick={handleClick}
36
+ {...(testId && { 'data-testid': testId })}
37
+ />
38
+ );
39
+ };
40
+
41
+ export const Toolbar = ({ actions: items, onAction, iconSize = 5, graph, ...props }: ToolbarProps) => {
42
+ return (
43
+ <NaturalToolbar.Root {...props}>
44
+ {items?.map((item, i) =>
45
+ isSeparator(item) ? (
46
+ <NaturalToolbar.Separator key={i} variant={item.properties.variant} />
47
+ ) : isMenu(item) ? (
48
+ item.properties.variant === 'dropdownMenu' ? (
49
+ <ToolbarDropdownMenu
50
+ key={item.id}
51
+ actionGroup={item}
52
+ onAction={onAction}
53
+ iconSize={iconSize}
54
+ graph={graph}
55
+ />
56
+ ) : (
57
+ <ToolbarToggleGroup
58
+ key={item.id}
59
+ actionGroup={item}
60
+ onAction={onAction}
61
+ iconSize={iconSize}
62
+ graph={graph}
63
+ />
64
+ )
65
+ ) : (
66
+ <ToolbarItem key={item.id} action={item as MenuAction} onAction={onAction} iconSize={iconSize} />
67
+ ),
68
+ )}
69
+ </NaturalToolbar.Root>
70
+ );
71
+ };
@@ -0,0 +1,36 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { useMemo, useRef } from 'react';
6
+
7
+ import { Toolbar as NaturalToolbar } from '@dxos/react-ui';
8
+
9
+ import { type ToolbarActionGroupProps } from './defs';
10
+ import { type MenuAction } from '../../defs';
11
+ import { ActionLabel } from '../ActionLabel';
12
+ import { DropdownMenu } from '../DropdownMenu';
13
+
14
+ export const ToolbarDropdownMenu = ({ actionGroup, graph, onAction, applyActiveIcon }: ToolbarActionGroupProps) => {
15
+ const menuActions = useMemo(() => (graph ? graph.actions(actionGroup) : []) as MenuAction[], [actionGroup, graph]);
16
+ const { icon, iconOnly = true, disabled, testId } = actionGroup.properties;
17
+ const suppressNextTooltip = useRef(false);
18
+
19
+ return (
20
+ <DropdownMenu.Root actions={menuActions} onAction={onAction} suppressNextTooltip={suppressNextTooltip}>
21
+ <DropdownMenu.Trigger asChild>
22
+ <NaturalToolbar.IconButton
23
+ size={5}
24
+ variant='ghost'
25
+ iconOnly={iconOnly}
26
+ disabled={disabled}
27
+ icon={(applyActiveIcon && menuActions.find((action) => !!action.properties.checked)?.properties.icon) || icon}
28
+ label={<ActionLabel action={actionGroup} />}
29
+ {...(testId && { 'data-testid': testId })}
30
+ suppressNextTooltip={suppressNextTooltip}
31
+ caretDown
32
+ />
33
+ </DropdownMenu.Trigger>
34
+ </DropdownMenu.Root>
35
+ );
36
+ };
@@ -0,0 +1,39 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { useMemo } from 'react';
6
+
7
+ import { Toolbar as NaturalToolbar } from '@dxos/react-ui';
8
+
9
+ import type { ToolbarActionGroupProps } from './defs';
10
+ import { type MenuAction } from '../../defs';
11
+ import { ActionLabel } from '../ActionLabel';
12
+
13
+ const ToolbarToggleGroupItem = ({ action }: { action: MenuAction }) => {
14
+ const { icon, iconOnly, disabled } = action.properties;
15
+ return (
16
+ <NaturalToolbar.ToggleGroupIconItem
17
+ key={action.id}
18
+ value={action.id}
19
+ icon={icon}
20
+ iconOnly={iconOnly}
21
+ disabled={disabled}
22
+ label={<ActionLabel action={action} />}
23
+ />
24
+ );
25
+ };
26
+
27
+ export const ToolbarToggleGroup = ({ actionGroup, graph }: ToolbarActionGroupProps) => {
28
+ const menuActions = useMemo(() => (graph ? (graph.actions(actionGroup) as MenuAction[]) : []), [actionGroup, graph]);
29
+ const { selectCardinality, value } = actionGroup.properties;
30
+ return (
31
+ // TODO(thure): This is extremely difficult to manage, what do?
32
+ // @ts-ignore
33
+ <NaturalToolbar.ToggleGroup type={selectCardinality} value={value}>
34
+ {menuActions.map((action) => (
35
+ <ToolbarToggleGroupItem key={action.id} action={action} />
36
+ ))}
37
+ </NaturalToolbar.ToggleGroup>
38
+ );
39
+ };
@@ -0,0 +1,36 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Node, type ActionGroup, type Graph, ACTION_GROUP_TYPE } from '@dxos/app-graph';
6
+ import { type ToolbarRootProps, type ToolbarSeparatorProps, type IconButtonProps } from '@dxos/react-ui';
7
+
8
+ import { type MenuAction, type MenuActionProperties, type MenuProps } from '../../defs';
9
+
10
+ export const ToolbarSeparatorType = '@dxos/react-ui-toolbar/separator' as const;
11
+
12
+ export type ToolbarSeparatorNode = Node<never, Pick<ToolbarSeparatorProps, 'variant'>> & {
13
+ type: typeof ToolbarSeparatorType;
14
+ };
15
+
16
+ export const isSeparator = (node: Node): node is ToolbarSeparatorNode => node.type === ToolbarSeparatorType;
17
+
18
+ export type ToolbarActionGroupProperties = Omit<MenuActionProperties, 'variant'> & {
19
+ variant: 'dropdownMenu' | 'toggleGroup';
20
+ } & ({ selectCardinality?: 'single'; value?: string } | { selectCardinality?: 'multiple'; value?: string[] });
21
+
22
+ export type ToolbarActionGroup = ActionGroup<ToolbarActionGroupProperties>;
23
+
24
+ export const isMenu = (node: Node): node is ToolbarActionGroup => node.type === ACTION_GROUP_TYPE;
25
+
26
+ export type ToolbarItem = ToolbarSeparatorNode | MenuAction | ToolbarActionGroup;
27
+
28
+ export type ToolbarProps = ToolbarRootProps &
29
+ MenuProps<ToolbarItem, MenuAction> & { iconSize?: IconButtonProps['size']; graph?: Graph };
30
+
31
+ export type ToolbarActionGroupProps = Pick<ToolbarProps, 'iconSize'> &
32
+ Omit<MenuProps<MenuAction>, 'actions'> & {
33
+ actionGroup: ToolbarActionGroup;
34
+ graph?: Graph;
35
+ applyActiveIcon?: boolean;
36
+ };
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './Toolbar';
6
+ export * from './defs';
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './DropdownMenu';
6
+ export * from './Toolbar';
package/src/defs.ts ADDED
@@ -0,0 +1,23 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+ import { type Node, type Action } from '@dxos/app-graph';
5
+ import { type Label } from '@dxos/react-ui';
6
+
7
+ export type MenuActionProperties = {
8
+ label: Label;
9
+ icon: string;
10
+ value?: string;
11
+ disabled?: boolean;
12
+ iconOnly?: boolean;
13
+ testId?: string;
14
+ variant?: 'action' | 'toggle';
15
+ checked?: boolean;
16
+ };
17
+
18
+ export type MenuAction = Action<MenuActionProperties>;
19
+
20
+ export type MenuProps<A extends Node = Action<MenuActionProperties>, P extends Node = A> = {
21
+ actions?: A[];
22
+ onAction?: (action: P) => void;
23
+ };
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './components';
6
+ export * from './defs';
@@ -0,0 +1,55 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import React from 'react';
8
+
9
+ import { faker } from '@dxos/random';
10
+ import { IconButton } from '@dxos/react-ui';
11
+ import { withTheme, withLayout, withSignals } from '@dxos/storybook-utils';
12
+
13
+ import { createActions, createNestedActionGraph, useMutateActions } from './index';
14
+ import { Toolbar as NaturalToolbar, DropdownMenu as NaturalDropdownMenu } from '../components';
15
+ import { type MenuAction } from '../defs';
16
+ import translations from '../translations';
17
+
18
+ faker.seed(1234);
19
+
20
+ export default {
21
+ title: 'ui/react-ui-menu/Menus',
22
+ component: NaturalToolbar,
23
+ decorators: [withTheme, withLayout({ tooltips: true }), withSignals],
24
+ parameters: { translations },
25
+ };
26
+
27
+ const menuActions = createActions() as MenuAction[];
28
+ const nestedMenuActions = createNestedActionGraph();
29
+
30
+ const handleAction = (action: MenuAction) => console.log('[on action]', action);
31
+
32
+ export const DropdownMenu = {
33
+ render: () => {
34
+ useMutateActions(menuActions);
35
+ return (
36
+ <NaturalDropdownMenu.Root actions={menuActions} onAction={handleAction}>
37
+ <NaturalDropdownMenu.Trigger asChild>
38
+ <IconButton icon='ph--list-checks--regular' size={5} label='Options' />
39
+ </NaturalDropdownMenu.Trigger>
40
+ </NaturalDropdownMenu.Root>
41
+ );
42
+ },
43
+ };
44
+
45
+ export const Toolbar = {
46
+ render: () => {
47
+ return (
48
+ <NaturalToolbar
49
+ actions={nestedMenuActions.topLevelActions}
50
+ onAction={handleAction}
51
+ graph={nestedMenuActions.graph}
52
+ />
53
+ );
54
+ },
55
+ };
@@ -0,0 +1,92 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useEffect } from 'react';
6
+
7
+ import { type Action, Graph, type NodeArg, ACTION_TYPE, ACTION_GROUP_TYPE, actionGroupSymbol } from '@dxos/app-graph';
8
+ import { create } from '@dxos/live-object';
9
+ import { faker } from '@dxos/random';
10
+ import { type DeepWriteable } from '@dxos/util';
11
+
12
+ import { type ToolbarItem } from '../components';
13
+ import { type MenuAction } from '../defs';
14
+
15
+ export type CreateActionsParams = Partial<{
16
+ type?: typeof ACTION_TYPE | typeof ACTION_GROUP_TYPE;
17
+ callback: () => void;
18
+ count: number;
19
+ }>;
20
+
21
+ const icons = {
22
+ regular: [
23
+ 'ph--text-b--regular',
24
+ 'ph--text-italic--regular',
25
+ 'ph--text-h-five--regular',
26
+ 'ph--chat-text--regular',
27
+ 'ph--clipboard-text--regular',
28
+ 'ph--link-simple--regular',
29
+ ],
30
+ fill: [
31
+ 'ph--text-b--fill',
32
+ 'ph--text-italic--fill',
33
+ 'ph--text-h-five--fill',
34
+ 'ph--chat-text--fill',
35
+ 'ph--clipboard-text--fill',
36
+ 'ph--link-simple--fill',
37
+ ],
38
+ };
39
+
40
+ export const createActions = (params?: CreateActionsParams) => {
41
+ // eslint-disable-next-line no-console
42
+ const { callback = () => console.log('invoke'), count = 12, type = ACTION_TYPE } = params ?? {};
43
+ return faker.helpers.multiple(
44
+ () =>
45
+ create({
46
+ id: faker.string.uuid(),
47
+ type,
48
+ data: type === ACTION_GROUP_TYPE ? actionGroupSymbol : callback,
49
+ properties: {
50
+ label: faker.lorem.words(2),
51
+ icon: faker.helpers.arrayElement(icons[faker.helpers.arrayElement(Object.keys(icons)) as keyof typeof icons]),
52
+ disabled: faker.helpers.arrayElement([true, false]),
53
+ ...(type === ACTION_GROUP_TYPE && { variant: 'dropdownMenu' }),
54
+ },
55
+ }),
56
+ { count },
57
+ );
58
+ };
59
+
60
+ export const createNestedActionGraph = (groupParams?: CreateActionsParams, params?: CreateActionsParams) => {
61
+ const graph = new Graph();
62
+ const actionGroups = createActions({ type: ACTION_GROUP_TYPE, ...groupParams });
63
+ actionGroups.forEach((group) => {
64
+ const actions = createActions(params);
65
+ // TODO(thure): these methods exist on `graph` but are marked as `@internal`; how should consumers properly build
66
+ // graph “islands” for the purposes of these components?
67
+ // @ts-ignore
68
+ graph._addNodes([group as NodeArg<any>, ...(actions as NodeArg<any>[])]);
69
+ // @ts-ignore
70
+ graph._addEdges(actions.map((action) => ({ source: group.id, target: action.id })));
71
+ void graph.expand(group);
72
+ });
73
+ return { graph, topLevelActions: actionGroups as ToolbarItem[] };
74
+ };
75
+
76
+ export const mutateActionsOnInterval = (actions: Action[]) => {
77
+ let cursor = 0;
78
+ return setInterval(() => {
79
+ const action = actions[cursor] as DeepWriteable<Action>;
80
+ action.properties.icon = faker.helpers.arrayElement(
81
+ action.properties.icon.endsWith('regular') ? icons.fill : icons.regular,
82
+ );
83
+ action.properties.disabled = !action.properties.disabled;
84
+ cursor = (cursor + actions.length + 1) % actions.length;
85
+ }, 1_000);
86
+ };
87
+
88
+ export const useMutateActions = (actions: MenuAction[]) =>
89
+ useEffect(() => {
90
+ const interval = mutateActionsOnInterval(actions);
91
+ return () => clearInterval(interval);
92
+ }, []);
@@ -0,0 +1,13 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export const translationKey = 'menus';
6
+
7
+ export default [
8
+ {
9
+ 'en-US': {
10
+ [translationKey]: {},
11
+ },
12
+ },
13
+ ] as const;
package/src/util.ts ADDED
@@ -0,0 +1,14 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type ActionLike } from '@dxos/app-graph';
6
+ import { getHostPlatform } from '@dxos/util';
7
+
8
+ export const getShortcut = (action: ActionLike) => {
9
+ return typeof action.properties?.keyBinding === 'string'
10
+ ? action.properties.keyBinding
11
+ : action.properties?.keyBinding?.[getHostPlatform()];
12
+ };
13
+
14
+ export const fallbackIcon = 'ph--placeholder--regular';