mirror of
https://github.com/tabler/tabler.git
synced 2026-10-12 05:06:55 +04:00
Redesign the Buttons demo page into Styles and Colors sections (#2862)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f4c514ae62
commit
da11f089a6
8 files changed
+354
-185
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@tabler/preview": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Added a short description and a docs link to each card on the Buttons demo page, grouped into Styles and Colors.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@tabler/preview": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Replaced lorem ipsum with unique placeholder text on the Scroll spy demo page and fixed its menu highlight.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@tabler/preview": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Reorganized the Star Ratings demo page into Basic/Icons/Sizes and Colors cards with short descriptions.
|
||||||
+207
-133
@@ -4,9 +4,10 @@ import Icon from '@ui/Icon.astro'
|
|||||||
import Button from '@ui/Button.astro'
|
import Button from '@ui/Button.astro'
|
||||||
import ButtonList from '@ui/ButtonList.astro'
|
import ButtonList from '@ui/ButtonList.astro'
|
||||||
import Card from '@ui/Card.astro'
|
import Card from '@ui/Card.astro'
|
||||||
import CardHeader from '@ui/CardHeader.astro'
|
|
||||||
import CardBody from '@ui/CardBody.astro'
|
import CardBody from '@ui/CardBody.astro'
|
||||||
|
import CardTitle from '@ui/CardTitle.astro'
|
||||||
import site from '@data/site.json'
|
import site from '@data/site.json'
|
||||||
|
import { getDocsUrl } from '@shared/lib/string-format'
|
||||||
|
|
||||||
type ColorEntry = [string, { title: string; icon?: string }]
|
type ColorEntry = [string, { title: string; icon?: string }]
|
||||||
|
|
||||||
@@ -18,136 +19,61 @@ const socialColors = Object.entries(site.socialColors) as ColorEntry[]
|
|||||||
|
|
||||||
const actions = ['edit', 'copy', 'settings', 'clipboard', 'x']
|
const actions = ['edit', 'copy', 'settings', 'clipboard', 'x']
|
||||||
const sizes = ['sm', 'md', 'lg', 'xl'] as const
|
const sizes = ['sm', 'md', 'lg', 'xl'] as const
|
||||||
|
|
||||||
|
const buttonDocsUrl = getDocsUrl('/ui/components/button')
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="Buttons" pageHeader="Buttons" pageMenu="base.buttons">
|
<DefaultLayout title="Buttons" pageMenu="base.buttons" description="Buttons are used to trigger actions in a user interface.">
|
||||||
<div class="row row-cards">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<div class="col-md-6">
|
<h1 class="page-title">Buttons</h1>
|
||||||
|
<a href={buttonDocsUrl} target="_blank" rel="noopener" class="link-secondary">
|
||||||
|
Docs <Icon name="external-link" class="icon-sm" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row row-cards mb-5">
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader title="Standard Buttons" />
|
|
||||||
<CardBody>
|
<CardBody>
|
||||||
|
<CardTitle>Basic</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Add an icon to the left, the right, or drop the label for an icon-only button.</div>
|
||||||
<ButtonList>
|
<ButtonList>
|
||||||
{
|
<Button text="Button" />
|
||||||
themeColors.map(([name, color]) => (
|
<Button icon="star" text="With icon" />
|
||||||
<a class={`btn btn-${name}`}>
|
<Button iconEnd="star" text="Icon at end" />
|
||||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
<Button icon="star" iconOnly text="Icon only" />
|
||||||
</a>
|
<Button text="Disabled" disabled />
|
||||||
))
|
|
||||||
}
|
|
||||||
</ButtonList>
|
</ButtonList>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6 col-lg-4">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader title="Outline Buttons" />
|
|
||||||
<CardBody>
|
<CardBody>
|
||||||
|
<CardTitle>Shapes</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Round the corners with <code>.btn-pill</code>, or square them off with <code>.btn-square</code>.</div>
|
||||||
<ButtonList>
|
<ButtonList>
|
||||||
{
|
<a class="btn">Default</a>
|
||||||
themeColors.map(([name, color]) => (
|
<a class="btn btn-pill">Pill</a>
|
||||||
<a class={`btn btn-outline btn-${name}`}>
|
<a class="btn btn-square">Square</a>
|
||||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
<a class="btn btn-icon">
|
||||||
</a>
|
<Icon name="star" />
|
||||||
))
|
</a>
|
||||||
}
|
<a class="btn btn-pill btn-icon">
|
||||||
|
<Icon name="star" />
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-square btn-icon">
|
||||||
|
<Icon name="star" />
|
||||||
|
</a>
|
||||||
</ButtonList>
|
</ButtonList>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6 col-lg-4">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader title="Ghost Buttons" />
|
|
||||||
<CardBody>
|
|
||||||
<ButtonList>
|
|
||||||
{
|
|
||||||
themeColors.map(([name, color]) => (
|
|
||||||
<a class={`btn btn-ghost btn-${name}`}>
|
|
||||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
|
||||||
</a>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</ButtonList>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Square Buttons" />
|
|
||||||
<CardBody>
|
|
||||||
<ButtonList>
|
|
||||||
{
|
|
||||||
themeColors.map(([name, color]) => (
|
|
||||||
<a class={`btn btn-square btn-${name}`}>
|
|
||||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
|
||||||
</a>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</ButtonList>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Pill Buttons" />
|
|
||||||
<CardBody>
|
|
||||||
<ButtonList>
|
|
||||||
{
|
|
||||||
themeColors.map(([name, color]) => (
|
|
||||||
<a class={`btn btn-pill btn-${name}`}>
|
|
||||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
|
||||||
</a>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</ButtonList>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Extra colors" />
|
|
||||||
<CardBody>
|
|
||||||
<ButtonList>
|
|
||||||
{
|
|
||||||
colors.map(([name, color]) => (
|
|
||||||
<a class={`btn btn-${name}`}>
|
|
||||||
{color.icon && <Icon name={color.icon} />} {color.title}
|
|
||||||
</a>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</ButtonList>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Icon buttons" />
|
|
||||||
<CardBody>
|
|
||||||
<ButtonList>
|
|
||||||
{socialColors.map(([name, app]) => <a class={`btn btn-icon btn-${name}`}>{app.icon && <Icon name={app.icon} />}</a>)}
|
|
||||||
</ButtonList>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Social colors" />
|
|
||||||
<CardBody>
|
|
||||||
<ButtonList>
|
|
||||||
{
|
|
||||||
socialColors.map(([name, app]) => (
|
|
||||||
<a class={`btn btn-${name}`}>
|
|
||||||
{app.icon && <Icon name={app.icon} />} {app.title}
|
|
||||||
</a>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</ButtonList>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Action buttons" />
|
|
||||||
<CardBody>
|
<CardBody>
|
||||||
|
<CardTitle>Action buttons</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Compact, icon-only buttons for card headers and toolbars.</div>
|
||||||
<div class="btn-actions">
|
<div class="btn-actions">
|
||||||
{
|
{
|
||||||
actions.map((action) => (
|
actions.map((action) => (
|
||||||
@@ -160,28 +86,11 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
|
|||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-12 col-lg-8">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader title="Buttons with icon" />
|
|
||||||
<CardBody>
|
|
||||||
<ButtonList>
|
|
||||||
<a class="btn btn-animate-icon">Save <Icon name="arrow-right" class="icon-end" /></a>
|
|
||||||
<a class="btn btn-animate-icon btn-animate-icon-rotate"><Icon name="plus" /> Add</a>
|
|
||||||
<a class="btn btn-animate-icon btn-animate-icon-shake"><Icon name="bell" /> Notifications</a>
|
|
||||||
<a class="btn btn-animate-icon btn-animate-icon-rotate"><Icon name="settings" /> Settings</a>
|
|
||||||
<a class="btn btn-animate-icon btn-animate-icon-pulse"><Icon name="heart" /> Love</a>
|
|
||||||
<a class="btn btn-animate-icon btn-animate-icon-rotate"><Icon name="x" /> Close</a>
|
|
||||||
<a class="btn btn-animate-icon btn-animate-icon-tada"><Icon name="check" /> Confirm</a>
|
|
||||||
<a class="btn btn-animate-icon">Next <Icon name="chevron-right" class="icon-end" /></a>
|
|
||||||
<a class="btn btn-animate-icon btn-animate-icon-move-start"><Icon name="chevron-left" /> Previous</a>
|
|
||||||
</ButtonList>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Buttons size" />
|
|
||||||
<CardBody>
|
<CardBody>
|
||||||
|
<CardTitle>Sizes</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Scale a button from <code>sm</code> to <code>xl</code> without changing its icon layout.</div>
|
||||||
<div class="space-y">
|
<div class="space-y">
|
||||||
{
|
{
|
||||||
sizes.map((size) => (
|
sizes.map((size) => (
|
||||||
@@ -197,5 +106,170 @@ const sizes = ['sm', 'md', 'lg', 'xl'] as const
|
|||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-12 col-lg-4">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Animated icon</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Small hover animations draw attention to the icon.</div>
|
||||||
|
<ButtonList>
|
||||||
|
<a class="btn btn-animate-icon">
|
||||||
|
Save <Icon name="arrow-right" class="icon-end" />
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-animate-icon btn-animate-icon-rotate">
|
||||||
|
<Icon name="plus" /> Add
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-animate-icon btn-animate-icon-shake">
|
||||||
|
<Icon name="bell" /> Notifications
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-animate-icon btn-animate-icon-rotate">
|
||||||
|
<Icon name="settings" /> Settings
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-animate-icon btn-animate-icon-pulse">
|
||||||
|
<Icon name="heart" /> Love
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-animate-icon btn-animate-icon-rotate">
|
||||||
|
<Icon name="x" /> Close
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-animate-icon btn-animate-icon-tada">
|
||||||
|
<Icon name="check" /> Confirm
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-animate-icon">
|
||||||
|
Next <Icon name="chevron-right" class="icon-end" />
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-animate-icon btn-animate-icon-move-start">
|
||||||
|
<Icon name="chevron-left" /> Previous
|
||||||
|
</a>
|
||||||
|
</ButtonList>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Standard</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Fill a button with a theme color to mark its purpose.</div>
|
||||||
|
<ButtonList>
|
||||||
|
{
|
||||||
|
themeColors.map(([name, color]) => (
|
||||||
|
<a class={`btn btn-${name}`}>
|
||||||
|
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ButtonList>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Outline</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Keep the border colored and the background transparent for a subtler action.</div>
|
||||||
|
<ButtonList>
|
||||||
|
{
|
||||||
|
themeColors.map(([name, color]) => (
|
||||||
|
<a class={`btn btn-outline btn-${name}`}>
|
||||||
|
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ButtonList>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Ghost</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Drop the border too — color appears only on hover.</div>
|
||||||
|
<ButtonList>
|
||||||
|
{
|
||||||
|
themeColors.map(([name, color]) => (
|
||||||
|
<a class={`btn btn-ghost btn-${name}`}>
|
||||||
|
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ButtonList>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Pill</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Combine <code>.btn-pill</code> with a theme color.</div>
|
||||||
|
<ButtonList>
|
||||||
|
{
|
||||||
|
themeColors.map(([name, color]) => (
|
||||||
|
<a class={`btn btn-pill btn-${name}`}>
|
||||||
|
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ButtonList>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Square</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Combine <code>.btn-square</code> with a theme color.</div>
|
||||||
|
<ButtonList>
|
||||||
|
{
|
||||||
|
themeColors.map(([name, color]) => (
|
||||||
|
<a class={`btn btn-square btn-${name}`}>
|
||||||
|
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ButtonList>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Extra colors</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">The full Tabler palette, beyond the eight theme colors.</div>
|
||||||
|
<ButtonList>
|
||||||
|
{
|
||||||
|
colors.map(([name, color]) => (
|
||||||
|
<a class={`btn btn-${name}`}>
|
||||||
|
{color.icon && <Icon name={color.icon} />} {color.title}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ButtonList>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Social colors</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Brand colors for linking out to social networks.</div>
|
||||||
|
<ButtonList>
|
||||||
|
{
|
||||||
|
socialColors.map(([name, app]) => (
|
||||||
|
<a class={`btn btn-${name}`}>
|
||||||
|
{app.icon && <Icon name={app.icon} />} {app.title}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ButtonList>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Icon buttons</CardTitle>
|
||||||
|
<div class="text-secondary mb-3 card-subtitle">Social brand colors without a label, for compact toolbars.</div>
|
||||||
|
<ButtonList>{socialColors.map(([name, app]) => <a class={`btn btn-icon btn-${name}`}>{app.icon && <Icon name={app.icon} />}</a>)}</ButtonList>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
@@ -2,20 +2,75 @@
|
|||||||
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
||||||
import { slugifyWord as slug } from '@shared/lib/string-format'
|
import { slugifyWord as slug } from '@shared/lib/string-format'
|
||||||
|
|
||||||
const headers = ['Home', 'Profile', 'Messages', 'Settings', 'About', 'Contact', 'Services', 'Team', 'Work']
|
const articles: [string, string[]][] = [
|
||||||
|
[
|
||||||
const lorem =
|
'Home',
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipisicing elit. A accusantium, alias autem beatae blanditiis corporis debitis eligendi, enim error excepturi exercitationem odit porro quasi reiciendis saepe sapiente veritatis? Aliquam assumenda beatae, cumque delectus dolorem enim, eveniet facere fugit harum illum iure magnam nemo neque nisi omnis, pariatur tenetur vel? Accusantium aut cum deleniti dolor doloribus eum, molestiae nulla officiis quasi. At cupiditate dolor explicabo id nesciunt placeat unde voluptates. Asperiores cum doloremque esse fugit labore quia reprehenderit similique. Architecto est ipsum maiores odio perferendis quibusdam tempore velit? Accusantium aliquid consequatur corporis dignissimos distinctio eos eum fugiat impedit nam obcaecati officiis, porro, quia quibusdam repellendus sapiente suscipit temporibus ullam velit vitae voluptates? Aliquam consectetur consequatur consequuntur deleniti dicta dolores ducimus, excepturi ipsam iure molestias necessitatibus numquam optio quaerat quasi quo repudiandae sed. Ad aliquam animi beatae culpa delectus esse excepturi in incidunt ipsam iusto labore laboriosam minima, nam, nemo nisi nobis, nulla praesentium provident quae quaerat qui quia quibusdam quis quisquam quos repellendus sint suscipit tempora vero vitae! Animi assumenda dolorum eaque, explicabo laborum officia praesentium quia repudiandae. Aliquam asperiores cupiditate deserunt nobis nostrum reprehenderit voluptates? Dolorem doloremque ducimus magni, maxime sint tenetur totam. Accusamus atque beatae consequatur corporis, dignissimos dolore dolores dolorum earum error eum eveniet, facere impedit incidunt minima molestias nemo non nostrum placeat quasi qui ratione repudiandae suscipit tenetur ullam vel velit voluptatibus. Accusantium alias assumenda blanditiis consectetur cupiditate delectus dolor dolores dolorum, ducimus eaque enim, error esse eum fugiat fugit id ipsam ipsum laboriosam laudantium minus modi molestias mollitia necessitatibus nihil odio officia praesentium quaerat quis quisquam quos reiciendis tempora tempore ut velit vitae voluptas voluptatem! Accusantium adipisci architecto assumenda atque aut consectetur consequuntur cum, deserunt doloribus ea excepturi exercitationem expedita explicabo facere fuga fugit impedit iste iusto laboriosam molestiae nihil officiis perferendis porro possimus provident quae quaerat qui quibusdam quos reiciendis repellendus vel vero, voluptatem! Ab amet aperiam assumenda aut error eveniet, id inventore laudantium molestias mollitia natus neque nulla officiis, porro quam quas quisquam repellendus repudiandae saepe sapiente ut voluptas, voluptate. Ab ad alias, aliquam atque consequatur culpa deserunt distinctio eius, enim est ex exercitationem facere facilis itaque magni maiores modi nemo neque perferendis placeat quam quas quia quis quod quos reiciendis sequi sunt tempore vero vitae! Earum explicabo nam quaerat quam quos sed voluptatem. Asperiores debitis dolorum, eaque eligendi optio ullam velit? Aperiam beatae cumque earum et explicabo maxime modi molestias odit, omnis placeat quasi quibusdam, ratione sapiente vel voluptas? A, aliquid beatae dolore eaque eos excepturi expedita facere facilis fugit ipsam iure molestiae molestias natus necessitatibus, nesciunt nulla, numquam obcaecati officia officiis pariatur quaerat quas quisquam rerum sapiente veniam. A aperiam beatae distinctio et illum laboriosam necessitatibus obcaecati porro sed vero. Accusantium at aut consequatur corporis culpa cupiditate delectus dolores eius eligendi, enim error esse est, et excepturi fugit id ipsam ipsum itaque modi mollitia necessitatibus neque non nulla obcaecati officia placeat qui quia saepe sit temporibus totam ut voluptas voluptatibus? Ad consectetur eos est illum laboriosam minus molestiae officia placeat quas tenetur.'
|
[
|
||||||
|
"Welcome back. Here's a quick look at what's new since your last visit — three updates shipped, two conversations are waiting for a reply, and your weekly summary is ready below.",
|
||||||
|
'The dashboard pulls together everything you touched this week: recent files, comments left on your work, and the tasks you marked to revisit. Nothing here requires action unless you want it to.',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Profile',
|
||||||
|
[
|
||||||
|
"Your profile is what teammates see when they open your card — name, role, and a short line about what you're working on. Keep it current; it's the first thing a new collaborator checks before reaching out.",
|
||||||
|
"Profile photos update automatically from your workspace avatar. If you'd rather use a different image, upload one from the settings page and it will sync across every app connected to this account.",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Messages',
|
||||||
|
[
|
||||||
|
'Conversations are grouped by project, so a thread never gets lost between unrelated channels. Mentions are highlighted, and anything addressed directly to you moves to the top of the list.',
|
||||||
|
"Replies you haven't read yet are marked with a dot. Archiving a conversation doesn't delete it — it just clears it from the active list until someone posts in it again.",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Settings',
|
||||||
|
[
|
||||||
|
'Most defaults here are fine for everyday use, but a few are worth reviewing: notification frequency, default timezone, and who can see your activity status.',
|
||||||
|
"Changes save immediately — there's no separate 'apply' step. If something looks wrong after a change, the history tab keeps the last ten versions of your settings so you can roll back.",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'About',
|
||||||
|
[
|
||||||
|
'This project started as an internal tool for a five-person team and grew from there. The goal has stayed the same the whole time: make the boring parts of daily work take less time.',
|
||||||
|
'We ship small updates often rather than large releases a few times a year. That means the product changes gradually, but it also means feedback turns into fixes faster.',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Contact',
|
||||||
|
[
|
||||||
|
'The fastest way to reach the team is the in-app chat — most questions get a reply within a few hours during business days. For anything urgent, use the priority line in your account settings.',
|
||||||
|
'Bug reports and feature requests both go to the same inbox, so include a short description of what you expected versus what happened. Screenshots help more than long explanations.',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Services',
|
||||||
|
[
|
||||||
|
'Onboarding, migration, and training are available as add-ons for teams moving from another tool. A specialist walks through your existing setup and maps it to the equivalent here before anything is switched over.',
|
||||||
|
"Ongoing support plans include a monthly check-in call, priority responses, and early access to features still in testing. Most teams start on the standard plan and upgrade once they've settled in.",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Team',
|
||||||
|
[
|
||||||
|
'The team is spread across four time zones, which means someone is usually online to help. Each person owns a specific part of the product, listed on their profile.',
|
||||||
|
"We hire slowly and keep the team small on purpose — it's easier to keep quality high and response times short when fewer people need to stay in sync.",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
['Work', ['A selection of recent projects is listed below, newest first. Each entry links to a short write-up covering the problem, the approach, and what changed as a result.', 'Older work is archived rather than deleted, so nothing here disappears — it just moves further down the list as new projects are added.']],
|
||||||
|
]
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="Scroll Spy" pageHeader="Scroll Spy">
|
<DefaultLayout title="Scroll Spy" pageHeader="Scroll Spy" pageMenu="base.scroll-spy">
|
||||||
<div class="row g-5">
|
<div class="row g-5">
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
<div class="sticky-top">
|
<div class="sticky-top">
|
||||||
<h3>Menu</h3>
|
<h3>Menu</h3>
|
||||||
<nav class="nav nav-vertical nav-pills" id="pills">
|
<nav class="nav nav-vertical nav-pills" id="pills">
|
||||||
{
|
{
|
||||||
headers.map((header) => (
|
articles.map(([header]) => (
|
||||||
<a class="nav-link" href={`#pills-${slug(header)}`}>
|
<a class="nav-link" href={`#pills-${slug(header)}`}>
|
||||||
{header}
|
{header}
|
||||||
</a>
|
</a>
|
||||||
@@ -28,10 +83,12 @@ const lorem =
|
|||||||
<div class="card card-lg">
|
<div class="card card-lg">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{
|
{
|
||||||
headers.map((header) => (
|
articles.map(([header, paragraphs]) => (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<h3 id={`pills-${slug(header)}`}>{header}</h3>
|
<h3 id={`pills-${slug(header)}`}>{header}</h3>
|
||||||
<p class="mb-6">{lorem}</p>
|
{paragraphs.map((paragraph, index) => (
|
||||||
|
<p class={index === paragraphs.length - 1 ? 'mb-6' : undefined}>{paragraph}</p>
|
||||||
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,41 +3,65 @@ import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
|
|||||||
import Rating from '@ui/Rating.astro'
|
import Rating from '@ui/Rating.astro'
|
||||||
import Card from '@ui/Card.astro'
|
import Card from '@ui/Card.astro'
|
||||||
import CardBody from '@ui/CardBody.astro'
|
import CardBody from '@ui/CardBody.astro'
|
||||||
|
import CardTitle from '@ui/CardTitle.astro'
|
||||||
|
import Icon from '@ui/Icon.astro'
|
||||||
|
import { getDocsUrl } from '@shared/lib/string-format'
|
||||||
|
|
||||||
|
const starsRatingDocsUrl = getDocsUrl('/ui/components/star-rating')
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="Star Ratings" pageHeader="Star Ratings" pageMenu="base.stars-rating" pageLibs={['tabler-flags', 'star-rating.js']}>
|
<DefaultLayout title="Star Ratings" pageMenu="base.stars-rating" pageLibs={['tabler-flags', 'star-rating.js']} description="An interactive star rating built on the star-rating.js widget, backed by a hidden select for accessibility and forms.">
|
||||||
<div class="row row-cards">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<div class="col-md-4">
|
<h1 class="page-title">Star Ratings</h1>
|
||||||
<div class="row row-cards">
|
<a href={starsRatingDocsUrl} target="_blank" rel="noopener" class="link-secondary">
|
||||||
<div class="col-12">
|
Docs <Icon name="external-link" class="icon-sm" />
|
||||||
<Card>
|
</a>
|
||||||
<CardBody>
|
</div>
|
||||||
<h3 class="card-title">Basic</h3>
|
|
||||||
<div><Rating id="basic" value={4} /></div>
|
<div class="row row-cards mb-5">
|
||||||
</CardBody>
|
<div class="col-md-6 col-lg-4">
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
<div class="col-12">
|
|
||||||
<Card>
|
|
||||||
<CardBody>
|
|
||||||
<h3 class="card-title">Different icon</h3>
|
|
||||||
<div class="space-y">
|
|
||||||
<Rating id="different-icon" value={4} />
|
|
||||||
<Rating icon="heart" value={4} color="red" />
|
|
||||||
<Rating icon="ghost" value={4} color="azure" />
|
|
||||||
<Rating icon="circle" value={4} color="green" />
|
|
||||||
</div>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<h3 class="card-title">Custom colors</h3>
|
<CardTitle>Basic</CardTitle>
|
||||||
|
<div class="card-subtitle">Click a star to set the value.</div>
|
||||||
|
<Rating id="basic" value={4} />
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Icons</CardTitle>
|
||||||
|
<div class="card-subtitle">Swap the star for any icon with the <code>icon</code> prop.</div>
|
||||||
<div class="space-y">
|
<div class="space-y">
|
||||||
<Rating id="color" value={3} />
|
<Rating id="icon-star" value={4} />
|
||||||
|
<Rating id="icon-heart" icon="heart" value={4} color="red" />
|
||||||
|
<Rating id="icon-ghost" icon="ghost" value={4} color="azure" />
|
||||||
|
<Rating id="icon-circle" icon="circle" value={4} color="green" />
|
||||||
|
</div>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Sizes</CardTitle>
|
||||||
|
<div class="card-subtitle">Match the rating to surrounding text with <code>size</code>.</div>
|
||||||
|
<div class="space-y">
|
||||||
|
<Rating id="size-sm" value={3} size="sm" />
|
||||||
|
<Rating id="size-md" value={3} size="md" />
|
||||||
|
<Rating id="size-lg" value={3} size="lg" />
|
||||||
|
</div>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<CardTitle>Colors</CardTitle>
|
||||||
|
<div class="card-subtitle">Set <code>color</code> to any theme or extended color to match the rest of the page.</div>
|
||||||
|
<div class="space-y">
|
||||||
|
<Rating id="color-default" value={3} />
|
||||||
<Rating id="color-primary" color="primary" value={3} />
|
<Rating id="color-primary" color="primary" value={3} />
|
||||||
<Rating id="color-red" color="red" value={3} />
|
<Rating id="color-red" color="red" value={3} />
|
||||||
<Rating id="color-lime" color="lime" value={3} />
|
<Rating id="color-lime" color="lime" value={3} />
|
||||||
@@ -45,18 +69,5 @@ import CardBody from '@ui/CardBody.astro'
|
|||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
|
||||||
<Card>
|
|
||||||
<CardBody>
|
|
||||||
<h3 class="card-title">Custom sizes</h3>
|
|
||||||
<div class="space-y">
|
|
||||||
<Rating id="size-sm" value={3} size="sm" />
|
|
||||||
<Rating id="size-primary" value={3} />
|
|
||||||
<Rating id="size-md" value={3} size="md" />
|
|
||||||
<Rating id="size-lg" value={3} size="lg" />
|
|
||||||
</div>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { capitalize, firstLetters, formatNumber, millisecondsToMinutes, parseCurrency, parsePercentage, pathSlug, roundTo, slugifyWord, sortBy, ucFirst } from './string-format'
|
import { capitalize, firstLetters, formatNumber, getDocsUrl, millisecondsToMinutes, parseCurrency, parsePercentage, pathSlug, roundTo, slugifyWord, sortBy, ucFirst } from './string-format'
|
||||||
import { requireIndex } from './array'
|
import { requireIndex } from './array'
|
||||||
|
|
||||||
|
describe('getDocsUrl', () => {
|
||||||
|
it('appends the path to the docs.tabler.io origin', () => {
|
||||||
|
expect(getDocsUrl('/ui/components/button#ghost-buttons')).toBe('https://docs.tabler.io/ui/components/button#ghost-buttons')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('capitalize', () => {
|
describe('capitalize', () => {
|
||||||
it('uppercases the first char and lowercases the rest', () => {
|
it('uppercases the first char and lowercases the rest', () => {
|
||||||
expect(capitalize('DISABLED')).toBe('Disabled')
|
expect(capitalize('DISABLED')).toBe('Disabled')
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
// String/number formatting helpers used by the demo data across components.
|
// String/number formatting helpers used by the demo data across components.
|
||||||
|
import { site } from './site'
|
||||||
|
|
||||||
|
/** Builds a docs.tabler.io URL from a path, e.g. "/ui/components/button#ghost-buttons". */
|
||||||
|
export function getDocsUrl(path: string): string {
|
||||||
|
return `${site.docsUrl}${path}`
|
||||||
|
}
|
||||||
|
|
||||||
/** Uppercases the first char, lowercases the rest. */
|
/** Uppercases the first char, lowercases the rest. */
|
||||||
export function capitalize(s: string): string {
|
export function capitalize(s: string): string {
|
||||||
|
|||||||
Reference in new issue
Block a user