Skip to content

Commit e058ec8

Browse files
fix: added playground for modal (#35)
--------- Co-authored-by: gioboa <[email protected]>
1 parent fc1fb53 commit e058ec8

File tree

5 files changed

+229
-48
lines changed

5 files changed

+229
-48
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { component$, useContext, useSignal, useTask$ } from '@builder.io/qwik';
2+
import {
3+
SfButton,
4+
SfIconClose,
5+
SfIconLock,
6+
SfIconSearch,
7+
SfModal,
8+
} from 'qwik-storefront-ui';
9+
import { ComponentExample } from '../../../components/utils/ComponentExample';
10+
import { createControlsOptions } from '../../../components/utils/ControlsOptions';
11+
import { ControlsType } from '../../../components/utils/types';
12+
import { EXAMPLES_STATE } from '../layout';
13+
14+
const prefixSlotOptions = createControlsOptions({
15+
none: undefined,
16+
'Search icon': <SfIconSearch />,
17+
});
18+
const suffixSlotOptions = createControlsOptions({
19+
none: undefined,
20+
'Lock icon': <SfIconLock />,
21+
});
22+
23+
export default component$(() => {
24+
const selectPrefix = useSignal<boolean>();
25+
const selectSuffix = useSignal<boolean>();
26+
27+
const examplesState = useContext(EXAMPLES_STATE);
28+
29+
useTask$(() => {
30+
examplesState.data = {
31+
controls: [
32+
{
33+
type: 'text',
34+
modelName: 'slot',
35+
description:
36+
'Only for demonstration purposes. Default slot, replaces example modal content',
37+
},
38+
{
39+
type: 'boolean',
40+
modelName: 'open',
41+
propType: 'boolean',
42+
isRequired: true,
43+
description: 'If true modal is visible',
44+
},
45+
{
46+
type: 'boolean',
47+
modelName: 'disableClickAway',
48+
propType: 'boolean',
49+
isRequired: true,
50+
description:
51+
"If true can't close modal when clicking outside of modal",
52+
},
53+
{
54+
type: 'boolean',
55+
modelName: 'disableEsc',
56+
propType: 'boolean',
57+
isRequired: true,
58+
description:
59+
"If true can't close modal drawer when using ESC keyboard button",
60+
},
61+
] satisfies ControlsType,
62+
state: {
63+
slot: 'Hello',
64+
open: false,
65+
disableClickAway: false,
66+
disableEsc: false,
67+
},
68+
};
69+
});
70+
71+
useTask$(({ track }) => {
72+
track(() => examplesState.data.state);
73+
if (selectPrefix.value === null) return;
74+
selectPrefix.value = prefixSlotOptions.getValue(
75+
examplesState.data.state.slotPrefix
76+
);
77+
});
78+
79+
useTask$(({ track }) => {
80+
track(() => examplesState.data.state);
81+
if (selectSuffix.value === null) return;
82+
selectSuffix.value = suffixSlotOptions.getValue(
83+
examplesState.data.state.slotSuffix
84+
);
85+
});
86+
87+
return (
88+
<ComponentExample componentContainerClass="space-x-2">
89+
<SfButton
90+
type="button"
91+
preventdefault:click
92+
onClick$={() => {
93+
examplesState.data.state.open = true;
94+
console.log('value from button:', examplesState.data.state.open);
95+
}}
96+
>
97+
To Checkout
98+
</SfButton>
99+
100+
{examplesState.data.state.open && (
101+
<SfModal
102+
open={examplesState.data.state.open}
103+
disableClickAway={examplesState.data.state.disableClickAway}
104+
disableEsc={examplesState.data.state.disableEsc}
105+
onClose$={() => {
106+
examplesState.data.state.open = false;
107+
}}
108+
class="max-w-[90%] md:max-w-lg"
109+
as="section"
110+
role="alertdialog"
111+
aria-labelledby="promoModalTitle"
112+
aria-describedby="promoModalDesc"
113+
>
114+
<header>
115+
<SfButton
116+
square
117+
variant="tertiary"
118+
class="absolute right-2 top-2"
119+
onClick$={() => {
120+
examplesState.data.state.open = false;
121+
}}
122+
>
123+
<SfIconClose />
124+
</SfButton>
125+
<h3
126+
id="promoModalTitle"
127+
class="font-bold typography-headline-4 md:typography-headline-3"
128+
>
129+
You might miss out on great deals
130+
</h3>
131+
</header>
132+
<p id="promoModalDesc" class="mt-2">
133+
There are special offers for some of the items on your wishlist. Do
134+
you want to see these deals before proceeding to checkout page?
135+
</p>
136+
<footer class="flex justify-end gap-4 mt-4">
137+
<SfButton
138+
type="button"
139+
variant="secondary"
140+
onClick$={() => {
141+
examplesState.data.state.open = false;
142+
}}
143+
>
144+
Skip
145+
</SfButton>
146+
<SfButton
147+
type="button"
148+
onClick$={() => {
149+
examplesState.data.state.open = false;
150+
}}
151+
>
152+
Yes!
153+
</SfButton>
154+
</footer>
155+
</SfModal>
156+
)}
157+
</ComponentExample>
158+
);
159+
});

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"@commitlint/cli": "17.6.5",
1515
"@commitlint/config-angular": "17.6.5",
1616
"@commitlint/config-conventional": "17.6.5",
17+
"@frsource/autoresize-textarea": "^1.3.76",
1718
"@jscutlery/semver": "3.0.0",
1819
"@k11r/nx-cloudflare-wrangler": "2.4.2",
1920
"@mertasan/tailwindcss-variables": "2.6.1",
@@ -37,7 +38,6 @@
3738
"@vue/shared": "^3.3.4",
3839
"all-contributors-cli": "6.26.0",
3940
"autoprefixer": "10.4.14",
40-
"@frsource/autoresize-textarea": "^1.3.76",
4141
"cypress": "12.13.0",
4242
"cz-conventional-changelog": "3.3.0",
4343
"eslint": "8.42.0",
@@ -82,6 +82,8 @@
8282
],
8383
"dependencies": {
8484
"history": "5.3.0",
85-
"nuxt-gtag": "1.1.1"
85+
"micromark": "^4.0.0",
86+
"nuxt-gtag": "1.1.1",
87+
"postprocess": "^0.2.4"
8688
}
8789
}

packages/qwik-storefront-ui/src/components/SfModal/SfModal.tsx

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
import { Slot, component$ } from '@builder.io/qwik';
1+
import { Slot, component$, useSignal, useVisibleTask$ } from '@builder.io/qwik';
22
import { SfModalProps } from './types';
33

44
const defaultModalTag = 'div';
55

66
export const SfModal = component$<SfModalProps>(
77
({
88
as,
9-
ref,
10-
open,
119
disableClickAway,
1210
disableEsc,
13-
onClose,
11+
onClose$,
1412
class: _class,
1513
...attributes
1614
}) => {
1715
const Tag = as || defaultModalTag;
16+
const elementRef = useSignal<Element>();
1817

1918
// TODO
2019
// const modalRef = useRef(null);
@@ -23,38 +22,52 @@ export const SfModal = component$<SfModalProps>(
2322
// onClose?.();
2423
// });
2524

26-
const onKeyDown = (event: KeyboardEvent) => {
27-
if (!disableEsc && event.key === 'Escape') {
28-
onClose?.();
29-
}
30-
if (
31-
'onKeyDown' in attributes &&
32-
typeof attributes.onKeyDown === 'function'
33-
)
34-
return attributes.onKeyDown?.(event);
35-
};
36-
3725
// TODO
3826
// useTrapFocus(modalRef, {
3927
// activeState: open,
4028
// initialFocus: InitialFocusType.autofocus,
4129
// initialFocusContainerFallback: true,
4230
// });
4331

44-
return open ? (
32+
useVisibleTask$(({ cleanup }) => {
33+
const handleClick = (event: Event) => {
34+
if (
35+
!disableClickAway &&
36+
!elementRef.value?.contains(event.target as Node)
37+
) {
38+
onClose$ && onClose$();
39+
}
40+
};
41+
document.addEventListener('click', handleClick);
42+
43+
const handleKeyDown = (event: KeyboardEvent) => {
44+
if (!disableEsc && event.key === 'Escape') {
45+
onClose$ && onClose$();
46+
}
47+
};
48+
document.addEventListener('keydown', handleKeyDown);
49+
50+
cleanup(() => {
51+
document.removeEventListener('click', handleClick);
52+
document.removeEventListener('keydown', handleKeyDown);
53+
});
54+
});
55+
56+
return (
4557
<Tag
46-
{...(ref ? { ref } : {})}
58+
ref={elementRef ? elementRef : {}}
4759
class={`fixed inset-0 w-fit h-fit m-auto p-6 pt-10 lg:p-10 border border-neutral-100 bg-white shadow-xl rounded-xl outline-none
4860
${_class}`}
4961
tabIndex="-1"
5062
aria-modal="true"
5163
data-testid="modal"
5264
{...attributes}
53-
onKeyDown={onKeyDown}
65+
disableEsc={disableEsc}
66+
disableClickAway={disableClickAway}
5467
>
5568
<Slot />
5669
</Tag>
57-
) : null;
70+
);
5871
}
5972
);
6073

packages/qwik-storefront-ui/src/components/SfModal/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export type SfModalProps = QwikIntrinsicElements['modal'] & {
44
as: any;
55
class?: string;
66
ref?: Signal<Element>;
7-
open?: boolean;
7+
open: boolean;
88
disableClickAway?: boolean;
99
disableEsc?: boolean;
1010
onClose$?: PropFunction<() => void>;

0 commit comments

Comments
 (0)