Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Scrollable dialog #411

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions build/api/ui.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,12 @@ export interface BoxOwnProps {
// (undocumented)
distinction?: BoxDistinction;
// (undocumented)
footer?: ReactNode;
// (undocumented)
gap?: Size | 'none';
// (undocumented)
header?: ReactNode;
// @deprecated (undocumented)
heading?: ReactNode;
// (undocumented)
intent?: Intent;
Expand Down Expand Up @@ -619,20 +623,22 @@ export interface DialogProviderProps {
}

// @public (undocumented)
export interface DialogSettings<Result> {
// (undocumented)
bare?: boolean;
// (undocumented)
container?: HTMLElement;
// (undocumented)
export type DialogSettings<Result> = ({
type?: Default | 'immersive' | 'captivating';
children?: ReactNode | ((resolve: ResolveCallback<Result>) => ReactElement);
content?: never;
container?: never;
bare?: never;
heading?: never;
gap?: never;
} | {
content: (props: RenderDialogContentProps<Result>) => ReactElement;
// (undocumented)
gap?: BoxProps['gap'];
// (undocumented)
heading?: ReactNode;
// (undocumented)
container?: HTMLElement;
type?: Default | 'immersive' | 'captivating';
}
bare?: boolean;
heading?: ReactNode;
gap?: BoxProps['gap'];
});

// @public (undocumented)
export function DimensionSwitcher({ dimensions }: DimensionSwitcherProps): JSX.Element;
Expand Down Expand Up @@ -2153,7 +2159,7 @@ withTopToolbar?: boolean | undefined;
// @public (undocumented)
export type RangeInputProps = TextInputProps;

// @public (undocumented)
// @public @deprecated (undocumented)
export interface RenderDialogContentProps<Result> {
// (undocumented)
resolve: (value?: Result) => void;
Expand Down Expand Up @@ -2186,6 +2192,9 @@ export interface RepeaterItemContainerProps {
label?: ReactNode;
}

// @public (undocumented)
export type ResolveCallback<Result> = (value?: Result) => void;

// @public (undocumented)
export interface RestHTMLCheckboxProps extends Omit<AllHTMLAttributes<HTMLInputElement>, ControlPropsKeys<boolean> | 'checked' | 'children'> {
}
Expand Down
17 changes: 17 additions & 0 deletions build/api/utilities.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ export type DeepPartial<T> = T extends Function ? T : T extends Array<infer Infe
[Key in keyof T]?: DeepPartial<T[Key]>;
} : T | undefined;

// Warning: (ae-forgotten-export) The symbol "SemverString" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export function deprecate(removal: SemverString, deprecatedName: string, replacementName: string): void;

// @public (undocumented)
export function deprecate<R, D extends R>(removal: SemverString, deprecatedName: string, replacementName: string, deprecated: undefined, replacement: undefined): void;

// @public (undocumented)
export function deprecate<R, D extends R>(removal: SemverString, deprecatedName: string, replacementName: string, deprecated: D, replacement: undefined): D;

// @public (undocumented)
export function deprecate<R, D extends R>(removal: SemverString, deprecatedName: string, replacementName: string, deprecated: undefined, replacement: R): R;

// @public (undocumented)
export function deprecate<R, D extends R>(removal: SemverString, deprecatedName: string, replacementName: string, deprecated: D, replacement: R): R;

// @public
export type ExtendableProps<ExtendedProps = {}, OverrideProps = {}> = OverrideProps & Omit<ExtendedProps, keyof OverrideProps>;

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@
"@microsoft/api-extractor": "^7.34.8",
"@types/react": "^18",
"@types/react-dom": "^18",
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.60.1",
"@vitejs/plugin-react": "^3.1.0",
"esbuild": "^0.17.14",
"eslint": "^8.37.0",
"eslint": "^8.44.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.32.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const OutdatedApplicationChecker: ComponentType = () => {
isOpen.current = true

const result = await modal.openDialog({
content: ({ resolve }) => (
children: resolve => (
<OutdatedApplicationDialog onReload={() => resolve(true)} onPostpone={() => resolve(false)} />
),
})
Expand Down
17 changes: 12 additions & 5 deletions packages/admin/src/components/Dev/IdentityPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useProjectSlug, useSessionTokenWithMeta, useSetSessionToken } from '@contember/react-client'
import {
Box,
Button,
ButtonGroup,
DevPanel,
Expand All @@ -26,14 +27,20 @@ export const IdentityPanel = () => {
const dialog = useDialog()
const openSwitchRole = useCallback(() => {
dialog.openDialog({
heading: 'Login as...',
content: () => <LoginAsRole />,
children: resolve => (
<Box header="Login as..." gap="large">
<LoginAsRole />
</Box>
),
})
}, [dialog])
const openLoginEmail = useCallback(() => {
dialog.openDialog({
heading: 'Login by email',
content: () => <LoginWithEmail />,
children: resolve => (
<Box header="Login by email" gap="large">
<LoginWithEmail />
</Box>
),
})
}, [dialog])

Expand Down Expand Up @@ -89,7 +96,7 @@ export const IdentityPanel = () => {
)
}

const LoginAsRole: FC = ({}) => {
const LoginAsRole: FC = ({ }) => {
const addToast = useShowToast()
const [isSubmitting, setSubmitting] = useState(false)
const [memberships, setMemberships] = useState<(Membership | undefined)[]>([undefined])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
useAccessorTreeState,
useGetEntityListSubTree,
} from '@contember/binding'
import { Button, Stack, useDialog } from '@contember/ui'
import { Box, Button, DialogModal, Stack, useDialog } from '@contember/ui'
import { useMemo } from 'react'
import { useMessageFormatter } from '../../../../../i18n'
import { BaseDynamicChoiceField } from '../BaseDynamicChoiceField'
Expand Down Expand Up @@ -34,17 +34,24 @@ export const useOnAddNew = ({ createNewForm, connect, ...props }: BaseDynamicCho
const entity = subTree.getChildEntityById(newEntityId.value)

const result = await dialog.openDialog({
heading: localization('choiceField.createNew.dialogTitle'),
content: contentProps => (
<Stack direction="vertical">
<AccessorTree state={accessorTreeState}>
<Entity accessor={entity}>{createNewForm}</Entity>
</AccessorTree>
<Stack direction="horizontal" evenly>
<Button onClick={() => contentProps.resolve()} distinction="default" elevation="none">{localization('choiceField.createNew.cancelButtonText')}</Button>
<Button onClick={() => contentProps.resolve(true)} distinction="primary" elevation="none">{localization('choiceField.createNew.confirmButtonText')}</Button>
</Stack>
</Stack>
children: resolve => (
<DialogModal
header={localization('choiceField.createNew.dialogTitle')}
onClose={() => resolve()}
children={(
<Stack direction="vertical">
<AccessorTree state={accessorTreeState}>
<Entity accessor={entity}>{createNewForm}</Entity>
</AccessorTree>
</Stack>
)}
footer={(
<Stack grow direction="horizontal" evenly>
<Button onClick={() => resolve()} distinction="default" elevation="none">{localization('choiceField.createNew.cancelButtonText')}</Button>
<Button onClick={() => resolve(true)} distinction="primary" elevation="none">{localization('choiceField.createNew.confirmButtonText')}</Button>
</Stack>
)}
/>
),
})
if (result === true) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Entity, useEnvironment, VariableInputTransformer } from '@contember/binding'
import { EditorToolbar, ToolbarGroup, useDialog } from '@contember/ui'
import { Box, EditorToolbar, Label, ToolbarGroup, useDialog } from '@contember/ui'
import { memo, MouseEvent as ReactMouseEvent } from 'react'
import { Transforms } from 'slate'
import { useSlate } from 'slate-react'
Expand Down Expand Up @@ -68,36 +68,40 @@ export const HoveringToolbarContents = memo(({ buttons: rawButtons }: HoveringTo

const Content = button.referenceContent
const result = await openDialog({
heading: button.label,
content: props => (
<Entity accessor={reference}>
<Content
referenceId={reference.id}
editor={editor}
selection={selection}
onSuccess={({ createElement } = {}) => {
if (createElement !== undefined) {
if (!selection) {
return
}
EditorTransforms.select(editor, selection)
EditorTransforms.wrapNodes(
editor,
{
type: referenceElementType,
children: [{ text: '' }],
referenceId: reference.id,
...createElement,
},
{ split: true },
)
EditorTransforms.collapse(editor, { edge: 'end' })
}
props.resolve(true)
}}
onCancel={() => props.resolve()}
/>
</Entity>
children: resolve => (
<Box
header={<Label>{button.label}</Label>}
children={(
<Entity accessor={reference}>
<Content
referenceId={reference.id}
editor={editor}
selection={selection}
onSuccess={({ createElement } = {}) => {
if (createElement !== undefined) {
if (!selection) {
return
}
EditorTransforms.select(editor, selection)
EditorTransforms.wrapNodes(
editor,
{
type: referenceElementType,
children: [{ text: '' }],
referenceId: reference.id,
...createElement,
},
{ split: true },
)
EditorTransforms.collapse(editor, { edge: 'end' })
}
resolve(true)
}}
onCancel={() => resolve()}
/>
</Entity>
)}
/>
),
})
if (result !== true) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,13 @@ export const SelectFileInput = Component(
event.stopPropagation()

const selectedEntities = await openDialog({
type: 'captivating',
bare: true,
content: props => (
children: resolve => (
<DropdownContentContainerProvider>
<AccessorTree state={accessorTree}>
<SelectFileDialog
formatMessage={formatMessage}
onCancel={() => props.resolve()}
onSelect={props.resolve}
onCancel={() => resolve()}
onSelect={resolve}
insertSelectedText={insertSelectedText}
selectButtonText={selectButtonText}
isMultiple={isMultiple}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/admin/tests/playwright/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"composite": true,
"allowImportingTsExtensions": true,
"noEmit": true,
Expand Down
17 changes: 15 additions & 2 deletions packages/ui/src/components/Box/Box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import type { BoxDistinction, Default, HTMLDivElementProps, Intent, Size } from
import { toEnumViewClass, toStateClass, toThemeClass } from '../../utils'
import { Stack, StackProps } from '../Stack'
import { Label } from '../Typography/Label'
import { deprecate } from '@contember/utilities'

export interface BoxOwnProps {
actions?: ReactNode
children?: ReactNode
distinction?: BoxDistinction
direction?: StackProps['direction']
footer?: ReactNode
gap?: Size | 'none'
/** @deprecated use `header` instead and wrap your content in `Label` as `header` is not fully backward compatible */
heading?: ReactNode
header?: ReactNode
isActive?: boolean
intent?: Intent
padding?: Default | 'no-padding' | 'with-padding'
Expand Down Expand Up @@ -39,11 +43,15 @@ export const Box = memo(forwardRef<HTMLDivElement, BoxProps>(({
distinction,
gap = 'small',
heading,
header,
footer,
intent,
isActive,
padding,
...divProps
}: BoxProps, ref) => {
header = deprecate('1.3.0', '`Box.heading` prop', '`Box.header` prop', heading && <Label>{heading}</Label>, header)

const componentClassName = useClassNameFactory('box')

return (
Expand All @@ -59,9 +67,9 @@ export const Box = memo(forwardRef<HTMLDivElement, BoxProps>(({
ref={ref}
>
<Stack gap={gap} direction={direction}>
{(heading || actions) && (
{(header || actions) && (
<div className={componentClassName('header')}>
{heading && <Label>{heading}</Label>}
{header}
{actions && (
<div className={componentClassName('actions')} contentEditable={false}>
{actions}
Expand All @@ -70,6 +78,11 @@ export const Box = memo(forwardRef<HTMLDivElement, BoxProps>(({
</div>
)}
{children}
{footer && (
<div className={componentClassName('footer')}>
{footer}
</div>
)}
</Stack>
</div>
)
Expand Down
17 changes: 13 additions & 4 deletions packages/ui/src/components/Box/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,29 @@
--cui-box-content-padding: 0;
}

:where(.cui-box-header),
:where(.cui-box-body) {
:where(
.cui-box-header,
.cui-box-body,
.cui-box-footer,
) {
display: flex;
gap: var(--cui-gap);
}

:where(.cui-box-header) {
:where(
.cui-box-header,
.cui-box-footer,
) {
align-items: center;
flex-direction: row;
font-weight: 600;
justify-content: space-between;
}

:where(.cui-box:not(.is-active):not(:focus-within) > .cui-stack > .cui-box-header) {
:where(
.cui-box:not(.is-active):not(:focus-within) > .cui-stack > .cui-box-header,
.cui-box:not(.is-active):not(:focus-within) > .cui-stack > .cui-box-footer,
) {
color: var(--cui-color--high);
}

Expand Down
Loading