Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
93 changes: 93 additions & 0 deletions apps/docs/components/components/badge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
layout: AtomLayout
hideBreadcrumbs: true
---

# Badge

::: slot usage

## Examples

### Basic usage

The badge component must be wrapped with a container that has `class="relative"`. You must provide such a container by yourself, but it gives you the flexibility to put the badge wherever you want. Bagde comes with a "dot" variant, which hides the content. When given content is of type number (or string that could be parsed to number), you can set a maximum limit of that number using `max` prop.

<Showcase showcase-name="Badge/BadgeBasic">
<!-- vue -->
<<<../../preview/nuxt/pages/showcases/Badge/BadgeBasic.vue
<!-- end vue -->
<!-- react -->
<<<../../preview/next/pages/showcases/Badge/BadgeBasic.tsx#source
<!-- end react -->
</Showcase>

### Placement

You can align the Badge in every corner of the container.

<Showcase showcase-name="Badge/BadgePlacement">
<!-- vue -->
<<<../../preview/nuxt/pages/showcases/Badge/BadgePlacement.vue
<!-- end vue -->
<!-- react -->
<<<../../preview/next/pages/showcases/Badge/BadgePlacement.tsx#source
<!-- end react -->
</Showcase>

### Custom outline

A nifty effect that makes the Badge a bit more attractive is to add an outline that separates the Badge from an element.

<Showcase showcase-name="Badge/BadgeOutline">
<!-- vue -->
<<<../../preview/nuxt/pages/showcases/Badge/BadgeOutline.vue
<!-- end vue -->
<!-- react -->
<<<../../preview/next/pages/showcases/Badge/BadgeOutline.tsx#source
<!-- end react -->
</Showcase>

### Avatars

A common use case for the Badge is to place it on a user's avatar.

<Showcase showcase-name="Badge/BadgeAvatar">
<!-- vue -->
<<<../../preview/nuxt/pages/showcases/Badge/BadgeAvatar.vue
<!-- end vue -->
<!-- react -->
<<<../../preview/next/pages/showcases/Badge/BadgeAvatar.tsx#source
<!-- end react -->
</Showcase>

## Playground

<Generate style="height: 380px" />

:::

::: slot api

## Props

| Prop name | Type | Default value | Possible values |
| ----------- | ------------------ | ------------- | ------------------------------------------------------ |
| `content` | `string | number` | |
Comment thread
aniamusial marked this conversation as resolved.
Outdated
| `max` | `number` | `99` | |
| `placement` | `SfBadgePlacement` | `top-right` | `top-right`, `top-left`, `bottom-right`, `bottom-left` |
| `variant` | `SfBadgeVariant` | `standard` | `standard`, `dot` |

:::

::: slot source
<SourceCode>

<!-- vue -->
<<<../../../packages/sfui/frameworks/vue/components/SfBadge/SfBadge.vue
<!-- end vue -->
<!-- react -->
<<<../../../packages/sfui/frameworks/react/components/SfBadge/SfBadge.tsx
<!-- end react -->
</SourceCode>
:::
58 changes: 58 additions & 0 deletions apps/preview/next/pages/examples/SfBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { SfBadge, SfButton, SfIconShoppingCart, SfBadgePlacement, SfBadgeVariant } from '@storefront-ui/react';
import { prepareControls } from '../../components/utils/Controls';
import ComponentExample from '../../components/utils/ComponentExample';
import { ExamplePageLayout } from '../examples';

function Example() {
const { state, controls } = prepareControls(
[
{
type: 'text',
modelName: 'content',
description: 'Content to display in the badge.',
propType: 'string | number',
},
{
type: 'text',
modelName: 'max',
description: 'Maximum number of counter to show.',
propType: 'number',
propDefaultValue: '99',
},
{
type: 'select',
modelName: 'variant',
description: 'Badge can have content or be a simple dot.',
options: Object.values(SfBadgeVariant),
propType: 'SfBadgeVariant',
propDefaultValue: 'standard',
},
{
type: 'select',
modelName: 'placement',
description: 'Position of the badge relatively to a container.',
options: Object.values(SfBadgePlacement),
propType: 'SfBadgePlacement',
propDefaultValue: 'top-right',
},
],
{
content: '1',
max: '99',
Comment thread
aniamusial marked this conversation as resolved.
Outdated
variant: SfBadgeVariant.standard,
placement: SfBadgePlacement['top-right'],
},
);

return (
<ComponentExample controls={{ state, controls }}>
<SfButton className="relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge {...state.get} max={Number(state.get.max)} />
</SfButton>
</ComponentExample>
);
}

Example.getLayout = ExamplePageLayout;
export default Example;
27 changes: 27 additions & 0 deletions apps/preview/next/pages/showcases/Badge/BadgeAvatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ShowcasePageLayout } from '../../showcases';
// #region source
import { SfBadge, SfIconCircle } from '@storefront-ui/react';

export default function BadgeAvatar() {
return (
<ul>
<li className="flex items-center mb-2">
<div className="relative">
<SfIconCircle className="text-neutral-300" size="lg" />
<SfBadge variant="dot" placement="bottom-right" className="!bg-primary-600 outline outline-white" />
</div>
<p className="ml-2 font-bold">John Doe</p>
</li>
<li className="flex items-center">
<div className="relative">
<SfIconCircle className="text-neutral-300" size="lg" />
<SfBadge variant="dot" placement="bottom-right" className="!bg-neutral-600 outline outline-white" />
</div>
<p className="ml-2">Jane Doe</p>
</li>
</ul>
);
}

// #endregion source
BadgeAvatar.getLayout = ShowcasePageLayout;
32 changes: 32 additions & 0 deletions apps/preview/next/pages/showcases/Badge/BadgeBasic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ShowcasePageLayout } from '../../showcases';
// #region source
import { SfBadge, SfButton, SfIconShoppingCart } from '@storefront-ui/react';

export default function BadgeBasic() {
return (
<div className="flex gap-3">
<SfButton className="relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge content={10} />
</SfButton>

<SfButton className="relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge content={10000} />
</SfButton>

<SfButton className="relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge variant="dot" />
</SfButton>

<SfButton className="relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge content="NEW" />
</SfButton>
</div>
);
}

// #endregion source
BadgeBasic.getLayout = ShowcasePageLayout;
32 changes: 32 additions & 0 deletions apps/preview/next/pages/showcases/Badge/BadgeOutline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ShowcasePageLayout } from '../../showcases';
// #region source
import { SfBadge, SfButton, SfIconShoppingCart } from '@storefront-ui/react';

export default function BadgeOutline() {
return (
<div className="flex gap-3">
<div className="p-3 bg-white">
<SfButton className="group relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge
content={10}
className="outline outline-white group-hover:outline-primary-100 group-active:outline-primary-200"
/>
</SfButton>
</div>

<div className="p-3 bg-primary-700">
<SfButton className="group relative hover:bg-primary-800 active:bg-primary-900" square variant="tertiary">
<SfIconShoppingCart className="text-white" />
<SfBadge
content={10}
className="outline outline-primary-700 group-hover:outline-primary-800 group-active:outline-primary-900"
/>
</SfButton>
</div>
</div>
);
}

// #endregion source
BadgeOutline.getLayout = ShowcasePageLayout;
32 changes: 32 additions & 0 deletions apps/preview/next/pages/showcases/Badge/BadgePlacement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ShowcasePageLayout } from '../../showcases';
// #region source
import { SfBadge, SfButton, SfIconShoppingCart } from '@storefront-ui/react';

export default function BadgePlacement() {
return (
<div className="flex gap-3">
<SfButton className="relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge content={7} placement="top-right" />
</SfButton>

<SfButton className="relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge content={7} placement="bottom-right" />
</SfButton>

<SfButton className="relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge content={7} placement="top-left" />
</SfButton>

<SfButton className="relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge content={7} placement="bottom-left" />
</SfButton>
</div>
);
}

// #endregion source
BadgePlacement.getLayout = ShowcasePageLayout;
68 changes: 68 additions & 0 deletions apps/preview/nuxt/pages/examples/SfBadge.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<template>
<ComponentExample :controls-attrs="controlsAttrs">
<SfButton class="relative" square variant="tertiary">
<SfIconShoppingCart />
<SfBadge v-bind="state" :max="Number(state.max)" />
</SfButton>
</ComponentExample>
</template>

<script lang="ts">
import { ref } from 'vue';
import { SfBadge, SfBadgePlacement, SfBadgeVariant, SfButton, SfIconShoppingCart } from '@storefront-ui/vue';
import { prepareControls } from '../../components/utils/Controls.vue';
import ComponentExample from '../../components/utils/ComponentExample.vue';

export default {
name: 'SfBadgeExample',
components: {
ComponentExample,
SfBadge,
SfButton,
SfIconShoppingCart,
},
setup() {
return {
...prepareControls(
[
{
type: 'text',
modelName: 'content',
description: 'Content to display in the badge.',
propType: 'string | number',
},
{
type: 'text',
modelName: 'max',
description: 'Maximum number of counter to show.',
propType: 'number',
propDefaultValue: '99',
},
{
type: 'select',
modelName: 'variant',
description: 'Badge can have content or be a simple dot.',
options: Object.values(SfBadgeVariant),
propType: 'SfBadgeVariant',
propDefaultValue: 'standard',
},
{
type: 'select',
modelName: 'placement',
description: 'Position of the badge relatively to a container.',
options: Object.values(SfBadgePlacement),
propType: 'SfBadgePlacement',
propDefaultValue: 'top-right',
},
],
{
content: ref('1'),
max: ref('99'),
Comment thread
aniamusial marked this conversation as resolved.
Outdated
variant: ref(SfBadgeVariant.standard),
placement: ref(SfBadgePlacement['top-right']),
},
),
};
},
};
</script>
22 changes: 22 additions & 0 deletions apps/preview/nuxt/pages/showcases/Badge/BadgeAvatar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<ul>
<li class="flex items-center mb-2">
<div class="relative">
<SfIconCircle class="text-neutral-300" size="lg" />
<SfBadge variant="dot" placement="bottom-right" class="!bg-primary-600 outline outline-white" />
</div>
<p class="ml-2 font-bold">John Doe</p>
</li>
<li class="flex items-center">
<div class="relative">
<SfIconCircle class="text-neutral-300" size="lg" />
<SfBadge variant="dot" placement="bottom-right" class="!bg-neutral-600 outline outline-white" />
</div>
<p class="ml-2">Jane Doe</p>
</li>
</ul>
</template>

<script lang="ts" setup>
import { SfBadge, SfIconCircle } from '@storefront-ui/vue';
</script>
Loading