Skip to content

Commit e13e5ad

Browse files
authored
feat: add SfBadge component (#8)
1 parent ea87566 commit e13e5ad

File tree

11 files changed

+232
-1
lines changed

11 files changed

+232
-1
lines changed

apps/docs/content/_components/badge.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ The component itself does not provide any specific accessibility features. Pleas
111111
<<<../../../../packages/sfui/frameworks/react/components/SfBadge/SfBadge.tsx
112112

113113
::
114-
::react-only
114+
::qwik-only
115115

116116
<<<../../../../dist/packages/qwik-storefront-ui/components/SfBadge/index.tsx
117117

5.6 KB
Loading
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { component$, useContext, useTask$ } from '@builder.io/qwik';
2+
import { SfBadge, SfBadgePlacement, SfBadgeVariant, SfButton, SfIconShoppingCart } from 'qwik-storefront-ui';
3+
import { ComponentExample } from '../../../components/utils/ComponentExample';
4+
import { ControlsType } from '../../../components/utils/types';
5+
import { EXAMPLES_STATE } from '../layout';
6+
7+
export default component$(() => {
8+
9+
const examplesState = useContext(EXAMPLES_STATE);
10+
11+
useTask$(() => {
12+
examplesState.data = {
13+
controls: [
14+
{
15+
type: 'text',
16+
modelName: 'content',
17+
description: 'Content to display in the badge.',
18+
propType: 'string | number',
19+
},
20+
{
21+
type: 'text',
22+
modelName: 'max',
23+
description: 'Maximum number of counter to show.',
24+
propType: 'number',
25+
propDefaultValue: '99',
26+
},
27+
{
28+
type: 'select',
29+
modelName: 'variant',
30+
description: 'Badge can have content or be a simple dot.',
31+
options: Object.values(SfBadgeVariant),
32+
propType: 'SfBadgeVariant',
33+
propDefaultValue: 'standard',
34+
},
35+
{
36+
type: 'select',
37+
modelName: 'placement',
38+
description: 'Position of the badge relatively to a container.',
39+
options: Object.values(SfBadgePlacement),
40+
propType: 'SfBadgePlacement',
41+
propDefaultValue: 'top-right',
42+
},
43+
] satisfies ControlsType,
44+
45+
state: {
46+
content: '1',
47+
max: 99,
48+
variant: SfBadgeVariant.standard,
49+
placement: SfBadgePlacement['top-right'],
50+
}
51+
}
52+
});
53+
54+
return (
55+
<ComponentExample>
56+
<SfButton class="relative" square variant="tertiary">
57+
<SfIconShoppingCart />
58+
<SfBadge {...examplesState.data.state} max={Number(examplesState.data.state.max)} />
59+
</SfButton>
60+
</ComponentExample>
61+
);
62+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { SfBadge } from 'qwik-storefront-ui';
2+
3+
export default function BadgeAvatar() {
4+
return (
5+
<ul>
6+
<li class="flex items-center mb-2">
7+
<div class="relative">
8+
<img src="/images/woman_avatar.png" alt="Avatar of a woman" width="36" height="36" />
9+
<SfBadge variant="dot" placement="bottom-right" class="!bg-primary-600 outline outline-white" />
10+
</div>
11+
</li>
12+
<li class="flex items-center">
13+
<div class="relative">
14+
<img src="/images/woman_avatar.png" alt="Avatar of a woman" width="36" height="36" />
15+
<SfBadge variant="dot" placement="bottom-right" class="!bg-neutral-600 outline outline-white" />
16+
</div>
17+
</li>
18+
</ul>
19+
);
20+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { component$ } from '@builder.io/qwik';
2+
import { SfBadge, SfButton, SfIconShoppingCart } from 'qwik-storefront-ui';
3+
4+
export default component$(() => {
5+
return (
6+
<div class="flex gap-3">
7+
<SfButton class="relative" square variant="tertiary">
8+
<SfIconShoppingCart />
9+
<SfBadge content={10} />
10+
</SfButton>
11+
12+
<SfButton class="relative" square variant="tertiary">
13+
<SfIconShoppingCart />
14+
<SfBadge content={10000} />
15+
</SfButton>
16+
17+
<SfButton class="relative" square variant="tertiary">
18+
<SfIconShoppingCart />
19+
<SfBadge content="New" />
20+
</SfButton>
21+
22+
<SfButton class="relative" square variant="tertiary">
23+
<SfIconShoppingCart />
24+
<SfBadge variant="dot" />
25+
</SfButton>
26+
</div>
27+
);
28+
})
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { SfBadge, SfButton, SfIconShoppingCart } from "qwik-storefront-ui";
2+
3+
export default function BadgeOutline() {
4+
return (
5+
<div class="flex gap-3">
6+
<div class="p-3 bg-white">
7+
<SfButton class="group relative" square variant="tertiary">
8+
<SfIconShoppingCart />
9+
<SfBadge
10+
content={100}
11+
max={99}
12+
class="outline outline-white group-hover:outline-primary-100 group-active:outline-primary-200"
13+
/>
14+
</SfButton>
15+
</div>
16+
17+
<div class="p-3 bg-primary-700">
18+
<SfButton class="group relative hover:bg-primary-800 active:bg-primary-900" square variant="tertiary">
19+
<SfIconShoppingCart class="text-white" />
20+
<SfBadge
21+
content={100}
22+
max={99}
23+
class="outline outline-primary-700 bg-white !text-neutral-900 group-hover:outline-primary-800 group-active:outline-primary-900"
24+
/>
25+
</SfButton>
26+
</div>
27+
</div>
28+
);
29+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { SfBadge, SfButton, SfIconShoppingCart } from "qwik-storefront-ui";
2+
3+
4+
export default function BadgePlacement() {
5+
return (
6+
<div class="flex gap-3">
7+
<SfButton class="relative" square variant="tertiary">
8+
<SfIconShoppingCart />
9+
<SfBadge content={100} max={99} placement="top-right" />
10+
</SfButton>
11+
12+
<SfButton class="relative" square variant="tertiary">
13+
<SfIconShoppingCart />
14+
<SfBadge content={100} max={99} placement="bottom-right" />
15+
</SfButton>
16+
17+
<SfButton class="relative" square variant="tertiary">
18+
<SfIconShoppingCart />
19+
<SfBadge content={100} max={99} placement="top-left" />
20+
</SfButton>
21+
22+
<SfButton class="relative" square variant="tertiary">
23+
<SfIconShoppingCart />
24+
<SfBadge content={100} max={99} placement="bottom-left" />
25+
</SfButton>
26+
</div>
27+
);
28+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { component$ } from '@builder.io/qwik';
2+
import { SfBadgePlacement, SfBadgeProps } from './types';
3+
4+
export const SfBadge = component$<SfBadgeProps>(
5+
({
6+
content,
7+
variant,
8+
max = 99,
9+
class: className,
10+
placement = SfBadgePlacement['top-right'],
11+
...attributes
12+
}: SfBadgeProps) => {
13+
const isDot = variant === 'dot';
14+
let displayValue = content;
15+
if (isDot) {
16+
displayValue = '';
17+
} else if (!Number.isNaN(content) && Number(content) > max) {
18+
displayValue = `${max}+`;
19+
}
20+
return (
21+
<span
22+
class={[
23+
'block absolute py-0.5 px-1 bg-secondary-700 font-medium text-white text-[8px] leading-[8px] rounded-xl',
24+
{
25+
'min-w-[12px] min-h-[12px]': !isDot,
26+
'w-[10px] h-[10px]': isDot,
27+
'top-0 right-0 -translate-x-0.5 translate-y-0.5': placement === 'top-right',
28+
'top-0 left-0 translate-x-0.5 translate-y-0.5': placement === 'top-left',
29+
'bottom-0 right-0 -translate-x-0.5 -translate-y-0.5': placement === 'bottom-right',
30+
'bottom-0 left-0 translate-x-0.5 -translate-y-0.5': placement === 'bottom-left',
31+
},
32+
className
33+
]}
34+
data-testid="badge"
35+
{...attributes}
36+
>
37+
{displayValue}
38+
</span>
39+
);
40+
})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './types';
2+
3+
export { SfBadge } from './SfBadge';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export type SfBadgeProps = {
2+
content?: string | number;
3+
max?: number;
4+
class?: string;
5+
placement?: `${SfBadgePlacement}`;
6+
variant?: `${SfBadgeVariant}`;
7+
}
8+
9+
export enum SfBadgeVariant {
10+
standard = 'standard',
11+
dot = 'dot',
12+
}
13+
14+
export enum SfBadgePlacement {
15+
'top-right' = 'top-right',
16+
'top-left' = 'top-left',
17+
'bottom-right' = 'bottom-right',
18+
'bottom-left' = 'bottom-left',
19+
}

packages/qwik-storefront-ui/src/components/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './SfAccordionItem';
2+
export * from './SfBadge';
23
export * from './SfButton';
34
export * from './SfCheckbox';
45
export * from './SfChip';
@@ -21,3 +22,4 @@ export * from './SfSelect';
2122
export * from './SfSwitch';
2223
export * from './SfThumbnail';
2324
export * from './SfTooltip';
25+

0 commit comments

Comments
 (0)