Skip to content

Commit

Permalink
feat: add alert dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
calvo-jp committed Oct 10, 2024
1 parent 0b2a205 commit 35a4d2e
Show file tree
Hide file tree
Showing 17 changed files with 470 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {anatomy as _} from '@zag-js/dialog';

export const anatomy = _.rename('alertdialog');
export const parts = anatomy.build();
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script lang="ts" module>
import type {HtmlIngredientProps} from '$lib/types.js';
import type {Action} from 'svelte/action';
export interface AlertDialogBackdropProps
extends HtmlIngredientProps<'div', HTMLDivElement, never, Action> {}
</script>

<script lang="ts">
import {mergeProps} from '$lib/merge-props.js';
import {createPresence} from '$lib/presence/create-presence.svelte.js';
import {getPresenceStrategyPropsContext} from '$lib/presence/presence-context.svelte.js';
import {reflect} from '@zag-js/svelte';
import {getAlertDialogContext} from './alert-dialog-context.svelte.js';
let {
ref = $bindable(null),
asChild,
children,
...props
}: AlertDialogBackdropProps = $props();
let alertDialog = getAlertDialogContext();
let presenceStrategyProps = getPresenceStrategyPropsContext();
let presence = createPresence(
reflect(() => ({
...presenceStrategyProps,
present: alertDialog.open,
})),
);
let mergedProps = $derived(
mergeProps(
alertDialog.getBackdropProps(),
presence.getPresenceProps(),
props,
),
);
</script>

{#if presence.mounted}
{#if asChild}
{@render asChild(presence.setReference, mergedProps)}
{:else}
<div bind:this={ref} use:presence.setReference {...mergedProps}>
{@render children?.()}
</div>
{/if}
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script lang="ts" module>
import type {HtmlIngredientProps} from '$lib/types.js';
export interface AlertDialogCloseTriggerProps
extends HtmlIngredientProps<'button', HTMLButtonElement> {}
</script>

<script lang="ts">
import {mergeProps} from '$lib/merge-props.js';
import {getAlertDialogContext} from './alert-dialog-context.svelte.js';
let {
ref = $bindable(null),
asChild,
children,
...props
}: AlertDialogCloseTriggerProps = $props();
let alertDialog = getAlertDialogContext();
let mergedProps = $derived(
mergeProps(alertDialog.getCloseTriggerProps(), props),
);
</script>

{#if asChild}
{@render asChild(mergedProps)}
{:else}
<button bind:this={ref} type="button" {...mergedProps}>
{@render children?.()}
</button>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script lang="ts" module>
import type {HtmlIngredientProps} from '$lib/types.js';
import type {Action} from 'svelte/action';
export interface AlertDialogContentProps
extends HtmlIngredientProps<'div', HTMLDivElement, never, Action> {}
</script>

<script lang="ts">
import {mergeProps} from '$lib/merge-props.js';
import {getPresenceContext} from '$lib/presence/presence-context.svelte.js';
import {getAlertDialogContext} from './alert-dialog-context.svelte.js';
let {
ref = $bindable(null),
asChild,
children,
...props
}: AlertDialogContentProps = $props();
let alertDialog = getAlertDialogContext();
let presence = getPresenceContext();
let mergedProps = $derived(
mergeProps(
alertDialog.getContentProps(),
presence.getPresenceProps(),
props,
),
);
</script>

{#if presence.mounted}
{#if asChild}
{@render asChild(presence.setReference, mergedProps)}
{:else}
<div bind:this={ref} use:presence.setReference {...mergedProps}>
{@render children?.()}
</div>
{/if}
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {createContext} from '$lib/create-context.svelte.js';
import type {CreateAlertDialogReturn} from './create-alert-dialog.svelte.js';

export const [getAlertDialogContext, setAlertDialogContext] =
createContext<CreateAlertDialogReturn>('AlertDialog');
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script lang="ts" module>
import type {HtmlIngredientProps} from '$lib/types.js';
export interface AlertDialogDescriptionProps
extends HtmlIngredientProps<'p', HTMLParagraphElement> {}
</script>

<script lang="ts">
import {mergeProps} from '$lib/merge-props.js';
import {getAlertDialogContext} from './alert-dialog-context.svelte.js';
let {
ref = $bindable(null),
asChild,
children,
...props
}: AlertDialogDescriptionProps = $props();
let alertDialog = getAlertDialogContext();
let mergedProps = $derived(
mergeProps(alertDialog.getDescriptionProps(), props),
);
</script>

{#if asChild}
{@render asChild(mergedProps)}
{:else}
<p bind:this={ref} {...mergedProps}>
{@render children?.()}
</p>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script lang="ts" module>
import type {HtmlIngredientProps} from '$lib/types.js';
export interface AlertDialogPositionerProps
extends HtmlIngredientProps<'div', HTMLDivElement> {}
</script>

<script lang="ts">
import {mergeProps} from '$lib/merge-props.js';
import {getPresenceContext} from '$lib/presence/presence-context.svelte.js';
import {getAlertDialogContext} from './alert-dialog-context.svelte.js';
let {
ref = $bindable(null),
asChild,
children,
...props
}: AlertDialogPositionerProps = $props();
let alertDialog = getAlertDialogContext();
let presence = getPresenceContext();
let mergedProps = $derived(
mergeProps(
alertDialog.getPositionerProps(),
presence.getPresenceProps(),
props,
),
);
</script>

{#if presence.mounted}
{#if asChild}
{@render asChild(mergedProps)}
{:else}
<div bind:this={ref} {...mergedProps}>
{@render children?.()}
</div>
{/if}
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script lang="ts" module>
import type {PresenceStrategyProps} from '$lib/presence/create-presence.svelte.js';
import type {Snippet} from 'svelte';
import type {
CreateAlertDialogProps,
CreateAlertDialogReturn,
} from './create-alert-dialog.svelte.js';
export interface AlertDialogProps
extends CreateAlertDialogProps,
PresenceStrategyProps {
children?: Snippet<[CreateAlertDialogReturn]>;
}
</script>

<script lang="ts">
import {createPresence} from '$lib/presence/create-presence.svelte.js';
import {
setPresenceContext,
setPresenceStrategyPropsContext,
} from '$lib/presence/presence-context.svelte.js';
import {reflect} from '@zag-js/svelte';
import {createSplitProps} from '@zag-js/utils';
import {setAlertDialogContext} from './alert-dialog-context.svelte.js';
import {createAlertDialog} from './create-alert-dialog.svelte.js';
let {children, ...props}: AlertDialogProps = $props();
let [presenceStrategyProps, createDialogProps] = $derived(
createSplitProps<PresenceStrategyProps>(['lazyMount', 'keepMounted'])(
props,
),
);
let alertDialog = createAlertDialog(reflect(() => createDialogProps));
let presence = createPresence(
reflect(() => ({
...presenceStrategyProps,
present: alertDialog.open,
})),
);
setAlertDialogContext(alertDialog);
setPresenceContext(presence);
setPresenceStrategyPropsContext(() => presenceStrategyProps);
</script>

{@render children?.(alertDialog)}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script lang="ts" module>
import type {HtmlIngredientProps} from '$lib/types.js';
export interface AlertDialogTitleProps
extends HtmlIngredientProps<'h2', HTMLHeadingElement> {}
</script>

<script lang="ts">
import {mergeProps} from '$lib/merge-props.js';
import {getAlertDialogContext} from './alert-dialog-context.svelte.js';
let {
ref = $bindable(null),
asChild,
children,
...props
}: AlertDialogTitleProps = $props();
let alertDialog = getAlertDialogContext();
let mergedProps = $derived(mergeProps(alertDialog.getTitleProps(), props));
</script>

{#if asChild}
{@render asChild(mergedProps)}
{:else}
<h2 bind:this={ref} {...mergedProps}>
{@render children?.()}
</h2>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script lang="ts" module>
import type {HtmlIngredientProps} from '$lib/types.js';
export interface AlertDialogTriggerProps
extends HtmlIngredientProps<'button', HTMLButtonElement> {}
</script>

<script lang="ts">
import {mergeProps} from '$lib/merge-props.js';
import {getAlertDialogContext} from './alert-dialog-context.svelte.js';
let {
ref = $bindable(null),
asChild,
children,
...props
}: AlertDialogTriggerProps = $props();
let alertDialog = getAlertDialogContext();
let mergedProps = $derived(mergeProps(alertDialog.getTriggerProps(), props));
</script>

{#if asChild}
{@render asChild(mergedProps)}
{:else}
<button bind:this={ref} type="button" {...mergedProps}>
{@render children?.()}
</button>
{/if}
8 changes: 8 additions & 0 deletions packages/ui-ingredients/src/lib/alert-dialog/alert-dialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export {default as Backdrop} from './alert-dialog-backdrop.svelte';
export {default as CloseTrigger} from './alert-dialog-close-trigger.svelte';
export {default as Content} from './alert-dialog-content.svelte';
export {default as Description} from './alert-dialog-description.svelte';
export {default as Positioner} from './alert-dialog-positioner.svelte';
export {default as Root} from './alert-dialog-root.svelte';
export {default as Title} from './alert-dialog-title.svelte';
export {default as Trigger} from './alert-dialog-trigger.svelte';
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {getEnvironmentContext} from '$lib/environment-provider/enviroment-provider-context.svelte.js';
import {getLocaleContext} from '$lib/locale-provider/local-provider-context.svelte.js';
import {mergeProps} from '$lib/merge-props.js';
import * as dialog from '@zag-js/dialog';
import {normalizeProps, reflect, useMachine} from '@zag-js/svelte';
import {uid} from 'uid';
import {parts} from './alert-dialog-anatomy.js';

export interface CreateAlertDialogProps
extends Omit<
dialog.Context,
'id' | 'dir' | 'role' | 'getRootNode' | 'open.controlled'
> {
id?: string;
openControlled?: boolean;
}

export interface CreateAlertDialogReturn extends dialog.Api {}

export function createAlertDialog(
props: CreateAlertDialogProps,
): CreateAlertDialogReturn {
const locale = getLocaleContext();
const environment = getEnvironmentContext();

const id = uid();

const context: dialog.Context = reflect(() => ({
id,
dir: locale?.dir,
role: 'alertdialog',
...props,
getRootNode: environment?.getRootNode,
'open.controlled': props.openControlled,
}));

const [state, send] = useMachine(dialog.machine(context), {context});

return reflect(() => {
const o = dialog.connect(state, send, normalizeProps);

return {
...o,
getBackdropProps() {
return mergeProps(o.getBackdropProps(), parts.backdrop.attrs);
},
getCloseTriggerProps() {
return mergeProps(o.getCloseTriggerProps(), parts.closeTrigger.attrs);
},
getContentProps() {
return mergeProps(o.getContentProps(), parts.content.attrs);
},
getDescriptionProps() {
return mergeProps(o.getDescriptionProps(), parts.description.attrs);
},
getPositionerProps() {
return mergeProps(o.getPositionerProps(), parts.positioner.attrs);
},
getTitleProps() {
return mergeProps(o.getTitleProps(), parts.title.attrs);
},
getTriggerProps() {
return mergeProps(o.getTriggerProps(), parts.trigger.attrs);
},
};
});
}
Loading

0 comments on commit 35a4d2e

Please sign in to comment.