@dxos/react-ui 0.8.4-main.8360d9e660 → 0.8.4-main.8baae0fced
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/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/browser/chunk-A5QCIG5R.mjs +24 -0
- package/dist/lib/browser/chunk-A5QCIG5R.mjs.map +7 -0
- package/dist/lib/browser/{chunk-EJSGYGYH.mjs → chunk-LY5XDQR5.mjs} +84 -12
- package/dist/lib/browser/chunk-LY5XDQR5.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1322 -811
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +30 -24
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/translations.mjs +9 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-B7MXDDMJ.mjs → chunk-NGKLIKP3.mjs} +84 -12
- package/dist/lib/node-esm/chunk-NGKLIKP3.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-XCFLA74M.mjs +26 -0
- package/dist/lib/node-esm/chunk-XCFLA74M.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1322 -811
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +30 -24
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/translations.mjs +10 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/types/src/components/Avatars/Avatar.d.ts +1 -1
- package/dist/types/src/components/Avatars/Avatar.d.ts.map +1 -1
- package/dist/types/src/components/Avatars/Avatar.stories.d.ts.map +1 -1
- package/dist/types/src/components/Avatars/AvatarGroup.stories.d.ts.map +1 -1
- package/dist/types/src/components/Breadcrumb/Breadcrumb.d.ts.map +1 -1
- package/dist/types/src/components/Breadcrumb/Breadcrumb.stories.d.ts.map +1 -1
- package/dist/types/src/components/Button/Button.d.ts.map +1 -1
- package/dist/types/src/components/Button/Button.stories.d.ts +1 -1
- package/dist/types/src/components/Button/Button.stories.d.ts.map +1 -1
- package/dist/types/src/components/Button/IconButton.d.ts +1 -0
- package/dist/types/src/components/Button/IconButton.d.ts.map +1 -1
- package/dist/types/src/components/Button/IconButton.stories.d.ts +3 -0
- package/dist/types/src/components/Button/IconButton.stories.d.ts.map +1 -1
- package/dist/types/src/components/Button/Toggle.stories.d.ts.map +1 -1
- package/dist/types/src/components/Button/ToggleGroup.d.ts +2 -2
- package/dist/types/src/components/Button/ToggleGroup.stories.d.ts +2 -2
- package/dist/types/src/components/Button/ToggleGroup.stories.d.ts.map +1 -1
- package/dist/types/src/components/Card/Card.d.ts +59 -42
- package/dist/types/src/components/Card/Card.d.ts.map +1 -1
- package/dist/types/src/components/Card/Card.stories.d.ts +2 -2
- package/dist/types/src/components/Card/Card.stories.d.ts.map +1 -1
- package/dist/types/src/components/Carousel/Carousel.d.ts +106 -0
- package/dist/types/src/components/Carousel/Carousel.d.ts.map +1 -0
- package/dist/types/src/components/Carousel/index.d.ts +2 -0
- package/dist/types/src/components/Carousel/index.d.ts.map +1 -0
- package/dist/types/src/components/Clipboard/ClipboardProvider.d.ts.map +1 -1
- package/dist/types/src/components/Clipboard/CopyButton.d.ts.map +1 -1
- package/dist/types/src/components/Clipboard/index.d.ts +1 -1
- package/dist/types/src/components/Clipboard/index.d.ts.map +1 -1
- package/dist/types/src/components/DensityProvider/DensityProvider.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/AlertDialog.d.ts +34 -23
- package/dist/types/src/components/Dialog/AlertDialog.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/AlertDialog.stories.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/Dialog.d.ts +38 -21
- package/dist/types/src/components/Dialog/Dialog.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/Dialog.stories.d.ts +3 -2
- package/dist/types/src/components/Dialog/Dialog.stories.d.ts.map +1 -1
- package/dist/types/src/components/ElevationProvider/ElevationProvider.d.ts.map +1 -1
- package/dist/types/src/components/ErrorFallback/ErrorFallback.d.ts.map +1 -1
- package/dist/types/src/components/ErrorFallback/ErrorFallback.stories.d.ts.map +1 -1
- package/dist/types/src/components/ErrorFallback/ErrorStack.d.ts +14 -3
- package/dist/types/src/components/ErrorFallback/ErrorStack.d.ts.map +1 -1
- package/dist/types/src/components/ErrorFallback/ThrowError.d.ts.map +1 -1
- package/dist/types/src/components/Focus/Focus.d.ts +36 -0
- package/dist/types/src/components/Focus/Focus.d.ts.map +1 -0
- package/dist/types/src/components/Focus/Focus.stories.d.ts +9 -0
- package/dist/types/src/components/Focus/Focus.stories.d.ts.map +1 -0
- package/dist/types/src/components/Focus/index.d.ts +2 -0
- package/dist/types/src/components/Focus/index.d.ts.map +1 -0
- package/dist/types/src/components/Icon/Icon.d.ts +4 -0
- package/dist/types/src/components/Icon/Icon.d.ts.map +1 -1
- package/dist/types/src/components/Icon/Icon.stories.d.ts +11 -3
- package/dist/types/src/components/Icon/Icon.stories.d.ts.map +1 -1
- package/dist/types/src/components/Image/Image.d.ts +2 -1
- package/dist/types/src/components/Image/Image.d.ts.map +1 -1
- package/dist/types/src/components/Image/Image.stories.d.ts +3 -2
- package/dist/types/src/components/Image/Image.stories.d.ts.map +1 -1
- package/dist/types/src/components/Input/Input.d.ts +12 -15
- package/dist/types/src/components/Input/Input.d.ts.map +1 -1
- package/dist/types/src/components/Input/Input.stories.d.ts +3 -3
- package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
- package/dist/types/src/components/Link/Link.d.ts.map +1 -1
- package/dist/types/src/components/Link/Link.stories.d.ts.map +1 -1
- package/dist/types/src/components/List/List.d.ts +5 -3
- package/dist/types/src/components/List/List.d.ts.map +1 -1
- package/dist/types/src/components/List/List.stories.d.ts +3 -1
- package/dist/types/src/components/List/List.stories.d.ts.map +1 -1
- package/dist/types/src/components/List/ListDropIndicator.d.ts.map +1 -1
- package/dist/types/src/components/List/Tree.d.ts +2 -2
- package/dist/types/src/components/List/Tree.d.ts.map +1 -1
- package/dist/types/src/components/List/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/components/List/TreeDropIndicator.d.ts.map +1 -1
- package/dist/types/src/components/List/Treegrid.d.ts +5 -9
- package/dist/types/src/components/List/Treegrid.d.ts.map +1 -1
- package/dist/types/src/components/List/Treegrid.stories.d.ts.map +1 -1
- package/dist/types/src/components/Main/Main.d.ts +7 -3
- package/dist/types/src/components/Main/Main.d.ts.map +1 -1
- package/dist/types/src/components/Main/Main.stories.d.ts.map +1 -1
- package/dist/types/src/components/Main/useSwipeToDismiss.d.ts.map +1 -1
- package/dist/types/src/components/MediaPlayer/MediaPlayer.d.ts +46 -0
- package/dist/types/src/components/MediaPlayer/MediaPlayer.d.ts.map +1 -0
- package/dist/types/src/components/MediaPlayer/MediaPlayer.stories.d.ts +16 -0
- package/dist/types/src/components/MediaPlayer/MediaPlayer.stories.d.ts.map +1 -0
- package/dist/types/src/components/MediaPlayer/index.d.ts +2 -0
- package/dist/types/src/components/MediaPlayer/index.d.ts.map +1 -0
- package/dist/types/src/components/Menu/ContextMenu.d.ts.map +1 -1
- package/dist/types/src/components/Menu/ContextMenu.stories.d.ts.map +1 -1
- package/dist/types/src/components/Menu/DropdownMenu.d.ts +13 -6
- package/dist/types/src/components/Menu/DropdownMenu.d.ts.map +1 -1
- package/dist/types/src/components/Menu/DropdownMenu.stories.d.ts +9 -1
- package/dist/types/src/components/Menu/DropdownMenu.stories.d.ts.map +1 -1
- package/dist/types/src/components/Message/Message.d.ts +1 -1
- package/dist/types/src/components/Message/Message.d.ts.map +1 -1
- package/dist/types/src/components/Message/Message.stories.d.ts +2 -2
- package/dist/types/src/components/Message/Message.stories.d.ts.map +1 -1
- package/dist/types/src/components/Popover/Popover.d.ts +14 -3
- package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
- package/dist/types/src/components/Popover/Popover.stories.d.ts.map +1 -1
- package/dist/types/src/components/ScrollArea/ScrollArea.d.ts +12 -9
- package/dist/types/src/components/ScrollArea/ScrollArea.d.ts.map +1 -1
- package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +18 -5
- package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts.map +1 -1
- package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts +42 -13
- package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts.map +1 -1
- package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts +5 -6
- package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts.map +1 -1
- package/dist/types/src/components/Select/Select.d.ts.map +1 -1
- package/dist/types/src/components/Select/Select.stories.d.ts +2 -2
- package/dist/types/src/components/Select/Select.stories.d.ts.map +1 -1
- package/dist/types/src/components/Skeleton/Skeleton.stories.d.ts.map +1 -1
- package/dist/types/src/components/Splitter/Splitter.d.ts +19 -17
- package/dist/types/src/components/Splitter/Splitter.d.ts.map +1 -1
- package/dist/types/src/components/Splitter/Splitter.stories.d.ts.map +1 -1
- package/dist/types/src/components/Status/Status.d.ts +3 -4
- package/dist/types/src/components/Status/Status.d.ts.map +1 -1
- package/dist/types/src/components/Status/Status.stories.d.ts.map +1 -1
- package/dist/types/src/components/Tag/Tag.stories.d.ts.map +1 -1
- package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts +1 -1
- package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts.map +1 -1
- package/dist/types/src/components/ThemeProvider/ThemeProvider.stories.d.ts +1 -1
- package/dist/types/src/components/ThemeProvider/ThemeProvider.stories.d.ts.map +1 -1
- package/dist/types/src/components/ThemeProvider/TranslationsProvider.d.ts +54 -55
- package/dist/types/src/components/ThemeProvider/TranslationsProvider.d.ts.map +1 -1
- package/dist/types/src/components/ThemeProvider/index.d.ts +1 -1
- package/dist/types/src/components/ThemeProvider/index.d.ts.map +1 -1
- package/dist/types/src/components/Toast/Toast.d.ts +4 -4
- package/dist/types/src/components/Toast/Toast.d.ts.map +1 -1
- package/dist/types/src/components/Toast/Toast.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +11 -15
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/components/Tooltip/Tooltip.d.ts +8 -8
- package/dist/types/src/components/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts +2 -2
- package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +3 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/exemplars/generics.stories.d.ts +6 -5
- package/dist/types/src/exemplars/generics.stories.d.ts.map +1 -1
- package/dist/types/src/exemplars/slot.stories.d.ts +1 -0
- package/dist/types/src/exemplars/slot.stories.d.ts.map +1 -1
- package/dist/types/src/exemplars/tabster.stories.d.ts.map +1 -1
- package/dist/types/src/exemplars/virtualizer.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/useDensityContext.d.ts +1 -1
- package/dist/types/src/hooks/useDensityContext.d.ts.map +1 -1
- package/dist/types/src/hooks/useElevationContext.d.ts.map +1 -1
- package/dist/types/src/hooks/useIconHref.d.ts.map +1 -1
- package/dist/types/src/hooks/useSafeArea.d.ts.map +1 -1
- package/dist/types/src/hooks/useSafeCollisionPadding.d.ts.map +1 -1
- package/dist/types/src/hooks/useVisualViewport.d.ts.map +1 -1
- package/dist/types/src/playground/Controls.stories.d.ts.map +1 -1
- package/dist/types/src/playground/Custom.stories.d.ts +1 -1
- package/dist/types/src/playground/Custom.stories.d.ts.map +1 -1
- package/dist/types/src/playground/Typography.stories.d.ts.map +1 -1
- package/dist/types/src/primitives/Column/Column.d.ts +21 -14
- package/dist/types/src/primitives/Column/Column.d.ts.map +1 -1
- package/dist/types/src/primitives/Column/Column.stories.d.ts +19 -0
- package/dist/types/src/primitives/Column/Column.stories.d.ts.map +1 -1
- package/dist/types/src/primitives/Container/Container.d.ts +4 -5
- package/dist/types/src/primitives/Container/Container.d.ts.map +1 -1
- package/dist/types/src/primitives/Container/Container.stories.d.ts.map +1 -1
- package/dist/types/src/primitives/Flex/Flex.d.ts +5 -7
- package/dist/types/src/primitives/Flex/Flex.d.ts.map +1 -1
- package/dist/types/src/primitives/Flex/Flex.stories.d.ts.map +1 -1
- package/dist/types/src/primitives/Grid/Grid.d.ts +3 -8
- package/dist/types/src/primitives/Grid/Grid.d.ts.map +1 -1
- package/dist/types/src/primitives/Grid/Grid.stories.d.ts.map +1 -1
- package/dist/types/src/primitives/Panel/Panel.d.ts +24 -15
- package/dist/types/src/primitives/Panel/Panel.d.ts.map +1 -1
- package/dist/types/src/primitives/Panel/Panel.stories.d.ts.map +1 -1
- package/dist/types/src/testing/Loading.d.ts.map +1 -1
- package/dist/types/src/testing/decorators/withLayout.d.ts.map +1 -1
- package/dist/types/src/testing/decorators/withLayoutVariants.d.ts.map +1 -1
- package/dist/types/src/testing/decorators/withTheme.d.ts +1 -1
- package/dist/types/src/testing/decorators/withTheme.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +8 -3
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/usePx.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +29 -26
- package/src/components/Avatars/Avatar.stories.tsx +2 -3
- package/src/components/Avatars/Avatar.tsx +1 -2
- package/src/components/Avatars/AvatarGroup.stories.tsx +0 -1
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +1 -2
- package/src/components/Button/Button.stories.tsx +0 -1
- package/src/components/Button/Button.tsx +3 -11
- package/src/components/Button/IconButton.stories.tsx +6 -4
- package/src/components/Button/IconButton.tsx +3 -3
- package/src/components/Button/Toggle.stories.tsx +0 -1
- package/src/components/Button/ToggleGroup.stories.tsx +0 -1
- package/src/components/Card/Card.stories.tsx +15 -15
- package/src/components/Card/Card.tsx +190 -66
- package/src/components/Carousel/Carousel.tsx +379 -0
- package/src/components/Carousel/index.ts +5 -0
- package/src/components/Clipboard/CopyButton.tsx +5 -6
- package/src/components/Dialog/AlertDialog.stories.tsx +5 -6
- package/src/components/Dialog/AlertDialog.tsx +51 -93
- package/src/components/Dialog/Dialog.stories.tsx +64 -9
- package/src/components/Dialog/Dialog.tsx +66 -56
- package/src/components/ErrorFallback/ErrorFallback.stories.tsx +3 -8
- package/src/components/ErrorFallback/ErrorStack.tsx +36 -2
- package/src/components/Focus/AUDIT.md +43 -0
- package/src/components/Focus/Focus.stories.tsx +230 -0
- package/src/components/Focus/Focus.tsx +201 -0
- package/src/components/Focus/index.ts +5 -0
- package/src/components/Icon/Icon.stories.tsx +43 -13
- package/src/components/Icon/Icon.tsx +13 -3
- package/src/components/Image/Image.stories.tsx +3 -3
- package/src/components/Image/Image.tsx +31 -8
- package/src/components/Input/Input.stories.tsx +3 -4
- package/src/components/Input/Input.tsx +3 -3
- package/src/components/Link/Link.stories.tsx +0 -1
- package/src/components/Link/Link.tsx +10 -2
- package/src/components/List/List.stories.tsx +1 -2
- package/src/components/List/List.tsx +7 -6
- package/src/components/List/ListDropIndicator.tsx +0 -1
- package/src/components/List/Tree.stories.tsx +2 -3
- package/src/components/List/Tree.tsx +0 -1
- package/src/components/List/Treegrid.stories.tsx +26 -27
- package/src/components/List/Treegrid.tsx +14 -14
- package/src/components/Main/Main.stories.tsx +0 -1
- package/src/components/Main/Main.tsx +0 -1
- package/src/components/MediaPlayer/MediaPlayer.stories.tsx +50 -0
- package/src/components/MediaPlayer/MediaPlayer.tsx +153 -0
- package/src/components/MediaPlayer/index.ts +5 -0
- package/src/components/Menu/ContextMenu.stories.tsx +0 -1
- package/src/components/Menu/DropdownMenu.stories.tsx +0 -1
- package/src/components/Menu/DropdownMenu.tsx +3 -3
- package/src/components/Message/Message.stories.tsx +7 -8
- package/src/components/Message/Message.tsx +23 -10
- package/src/components/Popover/Popover.stories.tsx +4 -5
- package/src/components/Popover/Popover.tsx +1 -1
- package/src/components/ScrollArea/ScrollArea.stories.tsx +89 -30
- package/src/components/ScrollArea/ScrollArea.tsx +39 -23
- package/src/components/ScrollContainer/ScrollContainer.stories.tsx +19 -17
- package/src/components/ScrollContainer/ScrollContainer.tsx +199 -92
- package/src/components/Select/Select.stories.tsx +5 -6
- package/src/components/Skeleton/Skeleton.stories.tsx +0 -1
- package/src/components/Splitter/Splitter.stories.tsx +29 -29
- package/src/components/Splitter/Splitter.tsx +35 -34
- package/src/components/Status/Status.stories.tsx +0 -1
- package/src/components/Status/Status.tsx +8 -5
- package/src/components/Tag/Tag.stories.tsx +0 -1
- package/src/components/ThemeProvider/ThemeProvider.stories.tsx +0 -1
- package/src/components/ThemeProvider/ThemeProvider.tsx +5 -4
- package/src/components/ThemeProvider/index.ts +1 -1
- package/src/components/Toast/Toast.stories.tsx +0 -1
- package/src/components/Toolbar/Toolbar.stories.tsx +0 -1
- package/src/components/Toolbar/Toolbar.tsx +19 -15
- package/src/components/Tooltip/Tooltip.stories.tsx +7 -8
- package/src/components/Tooltip/Tooltip.tsx +14 -13
- package/src/components/index.ts +3 -0
- package/src/exemplars/generics.stories.tsx +7 -15
- package/src/exemplars/slot.stories.tsx +65 -57
- package/src/exemplars/tabster.stories.tsx +1 -1
- package/src/exemplars/virtualizer.stories.tsx +4 -5
- package/src/hooks/useDensityContext.ts +2 -2
- package/src/playground/Custom.stories.tsx +6 -9
- package/src/primitives/Column/AUDIT.md +148 -0
- package/src/primitives/Column/Column.stories.tsx +122 -19
- package/src/primitives/Column/Column.tsx +73 -41
- package/src/primitives/Container/Container.stories.tsx +0 -1
- package/src/primitives/Container/Container.tsx +5 -8
- package/src/primitives/Flex/Flex.stories.tsx +0 -1
- package/src/primitives/Flex/Flex.tsx +10 -12
- package/src/primitives/Grid/Grid.stories.tsx +0 -1
- package/src/primitives/Grid/Grid.tsx +4 -9
- package/src/primitives/Panel/Panel.stories.tsx +8 -7
- package/src/primitives/Panel/Panel.tsx +64 -63
- package/src/testing/Loading.tsx +25 -4
- package/src/testing/decorators/withLayout.tsx +7 -17
- package/src/testing/decorators/withTheme.tsx +10 -7
- package/src/translations.ts +8 -3
- package/src/util/usePx.ts +1 -0
- package/dist/lib/browser/chunk-EJSGYGYH.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-B7MXDDMJ.mjs.map +0 -7
|
@@ -8,7 +8,6 @@ import React from 'react';
|
|
|
8
8
|
import { type MessageValence } from '@dxos/ui-types';
|
|
9
9
|
|
|
10
10
|
import { withLayoutVariants, withTheme } from '../../testing';
|
|
11
|
-
|
|
12
11
|
import {
|
|
13
12
|
type CheckboxProps,
|
|
14
13
|
Input,
|
|
@@ -28,7 +27,7 @@ type VariantMap = {
|
|
|
28
27
|
|
|
29
28
|
type Variant = { [K in keyof VariantMap]: { type: K } & VariantMap[K] }[keyof VariantMap];
|
|
30
29
|
|
|
31
|
-
type
|
|
30
|
+
type DefaultStoryProps = Partial<{
|
|
32
31
|
kind: keyof VariantMap;
|
|
33
32
|
label: string;
|
|
34
33
|
labelVisuallyHidden: boolean;
|
|
@@ -47,7 +46,7 @@ const DefaultStory = ({
|
|
|
47
46
|
validationValence,
|
|
48
47
|
validationMessage,
|
|
49
48
|
...props
|
|
50
|
-
}:
|
|
49
|
+
}: DefaultStoryProps) => {
|
|
51
50
|
return (
|
|
52
51
|
<Input.Root {...{ validationValence }}>
|
|
53
52
|
<Input.Label srOnly={labelVisuallyHidden}>{label}</Input.Label>
|
|
@@ -75,7 +74,7 @@ const meta = {
|
|
|
75
74
|
|
|
76
75
|
export default meta;
|
|
77
76
|
|
|
78
|
-
type Story = StoryObj<
|
|
77
|
+
type Story = StoryObj<DefaultStoryProps & Variant>;
|
|
79
78
|
|
|
80
79
|
export const DensityCoarse: Story = {
|
|
81
80
|
args: {
|
|
@@ -130,13 +130,13 @@ type TextInputProps = InputSharedProps & ThemedClassName<TextInputPrimitiveProps
|
|
|
130
130
|
|
|
131
131
|
const TextInput = forwardRef<HTMLInputElement, InputScopedProps<TextInputProps>>(
|
|
132
132
|
(
|
|
133
|
-
{ __inputScope, classNames, density:
|
|
133
|
+
{ __inputScope, classNames, density: densityProp, elevation: elevationProp, variant, noAutoFill, ...props },
|
|
134
134
|
forwardedRef,
|
|
135
135
|
) => {
|
|
136
136
|
const { hasIosKeyboard } = useThemeContext();
|
|
137
137
|
const { tx } = useThemeContext();
|
|
138
|
-
const density = useDensityContext(
|
|
139
|
-
const elevation = useElevationContext(
|
|
138
|
+
const density = useDensityContext(densityProp);
|
|
139
|
+
const elevation = useElevationContext(elevationProp);
|
|
140
140
|
const { validationValence } = useInputContext(INPUT_NAME, __inputScope);
|
|
141
141
|
|
|
142
142
|
return (
|
|
@@ -16,9 +16,17 @@ export type LinkProps = ThemedClassName<ComponentPropsWithRef<typeof Primitive.a
|
|
|
16
16
|
}>;
|
|
17
17
|
|
|
18
18
|
export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
|
|
19
|
-
({ asChild, variant,
|
|
19
|
+
({ classNames, asChild, variant, target = '_blank', rel = 'noreferrer', ...props }, forwardedRef) => {
|
|
20
20
|
const { tx } = useThemeContext();
|
|
21
21
|
const Comp = asChild ? Slot : Primitive.a;
|
|
22
|
-
return
|
|
22
|
+
return (
|
|
23
|
+
<Comp
|
|
24
|
+
{...props}
|
|
25
|
+
target={target}
|
|
26
|
+
rel={rel}
|
|
27
|
+
className={tx('link.root', { variant }, classNames)}
|
|
28
|
+
ref={forwardedRef}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
23
31
|
},
|
|
24
32
|
);
|
|
@@ -13,7 +13,6 @@ import { getSize, ghostHover, mx, surfaceShadow } from '@dxos/ui-theme';
|
|
|
13
13
|
|
|
14
14
|
import { withTheme } from '../../testing';
|
|
15
15
|
import { Icon } from '../Icon';
|
|
16
|
-
|
|
17
16
|
import { List, ListItem, type ListScopedProps } from './List';
|
|
18
17
|
|
|
19
18
|
const meta = {
|
|
@@ -177,7 +176,7 @@ export const Collapsible: Story = {
|
|
|
177
176
|
<List {...args}>
|
|
178
177
|
{items.map(({ id, text, body }, index) => (
|
|
179
178
|
<ListItem.Root key={id} id={id} collapsible={index !== 2} defaultOpen={index % 2 === 0}>
|
|
180
|
-
<div
|
|
179
|
+
<div className='grow flex'>
|
|
181
180
|
{index !== 2 ? <ListItem.OpenTrigger /> : <ListItem.MockOpenTrigger />}
|
|
182
181
|
<ListItem.Heading classNames='grow pt-2'>{text}</ListItem.Heading>
|
|
183
182
|
<ListItem.Endcap>
|
|
@@ -24,24 +24,25 @@ import {
|
|
|
24
24
|
useListContext,
|
|
25
25
|
useListItemContext,
|
|
26
26
|
} from '@dxos/react-list';
|
|
27
|
+
import { composable, composableProps } from '@dxos/ui-theme';
|
|
27
28
|
import { type Density } from '@dxos/ui-types';
|
|
28
29
|
|
|
29
30
|
import { useDensityContext, useThemeContext } from '../../hooks';
|
|
30
31
|
import { type ThemedClassName } from '../../util';
|
|
31
32
|
import { DensityProvider } from '../DensityProvider';
|
|
32
33
|
import { Icon } from '../Icon';
|
|
33
|
-
|
|
34
34
|
import { ListDropIndicator } from './ListDropIndicator';
|
|
35
35
|
|
|
36
|
-
type ListProps = ThemedClassName<ListPrimitiveProps
|
|
36
|
+
type ListProps = ThemedClassName<ListPrimitiveProps & { density?: Density }>;
|
|
37
37
|
|
|
38
|
-
const List =
|
|
38
|
+
const List = composable<HTMLOListElement, ListProps>(({ children, ...props }, forwardedRef) => {
|
|
39
39
|
const { tx } = useThemeContext();
|
|
40
40
|
const density = useDensityContext(props.density);
|
|
41
|
+
const { className, ...rest } = composableProps(props);
|
|
41
42
|
|
|
42
43
|
return (
|
|
43
44
|
<DensityProvider density={density}>
|
|
44
|
-
<ListPrimitive {...
|
|
45
|
+
<ListPrimitive {...rest} className={tx('list.root', {}, className)} ref={forwardedRef}>
|
|
45
46
|
{children}
|
|
46
47
|
</ListPrimitive>
|
|
47
48
|
</DensityProvider>
|
|
@@ -74,7 +75,7 @@ const MockListItemOpenTrigger = ({
|
|
|
74
75
|
}: ThemedClassName<Omit<ComponentPropsWithoutRef<'div'>, 'children'>>) => {
|
|
75
76
|
const density = useDensityContext();
|
|
76
77
|
const { tx } = useThemeContext();
|
|
77
|
-
return <div
|
|
78
|
+
return <div {...props} className={tx('list.item.openTrigger', { density }, classNames)} />;
|
|
78
79
|
};
|
|
79
80
|
|
|
80
81
|
type ListItemHeadingProps = ThemedClassName<ListPrimitiveItemHeadingProps>;
|
|
@@ -89,7 +90,7 @@ const ListItemHeading = forwardRef<HTMLParagraphElement, ListItemHeadingProps>(
|
|
|
89
90
|
className={tx('list.item.heading', { density }, classNames)}
|
|
90
91
|
ref={forwardedRef}
|
|
91
92
|
>
|
|
92
|
-
{children}
|
|
93
|
+
<span>{children}</span>
|
|
93
94
|
</ListPrimitiveItemHeading>
|
|
94
95
|
);
|
|
95
96
|
},
|
|
@@ -6,7 +6,6 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
8
|
import { withTheme } from '../../testing';
|
|
9
|
-
|
|
10
9
|
import { Tree, TreeItem } from './Tree';
|
|
11
10
|
|
|
12
11
|
type StorybookTreeProps = {
|
|
@@ -25,7 +24,7 @@ const createKey = (key: string, prefix?: string) => (prefix === undefined ? key
|
|
|
25
24
|
const StorybookTreeItem = ({ data, prefix }: StorybookTreeItemProps) => {
|
|
26
25
|
const keys = Array.isArray(data) ? Array.from(data.keys()) : Object.keys(data);
|
|
27
26
|
return (
|
|
28
|
-
<Tree.Root
|
|
27
|
+
<Tree.Root>
|
|
29
28
|
{keys.map((key) => {
|
|
30
29
|
const id = createKey(String(key), prefix);
|
|
31
30
|
const value = data[key as keyof typeof data];
|
|
@@ -33,7 +32,7 @@ const StorybookTreeItem = ({ data, prefix }: StorybookTreeItemProps) => {
|
|
|
33
32
|
|
|
34
33
|
return (
|
|
35
34
|
<TreeItem.Root key={id} id={id} collapsible={!valueIsScalar} defaultOpen>
|
|
36
|
-
<div
|
|
35
|
+
<div className='grow flex'>
|
|
37
36
|
{valueIsScalar ? <TreeItem.MockOpenTrigger /> : <TreeItem.OpenTrigger />}
|
|
38
37
|
<TreeItem.Heading classNames='grow pt-1'>{valueIsScalar ? String(value) : key}</TreeItem.Heading>
|
|
39
38
|
</div>
|
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { random } from '@dxos/random';
|
|
9
9
|
|
|
10
10
|
import { withTheme } from '../../testing';
|
|
11
11
|
import { Icon } from '../Icon';
|
|
12
|
+
import { Treegrid, TREEGRID_PARENT_OF_SEPARATOR, TREEGRID_PATH_SEPARATOR } from './Treegrid';
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
faker.seed(1234);
|
|
14
|
+
random.seed(1234);
|
|
16
15
|
|
|
17
16
|
type StorybookNode = {
|
|
18
17
|
id: string;
|
|
@@ -32,33 +31,33 @@ const content = {
|
|
|
32
31
|
title: 'Root',
|
|
33
32
|
nodes: [
|
|
34
33
|
{
|
|
35
|
-
id:
|
|
34
|
+
id: random.string.uuid(),
|
|
36
35
|
title: 'Personal Space',
|
|
37
36
|
icon: 'ph--house--regular',
|
|
38
37
|
nodes: [
|
|
39
38
|
{
|
|
40
|
-
id:
|
|
41
|
-
title:
|
|
39
|
+
id: random.string.uuid(),
|
|
40
|
+
title: random.commerce.productName(),
|
|
42
41
|
},
|
|
43
42
|
{
|
|
44
|
-
id:
|
|
45
|
-
title:
|
|
43
|
+
id: random.string.uuid(),
|
|
44
|
+
title: random.commerce.productName(),
|
|
46
45
|
},
|
|
47
46
|
{
|
|
48
|
-
id:
|
|
49
|
-
title:
|
|
47
|
+
id: random.string.uuid(),
|
|
48
|
+
title: random.commerce.productName(),
|
|
50
49
|
nodes: [
|
|
51
50
|
{
|
|
52
|
-
id:
|
|
53
|
-
title:
|
|
51
|
+
id: random.string.uuid(),
|
|
52
|
+
title: random.commerce.productName(),
|
|
54
53
|
nodes: [
|
|
55
54
|
{
|
|
56
|
-
id:
|
|
57
|
-
title:
|
|
55
|
+
id: random.string.uuid(),
|
|
56
|
+
title: random.commerce.productName(),
|
|
58
57
|
},
|
|
59
58
|
{
|
|
60
|
-
id:
|
|
61
|
-
title:
|
|
59
|
+
id: random.string.uuid(),
|
|
60
|
+
title: random.commerce.productName(),
|
|
62
61
|
},
|
|
63
62
|
],
|
|
64
63
|
},
|
|
@@ -67,24 +66,24 @@ const content = {
|
|
|
67
66
|
],
|
|
68
67
|
},
|
|
69
68
|
{
|
|
70
|
-
id:
|
|
71
|
-
title:
|
|
69
|
+
id: random.string.uuid(),
|
|
70
|
+
title: random.commerce.productName(),
|
|
72
71
|
icon: 'ph--planet--regular',
|
|
73
72
|
nodes: [
|
|
74
73
|
{
|
|
75
|
-
id:
|
|
76
|
-
title:
|
|
74
|
+
id: random.string.uuid(),
|
|
75
|
+
title: random.commerce.productName(),
|
|
77
76
|
},
|
|
78
77
|
],
|
|
79
78
|
},
|
|
80
79
|
{
|
|
81
|
-
id:
|
|
82
|
-
title:
|
|
80
|
+
id: random.string.uuid(),
|
|
81
|
+
title: random.commerce.productName(),
|
|
83
82
|
icon: 'ph--sailboat--regular',
|
|
84
83
|
},
|
|
85
84
|
{
|
|
86
|
-
id:
|
|
87
|
-
title:
|
|
85
|
+
id: random.string.uuid(),
|
|
86
|
+
title: random.commerce.productName(),
|
|
88
87
|
icon: 'ph--planet--regular',
|
|
89
88
|
},
|
|
90
89
|
],
|
|
@@ -129,8 +128,8 @@ const DefaultStory = () => {
|
|
|
129
128
|
return (
|
|
130
129
|
<Treegrid.Row
|
|
131
130
|
key={node.id}
|
|
132
|
-
id={path.join(
|
|
133
|
-
{...(parentOf && { parentOf: parentOf.join(
|
|
131
|
+
id={path.join(TREEGRID_PATH_SEPARATOR)}
|
|
132
|
+
{...(parentOf && { parentOf: parentOf.join(TREEGRID_PARENT_OF_SEPARATOR) })}
|
|
134
133
|
>
|
|
135
134
|
<Treegrid.Cell indent classNames='flex items-center'>
|
|
136
135
|
{node.icon && <Icon icon={node.icon} classNames='w-[1em] h-[1em] my-1' />}
|
|
@@ -15,10 +15,12 @@ import React, {
|
|
|
15
15
|
useCallback,
|
|
16
16
|
} from 'react';
|
|
17
17
|
|
|
18
|
+
import { composable, composableProps } from '@dxos/ui-theme';
|
|
19
|
+
|
|
18
20
|
import { useThemeContext } from '../../hooks';
|
|
19
21
|
import { type ThemedClassName } from '../../util';
|
|
20
22
|
|
|
21
|
-
// TODO(thure):
|
|
23
|
+
// TODO(thure): https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/treegrid_role
|
|
22
24
|
|
|
23
25
|
const TREEGRID_ROW_NAME = 'TreegridRow';
|
|
24
26
|
|
|
@@ -34,17 +36,19 @@ type TreegridRowContextValue = {
|
|
|
34
36
|
const [TreegridRowProvider, useTreegridRowContext] =
|
|
35
37
|
createTreegridRowContext<TreegridRowContextValue>(TREEGRID_ROW_NAME);
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
+
// TODO(burdon): Replace with functions.
|
|
40
|
+
export const TREEGRID_PATH_SEPARATOR = '~';
|
|
41
|
+
export const TREEGRID_PARENT_OF_SEPARATOR = ' ';
|
|
39
42
|
|
|
40
43
|
type TreegridRootProps = ThemedClassName<ComponentPropsWithRef<typeof Primitive.div>> & {
|
|
41
44
|
gridTemplateColumns: CSSProperties['gridTemplateColumns'];
|
|
42
45
|
asChild?: boolean;
|
|
43
46
|
};
|
|
44
47
|
|
|
45
|
-
const TreegridRoot =
|
|
46
|
-
({ asChild, classNames, children, style, gridTemplateColumns, ...props }, forwardedRef) => {
|
|
48
|
+
const TreegridRoot = composable<HTMLDivElement, TreegridRootProps>(
|
|
49
|
+
({ asChild, classNames, children, style, gridTemplateColumns, onKeyDown: onKeyDownProp, ...props }, forwardedRef) => {
|
|
47
50
|
const { tx } = useThemeContext();
|
|
51
|
+
const { className, role: _role, ...rest } = composableProps<HTMLDivElement>(props, { classNames });
|
|
48
52
|
const Comp = asChild ? Slot : Primitive.div;
|
|
49
53
|
const { findFirstFocusable } = useFocusFinders();
|
|
50
54
|
|
|
@@ -89,16 +93,16 @@ const TreegridRoot = forwardRef<HTMLDivElement, TreegridRootProps>(
|
|
|
89
93
|
break;
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
|
-
|
|
96
|
+
onKeyDownProp?.(event);
|
|
93
97
|
},
|
|
94
|
-
[findFirstFocusable],
|
|
98
|
+
[findFirstFocusable, onKeyDownProp],
|
|
95
99
|
);
|
|
96
100
|
|
|
97
101
|
return (
|
|
98
102
|
<Comp
|
|
99
103
|
role='treegrid'
|
|
100
|
-
{...
|
|
101
|
-
className={tx('treegrid.root', {},
|
|
104
|
+
{...rest}
|
|
105
|
+
className={tx('treegrid.root', {}, className)}
|
|
102
106
|
style={{ ...style, gridTemplateColumns }}
|
|
103
107
|
onKeyDown={handleKeyDown}
|
|
104
108
|
ref={forwardedRef}
|
|
@@ -136,7 +140,7 @@ const TreegridRow = forwardRef<HTMLDivElement, TreegridRowScopedProps<TreegridRo
|
|
|
136
140
|
) => {
|
|
137
141
|
const { tx } = useThemeContext();
|
|
138
142
|
const Comp = asChild ? Slot : Primitive.div;
|
|
139
|
-
const pathParts = id.split(
|
|
143
|
+
const pathParts = id.split(TREEGRID_PATH_SEPARATOR);
|
|
140
144
|
const level = pathParts.length - 1;
|
|
141
145
|
const [open, onOpenChange] = useControllableState({
|
|
142
146
|
prop: propsOpen,
|
|
@@ -181,8 +185,4 @@ export const Treegrid = {
|
|
|
181
185
|
Root: TreegridRoot,
|
|
182
186
|
Row: TreegridRow,
|
|
183
187
|
Cell: TreegridCell,
|
|
184
|
-
PARENT_OF_SEPARATOR,
|
|
185
|
-
PATH_SEPARATOR,
|
|
186
|
-
createTreegridRowScope,
|
|
187
|
-
useTreegridRowContext,
|
|
188
188
|
};
|
|
@@ -30,7 +30,6 @@ import { type MainStyleProps, osTranslations } from '@dxos/ui-theme';
|
|
|
30
30
|
import { useThemeContext } from '../../hooks';
|
|
31
31
|
import { type ThemedClassName } from '../../util';
|
|
32
32
|
import { type Label, toLocalizedString, useTranslation } from '../ThemeProvider';
|
|
33
|
-
|
|
34
33
|
import { useSwipeToDismiss } from './useSwipeToDismiss';
|
|
35
34
|
|
|
36
35
|
const MAIN_NAME = 'Main';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
|
|
7
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
8
|
+
|
|
9
|
+
import { MediaPlayer } from './MediaPlayer';
|
|
10
|
+
|
|
11
|
+
const meta = {
|
|
12
|
+
title: 'ui/react-ui-core/components/MediaPlayer',
|
|
13
|
+
component: MediaPlayer,
|
|
14
|
+
decorators: [withTheme()],
|
|
15
|
+
parameters: { layout: 'centered' },
|
|
16
|
+
} satisfies Meta<typeof MediaPlayer>;
|
|
17
|
+
|
|
18
|
+
export default meta;
|
|
19
|
+
|
|
20
|
+
type Story = StoryObj<typeof meta>;
|
|
21
|
+
|
|
22
|
+
export const Video: Story = {
|
|
23
|
+
args: {
|
|
24
|
+
// TODO(burdon): CORS issue.
|
|
25
|
+
src: 'https://customer-5rxcjpyab08avpmn.cloudflarestream.com/f58459bcdf3a6f3e93644a4e0f39b22a/downloads/default.mp4',
|
|
26
|
+
classNames: 'max-w-[640px]',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const Audio: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
src: 'https://commondatastorage.googleapis.com/codeskulptor-demos/DDR_assets/Kangaroo_MusiQue_-_The_Neverwritten_Role_Playing_Game.mp3',
|
|
33
|
+
classNames: 'min-w-[480px]',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const ExplicitKind: Story = {
|
|
38
|
+
args: {
|
|
39
|
+
src: 'https://commondatastorage.googleapis.com/codeskulptor-demos/DDR_assets/Kangaroo_MusiQue_-_The_Neverwritten_Role_Playing_Game.mp3',
|
|
40
|
+
kind: 'audio',
|
|
41
|
+
classNames: 'min-w-[480px]',
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const Streaming: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
src: 'https://customer-5rxcjpyab08avpmn.cloudflarestream.com/f58459bcdf3a6f3e93644a4e0f39b22a/iframe?poster=https%3A%2F%2Fcustomer-5rxcjpyab08avpmn.cloudflarestream.com%2Ff58459bcdf3a6f3e93644a4e0f39b22a%2Fthumbnails%2Fthumbnail.jpg%3Ftime%3D%26height%3D600',
|
|
48
|
+
classNames: 'min-w-[480px]',
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { mx } from '@dxos/ui-theme';
|
|
8
|
+
|
|
9
|
+
import { type ThemedClassName } from '../../util';
|
|
10
|
+
|
|
11
|
+
export type MediaKind = 'video' | 'audio';
|
|
12
|
+
|
|
13
|
+
const VIDEO_EXTENSIONS = ['.mp4', '.webm', '.ogv', '.mov', '.m4v'];
|
|
14
|
+
const AUDIO_EXTENSIONS = ['.mp3', '.wav', '.ogg', '.m4a', '.aac', '.flac'];
|
|
15
|
+
|
|
16
|
+
/** iframe sandbox flags compatible with typical oEmbed-style players. */
|
|
17
|
+
const DEFAULT_IFRAME_SANDBOX = 'allow-scripts allow-same-origin allow-presentation';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Best-effort detection of `video` vs `audio` from a media URL.
|
|
21
|
+
* Inspects the pathname's extension (ignoring query/hash). Returns `undefined`
|
|
22
|
+
* when the URL doesn't look like a recognised media file — callers should
|
|
23
|
+
* default to 'video' or render a fallback (e.g. iframe / img).
|
|
24
|
+
*/
|
|
25
|
+
export const detectMediaKind = (src: string): MediaKind | undefined => {
|
|
26
|
+
// Strip query and hash, then take the last path segment's extension.
|
|
27
|
+
const pathname = src.split(/[?#]/, 1)[0]!;
|
|
28
|
+
const lower = pathname.toLowerCase();
|
|
29
|
+
if (VIDEO_EXTENSIONS.some((extension) => lower.endsWith(extension))) {
|
|
30
|
+
return 'video';
|
|
31
|
+
}
|
|
32
|
+
if (AUDIO_EXTENSIONS.some((extension) => lower.endsWith(extension))) {
|
|
33
|
+
return 'audio';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return undefined;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Heuristic match for URLs that should render as native `<video>` / `<audio>`
|
|
41
|
+
* (i.e. URLs ending in a recognised media extension).
|
|
42
|
+
*
|
|
43
|
+
* NB: legacy embed URLs (Cloudflare Stream etc. — paths containing `iframe`)
|
|
44
|
+
* serve an HTML player page, **not** a media stream, so they cannot be loaded
|
|
45
|
+
* via `<video>`. Those are detected by {@link isLegacyIframeUrl} and rendered
|
|
46
|
+
* via `<iframe>` instead.
|
|
47
|
+
*/
|
|
48
|
+
export const isEmbedUrl = (src: string): boolean => detectMediaKind(src) !== undefined;
|
|
49
|
+
|
|
50
|
+
/** Match URLs whose pathname has an `/iframe` segment (e.g. Cloudflare Stream embeds). */
|
|
51
|
+
const LEGACY_IFRAME_PATH_PATTERN = /\/iframe(?:[/?#]|$)/i;
|
|
52
|
+
|
|
53
|
+
const isLegacyIframeUrl = (src: string): boolean => {
|
|
54
|
+
const pathAndQuery = src.split('#', 1)[0]!;
|
|
55
|
+
return LEGACY_IFRAME_PATH_PATTERN.test(pathAndQuery);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type MediaPlayerProps = ThemedClassName<{
|
|
59
|
+
src: string;
|
|
60
|
+
/** Override auto-detection. When omitted, `detectMediaKind(src)` is used and falls back to 'video'. */
|
|
61
|
+
kind?: MediaKind;
|
|
62
|
+
controls?: boolean;
|
|
63
|
+
autoPlay?: boolean;
|
|
64
|
+
loop?: boolean;
|
|
65
|
+
muted?: boolean;
|
|
66
|
+
/** Accessible label for the `<video>` / `<audio>` element. */
|
|
67
|
+
alt?: string;
|
|
68
|
+
/** Defaults to 'anonymous' for cross-origin sources (e.g. signed S3 URLs). */
|
|
69
|
+
crossOrigin?: 'anonymous' | 'use-credentials' | '';
|
|
70
|
+
/** Additional classes applied only when rendering `<img>`. */
|
|
71
|
+
imgClassNames?: string;
|
|
72
|
+
/** Additional classes applied only when rendering native media or `<iframe>`. */
|
|
73
|
+
mediaClassNames?: string;
|
|
74
|
+
}>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Renders a media URL using the appropriate element:
|
|
78
|
+
* - Direct media URLs (mp4, mp3, …) → native `<video>` / `<audio>`.
|
|
79
|
+
* - Legacy `iframe`-style embed URLs (Cloudflare Stream, oEmbed players) → `<iframe>`.
|
|
80
|
+
* - Everything else → `<img>` that hides itself on load failure (broken images
|
|
81
|
+
* are common in feeds and the placeholder is uglier than nothing).
|
|
82
|
+
*/
|
|
83
|
+
export const MediaPlayer = ({
|
|
84
|
+
classNames,
|
|
85
|
+
src,
|
|
86
|
+
kind,
|
|
87
|
+
controls = true,
|
|
88
|
+
autoPlay = false,
|
|
89
|
+
loop = false,
|
|
90
|
+
muted = false,
|
|
91
|
+
alt,
|
|
92
|
+
crossOrigin = 'anonymous',
|
|
93
|
+
imgClassNames,
|
|
94
|
+
mediaClassNames,
|
|
95
|
+
}: MediaPlayerProps) => {
|
|
96
|
+
if (isEmbedUrl(src)) {
|
|
97
|
+
const resolved = kind ?? detectMediaKind(src) ?? 'video';
|
|
98
|
+
if (resolved === 'audio') {
|
|
99
|
+
return (
|
|
100
|
+
<audio
|
|
101
|
+
className={mx('w-full', classNames, mediaClassNames)}
|
|
102
|
+
src={src}
|
|
103
|
+
controls={controls}
|
|
104
|
+
autoPlay={autoPlay}
|
|
105
|
+
loop={loop}
|
|
106
|
+
muted={muted}
|
|
107
|
+
crossOrigin={crossOrigin}
|
|
108
|
+
aria-label={alt}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<video
|
|
115
|
+
className={mx('aspect-video max-w-full max-h-full', classNames, mediaClassNames)}
|
|
116
|
+
src={src}
|
|
117
|
+
controls={controls}
|
|
118
|
+
autoPlay={autoPlay}
|
|
119
|
+
loop={loop}
|
|
120
|
+
muted={muted}
|
|
121
|
+
crossOrigin={crossOrigin}
|
|
122
|
+
aria-label={alt}
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (isLegacyIframeUrl(src)) {
|
|
128
|
+
return (
|
|
129
|
+
<iframe
|
|
130
|
+
src={src}
|
|
131
|
+
title={alt ?? 'Embedded media'}
|
|
132
|
+
loading='lazy'
|
|
133
|
+
className={mx('border-none', classNames, mediaClassNames)}
|
|
134
|
+
sandbox={DEFAULT_IFRAME_SANDBOX}
|
|
135
|
+
referrerPolicy='no-referrer'
|
|
136
|
+
allow='accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;'
|
|
137
|
+
allowFullScreen
|
|
138
|
+
/>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<img
|
|
144
|
+
src={src}
|
|
145
|
+
alt={alt ?? ''}
|
|
146
|
+
loading='lazy'
|
|
147
|
+
className={mx(classNames, imgClassNames)}
|
|
148
|
+
onError={(event) => {
|
|
149
|
+
event.currentTarget.style.display = 'none';
|
|
150
|
+
}}
|
|
151
|
+
/>
|
|
152
|
+
);
|
|
153
|
+
};
|
|
@@ -19,6 +19,7 @@ import React, {
|
|
|
19
19
|
type ComponentPropsWithoutRef,
|
|
20
20
|
type ComponentRef,
|
|
21
21
|
type FC,
|
|
22
|
+
PropsWithChildren,
|
|
22
23
|
type ReactNode,
|
|
23
24
|
type RefObject,
|
|
24
25
|
forwardRef,
|
|
@@ -57,14 +58,13 @@ type DropdownMenuContextValue = {
|
|
|
57
58
|
const [DropdownMenuProvider, useDropdownMenuContext] =
|
|
58
59
|
createDropdownMenuContext<DropdownMenuContextValue>(DROPDOWN_MENU_NAME);
|
|
59
60
|
|
|
60
|
-
type DropdownMenuRootProps = {
|
|
61
|
-
children?: ReactNode;
|
|
61
|
+
type DropdownMenuRootProps = PropsWithChildren<{
|
|
62
62
|
dir?: Direction;
|
|
63
63
|
modal?: boolean;
|
|
64
64
|
open?: boolean;
|
|
65
65
|
defaultOpen?: boolean;
|
|
66
66
|
onOpenChange?(open: boolean): void;
|
|
67
|
-
}
|
|
67
|
+
}>;
|
|
68
68
|
|
|
69
69
|
const DropdownMenuRoot = ({
|
|
70
70
|
__scopeDropdownMenu,
|