Add segmented control component (#2087)

* Add segmented control component and update related files

* Implement segmented navigation component and update related styles

* Create grumpy-flowers-act.md

* Refactor .gitignore and move playground pages to preview directory

* Enhance segmented navigation with hover state and class support

* Refactor segmented control layout for improved responsiveness and alignment
This commit is contained in:
Paweł Kuna
2025-02-06 18:02:46 +01:00
committed by GitHub
parent 117fbbb2d5
commit cb278c762d
14 changed files with 278 additions and 170 deletions

View File

@@ -0,0 +1,6 @@
---
"@tabler/core": patch
"preview": patch
---
Add segmented control component

2
.gitignore vendored
View File

@@ -20,9 +20,9 @@ node_modules/
/components/
/percy.sh
/preview/pages/playground.html
/preview/pages/playground-*.html
/preview/pages/screenshot.html
/preview/pages/screenshot-*.html
/preview/pages/playground-*.html
/preview/pages/features.html
.pnp.loader.mjs

View File

@@ -49,6 +49,7 @@
@import "ui/ribbons";
@import "ui/markdown";
@import "ui/placeholder";
@import "ui/segmented";
@import "ui/steps";
@import "ui/status";
@import "ui/switch-icon";

View File

@@ -0,0 +1,101 @@
.nav-segmented {
--#{$prefix}nav-bg: var(--#{$prefix}bg-surface-tertiary);
--#{$prefix}nav-padding: 2px;
--#{$prefix}nav-height: 2.5rem;
--#{$prefix}nav-gap: .25rem;
--#{$prefix}nav-active-bg: var(--#{$prefix}bg-surface);
--#{$prefix}nav-font-size: inherit;
--#{$prefix}nav-radius: 6px;
--#{$prefix}nav-link-disabled-color: var(--#{$prefix}disabled-color);
--#{$prefix}nav-link-gap: .25rem;
--#{$prefix}nav-link-padding-x: .75rem;
--#{$prefix}nav-link-icon-size: 1.25rem;
display: inline-flex;
flex-wrap: wrap;
gap: var(--#{$prefix}nav-gap);
padding: var(--#{$prefix}nav-padding);
list-style: none;
background: var(--#{$prefix}nav-bg);
border-radius: calc(var(--#{$prefix}nav-radius) + var(--#{$prefix}nav-padding));
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .04);
.nav-link {
display: inline-flex;
gap: calc(.25rem + var(--#{$prefix}nav-link-gap));
align-items: center;
margin: 0;
font-size: var(--#{$prefix}nav-font-size);
min-width: calc(var(--#{$prefix}nav-height) - 2 * var(--#{$prefix}nav-padding));
height: calc(var(--#{$prefix}nav-height) - 2 * var(--#{$prefix}nav-padding));
padding: 0 calc(var(--#{$prefix}nav-link-padding-x) - 2px);
border: 1px solid transparent;
background: transparent;
color: var(--#{$prefix}secondary);
text-align: center;
text-decoration: none;
white-space: nowrap;
cursor: pointer;
transition: background-color $transition-time, color $transition-time;
border-radius: var(--#{$prefix}nav-radius);
flex-grow: 1;
justify-content: center;
&:hover,
&.hover {
background: rgba(0, 0, 0, .04);
color: var(--#{$prefix}body-color);
}
&.disabled,
&:disabled {
color: var(--#{$prefix}nav-link-disabled-color);
cursor: not-allowed;
}
}
.nav-link-input:checked + .nav-link,
.nav-link.active {
color: var(--#{$prefix}body-color);
background: var(--#{$prefix}nav-active-bg);
border-color: var(--#{$prefix}border-color);
}
.nav-link-input {
display: none;
}
.nav-link-icon {
width: var(--#{$prefix}nav-link-icon-size);
height: var(--#{$prefix}nav-link-icon-size);
margin: 0 -.25rem;
color: inherit;
}
}
.nav-segmented-vertical {
flex-direction: column;
.nav-link {
justify-content: flex-start;
}
}
.nav-sm {
--#{$prefix}nav-height: 2rem;
--#{$prefix}nav-font-size: var(--tblr-font-size-h5);
--#{$prefix}nav-radius: 4px;
--#{$prefix}nav-link-padding-x: .5rem;
--#{$prefix}nav-link-gap: .25rem;
--#{$prefix}nav-link-icon-size: 1rem;
}
.nav-lg {
--#{$prefix}nav-height: 3rem;
--#{$prefix}nav-font-size: var(--tblr-font-size-h3);
--#{$prefix}nav-radius: 8px;
--#{$prefix}nav-link-padding-x: 1rem;
--#{$prefix}nav-link-gap: .5rem;
--#{$prefix}nav-link-icon-size: 1.5rem;
}

View File

@@ -50,8 +50,8 @@ export default function (eleventyConfig) {
eleventyConfig.setLayoutsDirectory("_layouts");
eleventyConfig.setIncludesDirectory("_includes");
eleventyConfig.addWatchTarget("../core/dist/**");
eleventyConfig.setWatchThrottleWaitTime(100);
// eleventyConfig.addWatchTarget("../core/dist/**");
// eleventyConfig.setWatchThrottleWaitTime(100);
eleventyConfig.addPassthroughCopy(getCopyList());
eleventyConfig.setServerPassthroughCopyBehavior("passthrough");
@@ -66,6 +66,13 @@ export default function (eleventyConfig) {
dynamicPartials: true,
jekyllWhere: true,
});
/**
* Server
*/
if (process.env.ELEVENTY_RUN_MODE === "serve") {
eleventyConfig.setServerPassthroughCopyBehavior("passthrough");
}
/**
* Data
@@ -422,6 +429,10 @@ export default function (eleventyConfig) {
}
});
eleventyConfig.addFilter("contains", (items, item) => {
return items && Array.isArray(items) && items.includes(item);
});
eleventyConfig.addFilter("concat_objects", function (object, object2) {
if (
object &&

View File

@@ -29,10 +29,10 @@
"zip": "mkdir -p packages-zip && zip -r packages-zip/tabler-$(node -p \"require('./package.json').version\").zip demo/*"
},
"dependencies": {
"@tabler/core": "workspace:*",
"@tabler/icons": "^3.29.0",
"@melloware/coloris": "^0.19.1",
"apexcharts": "^4.4.0",
"@tabler/core": "workspace:*",
"star-rating.js": "^4.3.1",
"tinymce": "^7.6.0",
"tom-select": "^2.4.1",

View File

@@ -193,6 +193,11 @@
"url": "placeholder.html",
"title": "Placeholder"
},
"segmented-control": {
"title": "Segmented control",
"url": "segmented-control.html",
"badge": "New"
},
"social": {
"title": "Social icons",
"url": "social-icons.html"

View File

@@ -3,7 +3,7 @@
{% assign actions = page-header-actions | default: layout.page-header-actions %}
{% assign pretitle = page-header-pretitle | default: layout.page-header-pretitle %}
{% assign class = page-header-class | default: layout.page-header-class %}
{% assign icon = page-header-icon | default: layout.page-header-icon %}
{% assign page-icon = page-header-icon | default: layout.page-header-icon %}
{% if page-header-file %}
{% include "layout/headers/{{ page-header-file }}.html" %}
@@ -20,8 +20,8 @@
</div>
{% endif %}
<h2 class="page-title">
{% if icon %}
{% include "ui/icon.html" icon=icon %}
{% if page-icon %}
{% include "ui/icon.html" icon=page-icon %}
{% endif %}
{{ page-header }}
</h2>

View File

@@ -23,7 +23,7 @@
{% if site.useIconfont %}
<i class="icon ti ti-{{ icon-name }}{% if include.color %} {{ include.color }}{% endif %}{% if include.class %} {{ include.class }}{% endif %}"></i>
{% else %}
{% elsif icons[icon-name] %}
<!-- Download SVG icon from http://tabler.io/icons/icon/{{ icon-name }} -->
{% assign svg-icon = icons[icon-name].svg[icon-type] | default: '' %}
{% assign svg-icon = svg-icon | replace: '<path stroke="none" d="M0 0h24v24H0z" fill="none"/>', '' %}

View File

@@ -0,0 +1,28 @@
{% assign segmented-items = include.items | default: "" | split: "," %}
{% assign segmented-icons = include.icons | default: "" | split: "," %}
{% assign segmented-disabled = include.disabled | default: "" | split: "," %}
{% assign segmented-hover = include.hover | default: "" %}
{% assign segmented-items-count = segmented-items | size %}
{% assign segmented-icons-count = segmented-icons | size %}
{% assign segmented-all-count = segmented-items-count %}
{% if segmented-icons-count > segmented-all-count %}{% assign segmented-all-count = segmented-icons-count %}{% endif %}
<nav class="nav nav-segmented{% if include.vertical %} nav-segmented-vertical{% endif %}{% if include.size %} nav-{{ include.size }}{% endif %}{% if include.full-width %} w-100{% endif %}{% if include.class %} {{ include.class }}{% endif %}" role="tablist">
{% for i in (1..segmented-all-count) %}
{% assign index = forloop.index | append: "" %}
{% assign disabled = segmented-disabled | contains: index %}
{% if include.name %}<input type="radio" class="nav-link-input" name="segmented" id="segmented-{{include.name }}-{{ index }}" {% if forloop.index == 1 %}checked{% endif %} />{% endif %}
<{% if include.name %}label for="segmented-{{include.name }}-{{ index }}"{% else %}button{% endif %} class="nav-link{% if forloop.index == 1 %}{% unless include.name %} active{% endunless %}{% endif %}{% if disabled %} disabled{% endif %}{% if segmented-hover == index %} hover{% endif %}" role="tab"{% unless include.name %} data-bs-toggle="tab"{% endunless %} aria-selected="{% if forloop.index == 1 %}true{% else %}false{% endif %}" {% if disabled %} aria-disabled="true"{% endif %}{% if forloop.index == 1 %} aria-current="page"{% endif %}>
{% if segmented-icons[forloop.index0] %}
{% include "ui/icon.html" icon=segmented-icons[forloop.index0] class="nav-link-icon" %}
{% endif %}
{% if segmented-items[forloop.index0] %}
{{ segmented-items[forloop.index0] }}
{% endif %}
</{% if include.name %}label{% else %}button{% endif %}>
{% endfor %}
</nav>

View File

@@ -1,15 +1,15 @@
{% assign value = include.value | default: 25 %}
{% if value > 0 %}
{% assign color = 'green' %}
{% assign icon = 'trending-up' %}
{% assign trending-color = 'green' %}
{% assign trending-icon = 'trending-up' %}
{% elsif value == 0 %}
{% assign color = 'yellow' %}
{% assign icon = 'minus' %}
{% assign trending-color = 'yellow' %}
{% assign trending-icon = 'minus' %}
{% else %}
{% assign color = 'red' %}
{% assign icon = 'trending-down' %}
{% assign trending-color = 'red' %}
{% assign trending-icon = 'trending-down' %}
{% endif %}
<span class="text-{{ color }} d-inline-flex align-items-center lh-1{% if include.class %} {{ include.class }}{% endif %}">
{{ value }}% {% include "ui/icon.html" icon=icon class="ms-1" %}
<span class="text-{{ trending-color }} d-inline-flex align-items-center lh-1{% if include.class %} {{ include.class }}{% endif %}">
{{ value }}% {% include "ui/icon.html" icon=trending-icon class="ms-1" %}
</span>

View File

@@ -1,5 +1,5 @@
---
title: Playground
title: Chat
page-header: Chat
page-menu: extra.chat
layout: default

View File

@@ -1,153 +0,0 @@
---
layout: default
permalink: playground.html
---
<div class="container-xl">
<div class="page-header d-print-none mb-4">
<div class="row align-items-center">
<div class="col">
<div class="page-pretitle"></div>
<div class="d-flex align-items-center">
<img
src="https://hebbkx1anhila5yf.public.blob.vercel-storage.com/chips-b5dF2YPuqsOD3mhXWZ7cLs6YW8B5QM.png"
alt="Tabler Logo"
class="h-8 me-2"
/>
<h2 class="page-title">tabler</h2>
</div>
</div>
</div>
</div>
<div class="row row-deck row-cards">
<div class="col-sm-6 col-lg-6">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Sales</div>
<div class="ms-auto lh-1">
<div class="text-success d-inline-flex align-items-center lh-1">
1%{" "}
<svg
xmlns="http://www.w3.org/2000/svg"
class="icon ms-1"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M3 17l6 -6l4 4l8 -8" />
<path d="M14 7l7 0l0 7" />
</svg>
</div>
</div>
</div>
<div class="d-flex align-items-baseline">
<div class="h1 mb-0 me-2">132</div>
<div class="me-auto">
<span class="text-muted">12 waiting payments</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-6">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Orders</div>
<div class="ms-auto lh-1">
<div class="text-muted d-inline-flex align-items-center lh-1">
0% <span class="ms-1"></span>
</div>
</div>
</div>
<div class="d-flex align-items-baseline">
<div class="h1 mb-0 me-2">78</div>
<div class="me-auto">
<span class="text-muted">32 shipped</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-6">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Shares</div>
<div class="ms-auto lh-1">
<div class="text-danger d-inline-flex align-items-center lh-1">
4%{" "}
<svg
xmlns="http://www.w3.org/2000/svg"
class="icon ms-1"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M3 7l6 6l4 -4l8 8" />
<path d="M14 17l7 0l0 -7" />
</svg>
</div>
</div>
</div>
<div class="d-flex align-items-baseline">
<div class="h1 mb-0 me-2">623</div>
<div class="me-auto">
<span class="text-muted">16 today</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-6">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Likes</div>
<div class="ms-auto lh-1">
<div class="text-success d-inline-flex align-items-center lh-1">
8%{" "}
<svg
xmlns="http://www.w3.org/2000/svg"
class="icon ms-1"
width="24"
height="24"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M3 17l6 -6l4 4l8 -8" />
<path d="M14 7l7 0l0 7" />
</svg>
</div>
</div>
</div>
<div class="d-flex align-items-baseline">
<div class="h1 mb-0 me-2">132</div>
<div class="me-auto">
<span class="text-muted">21 today</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,109 @@
---
layout: default
title: Segmented control
permalink: /segmented-control.html
page-header: Segmented control
page-menu: base.segmented-control
---
<div class="row row-cards">
<div class="col-md-6">
<div class="card">
<div class="card-body">
{% include "ui/nav-segmented.html" items="1,2,3,4" %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
{% include "ui/nav-segmented.html" items="👦,👦🏿,👦🏾,👦🏽,👦🏼,👦🏻" %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
{% include "ui/nav-segmented.html" icons="home,star,clock,ghost,bold,italic,underline" %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
{% include "ui/nav-segmented.html" items="List,Kanban,Calendar,Files" icons="list,layout,calendar,files" %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
{% include "ui/nav-segmented.html" items="List,Kanban,Calendar,Files" icons="list,layout,calendar,files" vertical %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
{% include "ui/nav-segmented.html" icons="home,star,clock,ghost,bold,italic,underline" vertical disabled="3" %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
{% include "ui/nav-segmented.html" items="Daily,Weekly,Monthly,Quarterly,Yearly" disabled="4,5" %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
{% include "ui/nav-segmented.html" items="Daily,Weekly,Monthly,Quarterly,Yearly" vertical=true %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
{% include "ui/nav-segmented.html" items="Daily,Weekly,Monthly,Quarterly,Yearly" name="checkbox" %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
{% include "ui/nav-segmented.html" items="Daily,Weekly,Monthly,Quarterly,Yearly" full-width=true %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
<div class="space-y">
<div>{% include "ui/nav-segmented.html" items="Overview,Analytics,Reports,Notifications" full-width=true %}</div>
<div>{% include "ui/nav-segmented.html" items="Account,Password" full-width=true %}</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
<div class="space-y">
<div>{% include "ui/nav-segmented.html" items="List,Kanban,Calendar,Files" disabled="3,5" size="sm" %}</div>
<div>{% include "ui/nav-segmented.html" items="List,Kanban,Calendar,Files" disabled="3,5" %}</div>
<div>{% include "ui/nav-segmented.html" items="List,Kanban,Calendar,Files" disabled="3,5" size="lg" %}</div>
<div>{% include "ui/nav-segmented.html" items="List,Kanban,Calendar,Files" icons="list,layout,calendar,files" disabled="3,5" size="sm" %}</div>
<div>{% include "ui/nav-segmented.html" items="List,Kanban,Calendar,Files" icons="list,layout,calendar,files" disabled="3,5" %}</div>
<div>{% include "ui/nav-segmented.html" items="List,Kanban,Calendar,Files" icons="list,layout,calendar,files" disabled="3,5" size="lg" %}</div>
<div>{% include "ui/nav-segmented.html" icons="list,layout,calendar,files" disabled="3,5" size="sm" %}</div>
<div>{% include "ui/nav-segmented.html" icons="list,layout,calendar,files" disabled="3,5" %}</div>
<div>{% include "ui/nav-segmented.html" icons="list,layout,calendar,files" disabled="3,5" size="lg" %}</div>
</div>
</div>
</div>
</div>
</div>