mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-12 13:18:22 +00:00
289 lines
12 KiB
Plaintext
289 lines
12 KiB
Plaintext
---
|
|
layout: layouts/page.njk
|
|
title: Command
|
|
description: Fast, composable command menu for quick navigation and actions.
|
|
toc:
|
|
- label: Usage
|
|
id: usage
|
|
children:
|
|
- label: HTML + JavaScript
|
|
id: usage-html-js
|
|
children:
|
|
- label: "Step 1: Include the JavaScript files"
|
|
id: usage-html-js-1
|
|
- label: "Step 2: Add your command HTML"
|
|
id: usage-html-js-2
|
|
- label: HTML structure
|
|
id: usage-html-js-3
|
|
- label: JavaScript events
|
|
id: usage-html-js-4
|
|
- label: Jinja and Nunjucks
|
|
id: usage-macro
|
|
- label: Examples
|
|
id: examples
|
|
children:
|
|
- label: Dialog
|
|
id: example-dialog
|
|
---
|
|
|
|
{% from "macros/code_preview.njk" import code_preview %}
|
|
{% from "macros/code_block.njk" import code_block %}
|
|
{% from "command.njk" import command, command_dialog %}
|
|
|
|
{% macro command_items(suggestions_id="suggestions", settings_id="settings") %}
|
|
<div role="group" aria-labelledby="{{ suggestions_id }}">
|
|
<span role="heading" id="{{ suggestions_id }}">Suggestions</span>
|
|
<div role="menuitem" onclick="console.log('Open calendar')">
|
|
{% lucide "calendar" %}
|
|
<span>Calendar</span>
|
|
</div>
|
|
<div role="menuitem" onclick="console.log('Search emoji')">
|
|
{% lucide "smile" %}
|
|
<span>Search Emoji</span>
|
|
</div>
|
|
<div role="menuitem" aria-disabled="true">
|
|
{% lucide "calculator" %}
|
|
<span>Calculator</span>
|
|
</div>
|
|
</div>
|
|
<hr role="separator" />
|
|
<div role="group" aria-labelledby="{{ settings_id }}">
|
|
<span role="heading" id="{{ settings_id }}">Settings</span>
|
|
<div role="menuitem" onclick="console.log('Open profile')" data-filter="Profile">
|
|
{% lucide "user" %}
|
|
<span>Profile</span>
|
|
<kbd class="ml-auto text-muted-foreground bg-transparent tracking-widest">⌘P</kbd>
|
|
</div>
|
|
<div role="menuitem" onclick="console.log('Open billing')" data-filter="Billing">
|
|
{% lucide "credit-card" %}
|
|
<span>Billing</span>
|
|
<kbd class="ml-auto text-muted-foreground bg-transparent tracking-widest">⌘B</kbd>
|
|
</div>
|
|
<div role="menuitem" onclick="console.log('Open settings')" data-filter="Settings">
|
|
{% lucide "settings" %}
|
|
<span>Settings</span>
|
|
<kbd class="ml-auto text-muted-foreground bg-transparent tracking-widest">⌘S</kbd>
|
|
</div>
|
|
</div>
|
|
{% endmacro %}
|
|
|
|
{% set code_standalone %}
|
|
{% call command(
|
|
id="demo-command-standalone",
|
|
main_attrs={ "class": "rounded-lg border shadow-md" }
|
|
) %}
|
|
{{ command_items("suggestions", "settings") }}
|
|
{% endcall %}
|
|
{% endset %}
|
|
|
|
{{ code_preview("command-standalone", code_standalone | prettyHtml, class=" w-full max-w-[450px]") }}
|
|
|
|
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
|
|
|
<h3 id="usage-html-js"><a href="#usage-html-js">HTML + JavaScript</a></h3>
|
|
|
|
<h4 id="usage-html-js-1"><a href="#usage-html-js-1">Step 1: Include the JavaScript files</a></h4>
|
|
|
|
<section class="prose">
|
|
<p>You can either <a href="/installation/#install-cdn-all">include the JavaScript file for all the components</a>, or just the one for this component by adding this to the <code><head></code> of your page:</p>
|
|
</section>
|
|
|
|
{% set code_script %}<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/basecoat.min.js" defer></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/command.min.js" defer></script>{% endset %}
|
|
{{ code_block(code_script | prettyHtml, "html") }}
|
|
|
|
<div class="flex flex-wrap gap-2 my-6">
|
|
<a class="badge-outline" href="/installation/#install-js">
|
|
Components with JavaScript
|
|
{% lucide "arrow-right" %}
|
|
</a>
|
|
<a class="badge-outline" href="/installation/#install-cli">
|
|
Use the CLI
|
|
{% lucide "arrow-right" %}
|
|
</a>
|
|
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/js/command.js" target="_blank">
|
|
command.js
|
|
{% lucide "arrow-right" %}
|
|
</a>
|
|
</div>
|
|
|
|
<h4 id="usage-html-js-2"><a href="#usage-html-js-2">Step 2: Add your command HTML</a></h4>
|
|
|
|
<section class="prose">
|
|
<p>You can use the command menu standalone:</p>
|
|
</section>
|
|
|
|
{{ code_block(code_standalone | prettyHtml, "html") }}
|
|
|
|
<section class="prose">
|
|
<p>Wrap the command menu in a <code><dialog class="command-dialog"></code> to display in a dialog:</p>
|
|
</section>
|
|
|
|
{% set code_dialog %}
|
|
<dialog class="command-dialog" aria-label="Command menu">
|
|
<div class="command">
|
|
<!-- Regular command content: header, menu, etc. -->
|
|
</div>
|
|
</dialog>
|
|
{% endset %}
|
|
|
|
{{ code_block(code_dialog | prettyHtml, "html") }}
|
|
|
|
<h4 id="usage-html-js-3"><a href="#usage-html-js-3">HTML structure</a></h4>
|
|
|
|
<section class="prose">
|
|
<dl>
|
|
<dt><code class="highlight language-html"><dialog class="command-dialog"></code> <span class="badge-secondary">Optional</span></dt>
|
|
<dd>For dialog variant, wraps a <code><div class="command"></code> to display as a modal. Uses native <code><dialog></code> behavior and requires <code>aria-label</code> or <code>aria-labelledby</code> for accessibility.
|
|
<dl>
|
|
<dt><code class="highlight language-html"><div class="command"></code></dt>
|
|
<dd>Wraps around the entire command component. Can be used standalone or inside a dialog.
|
|
<dl>
|
|
<dt><code class="highlight language-html"><header></code></dt>
|
|
<dd>Contains the search input to filter command options.
|
|
<dl>
|
|
<dt><code class="highlight language-html"><input type="text"></code></dt>
|
|
<dd>The search input. Should have:
|
|
<ul>
|
|
<li><code>role="combobox"</code>: indicates the input controls a menu with filtering</li>
|
|
<li><code>aria-expanded="true"</code>: the menu is always expanded</li>
|
|
<li><code>aria-controls="{ MENU_ID }"</code>: points to the menu's id</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</dd>
|
|
<dt><code class="highlight language-html"><div role="menu"></code></dt>
|
|
<dd>The menu containing the command items. Should have <code>id="{ MENU_ID }"</code> refered to by the input's <code>aria-controls</code> attribute.
|
|
<dl>
|
|
<dt><code class="highlight language-html"><div role="menuitem"></code> or <code class="highlight language-html"><a role="menuitem"></code></dt>
|
|
<dd>Command item that triggers an action when clicked. Use <code><a></code> for navigation links, or <code><div></code> for actions. In a command-dialog, clicking a menuitem automatically closes the dialog unless the <code>data-keep-command-open</code> attribute is present. Optional attributes:
|
|
<ul>
|
|
<li><code>href</code>: If present on an <code><a></code>, the item navigates to that URL</li>
|
|
<li><code>data-filter</code>: custom text used for filtering (if different from displayed content, e.g., <code>data-filter="calendar event"</code>)</li>
|
|
<li><code>data-keywords</code>: additional keywords for enhanced filtering (e.g., <code>data-keywords="calendar event date"</code>)</li>
|
|
<li><code>data-force</code>: forces the item to always display, regardless of filter (e.g., <code>data-force</code>)</li>
|
|
<li><code>aria-disabled="true"</code>: marks item as disabled (excluded from keyboard navigation and filtering)</li>
|
|
</ul>
|
|
</dd>
|
|
<dt><code class="highlight language-html"><hr role="separator"></code> <span class="badge-secondary">Optional</span></dt>
|
|
<dd>Separator between groups/items. Hidden when filtering is active.</dd>
|
|
<dt><code class="highlight language-html"><div role="group"></code> <span class="badge-secondary">Optional</span></dt>
|
|
<dd>Group of items, can have <code>aria-labelledby</code> attribute to link to a heading.</dd>
|
|
<dt><code class="highlight language-html"><span role="heading"></code></dt>
|
|
<dd>Group heading, must have an <code>id</code> attribute if you use the <code>aria-labelledby</code> attribute on the group.</dd>
|
|
</dl>
|
|
</dd>
|
|
</dl>
|
|
</dd>
|
|
</dl>
|
|
</dd>
|
|
</dl>
|
|
</section>
|
|
|
|
<h4 id="usage-html-js-4"><a href="#usage-html-js-4">JavaScript events</a></h4>
|
|
|
|
<section class="prose">
|
|
<dl>
|
|
<dt><code>basecoat:initialized</code></dt>
|
|
<dd>Once the component is fully initialized, it dispatches a custom (non-bubbling) <code>basecoat:initialized</code> event on itself.</dd>
|
|
</dl>
|
|
</section>
|
|
|
|
<h3 id="usage-macro"><a href="#usage-macro">Jinja and Nunjucks</a></h3>
|
|
|
|
<div class="prose">
|
|
<p>You can use the <code class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs">command()</code> or <code class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs">command_dialog()</code> Nunjucks or Jinja macros for this component.</p>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap gap-2 my-6">
|
|
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
|
Use Nunjucks or Jinja macros
|
|
{% lucide "arrow-right" %}
|
|
</a>
|
|
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/jinja/command.html.jinja" target="_blank">
|
|
Jinja macro
|
|
{% lucide "arrow-right" %}
|
|
</a>
|
|
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/command.njk" target="_blank">
|
|
Nunjucks macro
|
|
{% lucide "arrow-right" %}
|
|
</a>
|
|
</div>
|
|
|
|
{% set raw_code %}{% raw %}{% call command() %}
|
|
<div role="menuitem">
|
|
<svg>...</svg>
|
|
<span>Calendar</span>
|
|
</div>
|
|
<div role="menuitem" data-keywords="config preferences">
|
|
<svg>...</svg>
|
|
<span>Settings</span>
|
|
</div>
|
|
{% endcall %}{% endraw %}{% endset %}
|
|
{{ code_block(raw_code, "jinja") }}
|
|
|
|
<section class="prose">
|
|
<p>You can also use the <code>items</code> parameter to pass an array of items:</p>
|
|
</section>
|
|
|
|
{% set icon_calendar %}{% lucide "calendar" %}{% endset %}
|
|
{% set icon_settings %}{% lucide "settings" %}{% endset %}
|
|
{% set raw_code_items %}{% raw %}{% set icon_calendar %}{% lucide "calendar" %}{% endset %}
|
|
{% set icon_settings %}{% lucide "settings" %}{% endset %}
|
|
{% set items = [
|
|
{ type: "group", label: "Suggestions", items: [
|
|
{ label: "Calendar", icon: icon_calendar, keywords: "date event" },
|
|
{ label: "Search Emoji", icon: icon_settings },
|
|
{ label: "Calculator", disabled: true }
|
|
]},
|
|
{ type: "separator" },
|
|
{ type: "group", label: "Settings", items: [
|
|
{ label: "Profile", url: "/profile", icon: icon_settings, keywords: "user account" },
|
|
{ label: "Billing", url: "/billing", icon: icon_settings },
|
|
{ label: "Settings", url: "/settings", icon: icon_settings }
|
|
]}
|
|
] %}
|
|
|
|
{% call command(items=items) %}{% endcall %}{% endraw %}{% endset %}
|
|
{{ code_block(raw_code_items, "jinja") }}
|
|
|
|
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
|
|
|
<h3 id="example-dialog"><a href="#example-dialog">Dialog</a></h3>
|
|
|
|
<section class="prose">
|
|
<p>You can use some simple JavaScript to open the command menu with a keyboard shortcut (in this case <kbd class="kbd">⌘J</kbd>).</p>
|
|
</section>
|
|
|
|
{% set code_dialog %}
|
|
<button
|
|
type="button"
|
|
class="btn-outline"
|
|
onclick="document.getElementById('demo-command-dialog').showModal()"
|
|
>
|
|
Open command menu
|
|
<kbd class="kbd">⌘J</kbd>
|
|
</button>
|
|
|
|
{% call command_dialog(id="demo-command-dialog") %}
|
|
{{ command_items("cmd-suggestions", "cmd-settings") }}
|
|
{% endcall %}
|
|
|
|
<script>
|
|
document.addEventListener('keydown', (e) => {
|
|
if ((e.metaKey || e.ctrlKey) && e.key === 'j') {
|
|
e.preventDefault();
|
|
const dialog = document.getElementById('demo-command-dialog');
|
|
if (dialog.open) {
|
|
dialog.close();
|
|
} else {
|
|
dialog.showModal();
|
|
dialog.querySelector('header input')?.focus();
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
{% endset %}
|
|
|
|
{{ code_preview("command-dialog", code_dialog | prettyHtml) }}
|