mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-13 21:58:17 +00:00
372 lines
15 KiB
Plaintext
372 lines
15 KiB
Plaintext
---
|
|
layout: layouts/page.njk
|
|
title: Input Group
|
|
description: Display additional information or actions to an input or textarea.
|
|
toc:
|
|
- label: Usage
|
|
id: usage
|
|
- label: Examples
|
|
id: examples
|
|
children:
|
|
- label: Icon
|
|
id: example-icon
|
|
- label: Text
|
|
id: example-text
|
|
- label: Button
|
|
id: example-button
|
|
- label: Tooltip
|
|
id: example-tooltip
|
|
- label: Textarea
|
|
id: example-textarea
|
|
- label: Spinner
|
|
id: example-spinner
|
|
- label: Dropdown
|
|
id: example-dropdown
|
|
- label: Group
|
|
id: example-group
|
|
---
|
|
|
|
{% from "macros/code_preview.njk" import code_preview %}
|
|
{% from "macros/code_block.njk" import code_block %}
|
|
{% from "dropdown-menu.njk" import dropdown_menu %}
|
|
{% from "popover.njk" import popover %}
|
|
|
|
<div class="alert mb-6">
|
|
{% lucide "circle-alert" %}
|
|
<h2>There is no dedicated Input Group component in Basecoat.</h2>
|
|
</div>
|
|
|
|
{% set code_default %}
|
|
<div class="grid gap-6">
|
|
<div class="relative">
|
|
<input type="text" class="input pl-9 pr-20" placeholder="Search...">
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
|
|
{% lucide "search" %}
|
|
</div>
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">12 results</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input pl-15 pr-9" placeholder="example.com">
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">https://</div>
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4" data-tooltip="This is content in a tooltip.">{% lucide "info" %}</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<textarea class="textarea pr-10 min-h-27 pb-12" placeholder="Ask, Search or Chat..."></textarea>
|
|
<footer role="group" class="absolute bottom-0 px-3 pb-3 pt-1.5 flex items-center w-full gap-2">
|
|
<button type="button" class="btn-icon-outline rounded-full size-6 text-muted-foreground hover:text-accent-foreground">
|
|
{% lucide "plus" %}
|
|
</button>
|
|
{{ dropdown_menu(
|
|
id="demo-dropdown-menu",
|
|
trigger="Auto",
|
|
trigger_attrs={"class": "btn-sm-ghost text-muted-foreground hover:text-accent-foreground h-6 p-2"},
|
|
popover_attrs={"data-side": "top", "class": "min-w-32"},
|
|
items=[
|
|
{ type: "item", label: "Auto" },
|
|
{ type: "item", label: "Agent" },
|
|
{ type: "item", label: "Manual" }
|
|
]
|
|
) }}
|
|
<div class="text-muted-foreground text-sm ml-auto">52% used</div>
|
|
<hr class="w-0 h-4 border-r border-border shrink-0 m-0">
|
|
<button type="button" class="btn-icon rounded-full size-6 bg-muted-foreground hover:bg-foreground" disabled>
|
|
{% lucide "arrow-up" %}
|
|
</button>
|
|
</footer>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input pr-9" placeholder="@shadcn">
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none bg-primary text-primary-foreground flex size-4 items-center justify-center rounded-full [&>svg]:size-3">{% lucide "check" %}</div>
|
|
</div>
|
|
</div>
|
|
{% endset %}
|
|
|
|
{{ code_preview("input-group-default", code_default, class="w-full max-w-sm") }}
|
|
|
|
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
|
|
|
<section class="prose">
|
|
<p>Input groups are pure HTML composition using Tailwind utility classes. There is no dedicated component: use <code>relative</code> positioning on a wrapper, add your content with <code>absolute</code> positioning, and adjust input padding to make room.</p>
|
|
<p>Browse the examples below and copy the pattern that fits your needs.</p>
|
|
</section>
|
|
|
|
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
|
|
|
<h3 id="example-icon"><a href="#example-icon">Icon</a></h3>
|
|
|
|
{% set code_icon %}
|
|
<div class="grid gap-6">
|
|
<div class="relative">
|
|
<input type="text" class="input pl-9" placeholder="Search...">
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
|
|
{% lucide "search" %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="email" class="input pl-9" placeholder="Enter your email">
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
|
|
{% lucide "mail" %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input px-9" placeholder="Card number">
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
|
|
{% lucide "credit-card" %}
|
|
</div>
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
|
|
{% lucide "check" %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input pr-16" placeholder="Card number">
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4 flex items-center gap-2">
|
|
{% lucide "star" %}
|
|
{% lucide "info" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endset %}
|
|
|
|
{{ code_preview("input-group-icon-prefix", code_icon, class="w-full max-w-sm") }}
|
|
|
|
<h3 id="example-text"><a href="#example-text">Text</a></h3>
|
|
|
|
{% set code_text %}
|
|
<div class="grid gap-6">
|
|
<div class="relative">
|
|
<input type="text" class="input pl-7 pr-12" placeholder="0.00">
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">$</div>
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">USD</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input pl-15 pr-12" placeholder="example.com">
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">https://</div>
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">.com</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input pr-30" placeholder="Enter your username">
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">@company.com</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<textarea class="textarea pr-10 min-h-25 pb-12" placeholder="Enter your message"></textarea>
|
|
<footer role="group" class="absolute bottom-0 px-3 pb-3 pt-1.5 flex items-center w-full gap-2">
|
|
<div class="text-muted-foreground text-sm">120 characters left</div>
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
{% endset %}
|
|
|
|
{{ code_preview("input-group-text", code_text, class="w-full max-w-sm") }}
|
|
|
|
<h3 id="example-button"><a href="#example-button">Button</a></h3>
|
|
|
|
{% set code_button %}
|
|
<div class="grid gap-6">
|
|
<div class="relative">
|
|
<input type="text" readonly class="input pr-9" value="https://x.com/hunvreus">
|
|
<button
|
|
data-copied="false"
|
|
onclick="
|
|
this.dataset.copied = 'true';
|
|
setTimeout(() => this.dataset.copied = 'false', 2000);
|
|
"
|
|
class="group absolute right-1.5 top-1/2 -translate-y-1/2 btn-icon-ghost text-muted-foreground hover:text-accent-foreground size-6"
|
|
>
|
|
{% lucide "copy", { "class": "group-data-[copied=true]:hidden" } %}
|
|
{% lucide "check", { "class": "hidden group-data-[copied=true]:block" } %}
|
|
</button>
|
|
</div>
|
|
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input pl-21 pr-9 rounded-full">
|
|
<button
|
|
aria-pressed="false"
|
|
onclick="this.ariaPressed = this.ariaPressed === 'true' ? 'false' : 'true'"
|
|
class="absolute right-1.5 top-1/2 -translate-y-1/2 btn-icon-ghost text-muted-foreground hover:text-accent-foreground size-6 rounded-full aria-pressed:bg-transparent aria-pressed:hover:bg-accent dark:aria-pressed:hover:bg-accent/50 aria-pressed:text-blue-600 aria-pressed:[&>svg]:fill-blue-600"
|
|
>{% lucide "star" %}</button>
|
|
<div class="absolute left-1.5 top-1/2 -translate-y-1/2 flex items-center gap-1 z-10">
|
|
{% set trigger %}
|
|
{% lucide "info" %}
|
|
{% endset %}
|
|
{% call popover(
|
|
trigger=trigger,
|
|
trigger_attrs={"class": "btn-sm-icon-ghost rounded-full size-6"},
|
|
popover_attrs={"class": "max-w-72"}
|
|
) %}
|
|
<h3 class="font-medium mb-1">Your connection is not secure.</h3>
|
|
<p>You should not enter any sensitive information on this site.</p>
|
|
{% endcall %}
|
|
<div class="text-muted-foreground text-sm pointer-events-none">https://</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" readonly class="input pr-9" placeholder="Type to search...">
|
|
<button type="button" class="absolute right-1.5 top-1/2 -translate-y-1/2 btn-secondary h-6 p-2 rounded">Search</button>
|
|
</div>
|
|
</div>
|
|
{% endset %}
|
|
|
|
{{ code_preview("input-group-button", code_button, class="w-full max-w-sm") }}
|
|
|
|
<h3 id="example-tooltip"><a href="#example-tooltip">Tooltip</a></h3>
|
|
|
|
{% set code_tooltip %}
|
|
<div class="grid gap-6">
|
|
<div class="relative">
|
|
<input type="password" class="input pr-9" placeholder="Enter password">
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4" data-tooltip="Password must be at least 8 characters long.">{% lucide "info" %}</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="email" class="input pr-9" placeholder="Your email address">
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4" data-tooltip="We'll use this to send your notifications.">{% lucide "circle-help" %}</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="email" class="input pl-9" placeholder="Your email address">
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4" data-tooltip="Click for help with API keys." data-side="left">{% lucide "circle-help" %}</div>
|
|
</div>
|
|
</div>
|
|
{% endset %}
|
|
|
|
{{ code_preview("input-group-tooltip", code_tooltip, class="w-full max-w-sm") }}
|
|
|
|
<h3 id="example-textarea"><a href="#example-textarea">Textarea</a></h3>
|
|
|
|
{% set code_textarea %}
|
|
<div class="relative">
|
|
<textarea class="textarea pt-15 pb-17 min-h-77" placeholder="console.log('Hello, world!')."></textarea>
|
|
<header class="absolute top-0 flex items-center w-full gap-2 p-3 border-b">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-brand-javascript size-4 text-muted-foreground"><path d="M20 4l-2 14.5l-6 2l-6 -2l-2 -14.5z"></path><path d="M7.5 8h3v8l-2 -1"></path><path d="M16.5 8h-2.5a.5 .5 0 0 0 -.5 .5v3a.5 .5 0 0 0 .5 .5h1.423a.5 .5 0 0 1 .495 .57l-.418 2.93l-2 .5"></path></svg>
|
|
<span class="font-mono text-sm text-muted-foreground mr-auto">script.js</span>
|
|
<button class="btn-sm-icon-ghost text-muted-foreground hover:text-accent-foreground size-6">
|
|
{% lucide "refresh-ccw" %}
|
|
</button>
|
|
<button class="btn-sm-icon-ghost text-muted-foreground hover:text-accent-foreground size-6">
|
|
{% lucide "copy" %}
|
|
</button>
|
|
</header>
|
|
<footer class="absolute bottom-0 flex items-center w-full gap-2 p-3 border-t">
|
|
<span class="text-sm text-muted-foreground mr-auto">Line 1, Column 1</span>
|
|
<button type="button" class="btn-sm">
|
|
Run
|
|
{% lucide "corner-down-left" %}
|
|
</button>
|
|
</footer>
|
|
</div>
|
|
{% endset %}
|
|
|
|
{{ code_preview("input-group-textarea", code_textarea, class="w-full max-w-md") }}
|
|
|
|
<h3 id="example-spinner"><a href="#example-spinner">Spinner</a></h3>
|
|
|
|
{% set code_spinner %}
|
|
<div class="grid gap-6">
|
|
<div class="relative">
|
|
<input type="text" class="input pr-9" placeholder="Searching..." disabled>
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4 opacity-50">
|
|
{% lucide "loader-circle", { "class": "animate-spin" } %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input pl-9" placeholder="Processing..." disabled>
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4 opacity-50">
|
|
{% lucide "loader-circle", { "class": "animate-spin" } %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input pr-24" placeholder="Saving changes..." disabled>
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-2 text-sm text-muted-foreground opacity-50">
|
|
<div>Saving...</div>
|
|
<div class="[&>svg]:size-4">
|
|
{% lucide "loader-circle", { "class": "animate-spin" } %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input pl-9 pr-26" placeholder="Refreshing data..." disabled>
|
|
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4 opacity-50">
|
|
{% lucide "loader", { "class": "animate-spin" } %}
|
|
</div>
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-2 text-sm text-muted-foreground opacity-50">
|
|
Please wait...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endset %}
|
|
|
|
{{ code_preview("input-group-spinner", code_spinner, class="w-full max-w-md") }}
|
|
|
|
<h3 id="example-dropdown"><a href="#example-dropdown">Dropdown</a></h3>
|
|
|
|
{% set code_dropdown %}
|
|
<div class="grid gap-6">
|
|
<div class="relative">
|
|
<input type="text" class="input pr-9" placeholder="Enter file name">
|
|
{% set trigger %}
|
|
{% lucide "ellipsis" %}
|
|
{% endset %}
|
|
{{ dropdown_menu(
|
|
main_attrs={"class": "absolute right-1.5 top-1/2 -translate-y-1/2 z-10"},
|
|
trigger=trigger,
|
|
trigger_attrs={"class": "btn-sm-icon-ghost text-muted-foreground hover:text-accent-foreground size-6"},
|
|
popover_attrs={"data-align": "end", "class": "min-w-32"},
|
|
items=[
|
|
{ type: "item", label: "Settings" },
|
|
{ type: "item", label: "Copy path" },
|
|
{ type: "item", label: "Open location" }
|
|
]
|
|
) }}
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<input type="text" class="input pr-30" placeholder="Enter search query">
|
|
{% set trigger %}
|
|
Search in...
|
|
{% lucide "chevron-down", { "class": "size-3" } %}
|
|
{% endset %}
|
|
{{ dropdown_menu(
|
|
main_attrs={"class": "absolute right-1.5 top-1/2 -translate-y-1/2"},
|
|
trigger=trigger,
|
|
trigger_attrs={"class": "btn-sm-ghost text-muted-foreground hover:text-accent-foreground p-2 h-6"},
|
|
popover_attrs={"data-align": "end", "class": "min-w-32"},
|
|
items=[
|
|
{ type: "item", label: "Documentation" },
|
|
{ type: "item", label: "Blog Posts" },
|
|
{ type: "item", label: "Changelog" }
|
|
]
|
|
) }}
|
|
</div>
|
|
</div>
|
|
{% endset %}
|
|
|
|
{{ code_preview("input-group-dropdown", code_dropdown, class="w-full max-w-sm") }}
|
|
|
|
<h3 id="example-group"><a href="#example-group">Group</a></h3>
|
|
|
|
{% set code_group %}
|
|
<div class="flex w-fit items-stretch">
|
|
<label for="url" class="text-sm flex items-center bg-muted border border-r-0 px-4 rounded-l-md shadow-xs">https://</label>
|
|
<div class="relative">
|
|
<input type="text" class="input pr-9 rounded-none">
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4">{% lucide "link-2" %}</div>
|
|
</div>
|
|
<div class="text-sm flex items-center bg-muted border border-l-0 px-4 rounded-r-md shadow-xs">.com</div>
|
|
</div>
|
|
{% endset %}
|
|
|
|
{{ code_preview("input-group-group", code_group, class="w-full max-w-sm") }} |