You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
413 lines
10 KiB
Vue
413 lines
10 KiB
Vue
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
|
|
import { CAppBar, CButton, CMenu, CSeparator } from '@/index'
|
|
import type { CMenuEntry, CMenuSelectEvent } from '@/index'
|
|
import CCodeBlock from '@/documentation/CCodeBlock.vue'
|
|
|
|
const lastAction = ref('No command selected')
|
|
|
|
const appBarUsage = `<CAppBar>
|
|
<template #start>
|
|
<CMenu :items="mainMenu" orientation="horizontal" />
|
|
</template>
|
|
|
|
<template #center>Order Management</template>
|
|
|
|
<template #end>
|
|
<input type="search" placeholder="Search" />
|
|
<CButton variant="primary">Create order</CButton>
|
|
</template>
|
|
</CAppBar>`
|
|
|
|
const appBarJavaScript = `const mainMenu = [
|
|
{
|
|
label: 'File',
|
|
icon: '▤',
|
|
children: [
|
|
{ label: 'New', icon: '+', command: () => createDocument() },
|
|
{ label: 'Open…', icon: '📂', command: () => openDocument() },
|
|
{ type: 'separator' },
|
|
{ label: 'Save', icon: '💾', command: () => saveDocument() },
|
|
],
|
|
},
|
|
{
|
|
label: 'Edit',
|
|
icon: '✎',
|
|
children: [
|
|
{ label: 'Undo', icon: '↶', command: () => undo() },
|
|
{ label: 'Redo', icon: '↷', command: () => redo(), disabled: true },
|
|
],
|
|
},
|
|
{ type: 'separator' },
|
|
{ label: 'Help', icon: '?', url: '#help' },
|
|
]`
|
|
|
|
const itemStructureJavaScript = `const fileMenu = [
|
|
{
|
|
label: 'File',
|
|
icon: '▤',
|
|
children: [
|
|
{ label: 'Open…', icon: '📂', command: () => openDocument() },
|
|
{ type: 'separator' },
|
|
{ label: 'Save', icon: '💾', command: () => saveDocument() },
|
|
],
|
|
},
|
|
]`
|
|
|
|
const disabledAndHiddenJavaScript = `import { computed } from 'vue'
|
|
|
|
const editMenu = computed(() => [
|
|
{
|
|
label: 'Redo',
|
|
icon: '↷',
|
|
disabled: !canRedo.value,
|
|
command: () => redo(),
|
|
},
|
|
{
|
|
label: 'Administration',
|
|
icon: '⚙︎',
|
|
hidden: !currentUser.value.isAdministrator,
|
|
url: '/administration',
|
|
},
|
|
])`
|
|
|
|
const linkStateJavaScript = `import { computed } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
|
|
const route = useRoute()
|
|
|
|
const navigation = computed(() => [
|
|
{
|
|
label: 'Dashboard',
|
|
icon: '⌂',
|
|
url: '/dashboard',
|
|
active: route.path === '/dashboard',
|
|
},
|
|
{
|
|
label: 'Documentation',
|
|
icon: '?',
|
|
url: 'https://example.com/docs',
|
|
target: '_blank',
|
|
},
|
|
])`
|
|
|
|
const topMenu: CMenuEntry[] = [
|
|
{
|
|
label: 'File',
|
|
icon: '▤',
|
|
children: [
|
|
{ label: 'New', icon: '+' },
|
|
{ label: 'Open…', icon: '📂' },
|
|
{ type: 'separator' },
|
|
{ label: 'Save', icon: '💾' },
|
|
{ label: 'Save As…', icon: '💾' },
|
|
],
|
|
},
|
|
{
|
|
label: 'Edit',
|
|
icon: '✎',
|
|
children: [
|
|
{ label: 'Undo', icon: '↶' },
|
|
{ label: 'Redo', icon: '↷', disabled: true },
|
|
{ type: 'separator' },
|
|
{ label: 'Preferences', icon: '⚙︎' },
|
|
],
|
|
},
|
|
{ type: 'separator' },
|
|
{ label: 'Help', icon: '?', url: '#help' },
|
|
]
|
|
|
|
function recordSelection(event: CMenuSelectEvent) {
|
|
lastAction.value = event.item.label
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<article id="help" class="component-page">
|
|
<header class="page-header">
|
|
<div>
|
|
<p class="category">Navigation</p>
|
|
<h1>AppBar & Menu</h1>
|
|
</div>
|
|
<p>
|
|
Compose application-level regions with <code>CAppBar</code>, then place data-driven
|
|
horizontal or vertical menus where the workflow needs them.
|
|
</p>
|
|
</header>
|
|
|
|
<CSeparator />
|
|
|
|
<section class="example">
|
|
<div class="description">
|
|
<h2>Application bar</h2>
|
|
<p>A horizontal menu can share the bar with centered context and controls at the end.</p>
|
|
</div>
|
|
|
|
<div class="preview">
|
|
<CAppBar aria-label="Example application bar">
|
|
<template #start>
|
|
<strong class="brand">Operations</strong>
|
|
<CMenu
|
|
:items="topMenu"
|
|
orientation="horizontal"
|
|
aria-label="Example application menu"
|
|
@select="recordSelection"
|
|
/>
|
|
</template>
|
|
|
|
<template #center>
|
|
<span class="context">Order Management</span>
|
|
</template>
|
|
|
|
<template #end>
|
|
<input class="input" type="search" aria-label="Search orders" placeholder="Search" />
|
|
<CButton variant="primary">Create order</CButton>
|
|
</template>
|
|
</CAppBar>
|
|
<div class="status" aria-live="polite">Last action: {{ lastAction }}</div>
|
|
</div>
|
|
<CCodeBlock class="code-sample" :code="appBarUsage" />
|
|
<CCodeBlock class="code-sample" :code="appBarJavaScript" language="javascript" />
|
|
</section>
|
|
|
|
<section class="example">
|
|
<div class="description">
|
|
<h2>Item content and hierarchy</h2>
|
|
<p>
|
|
Each regular menu entry has a label and can include a Unicode icon or a nested menu.
|
|
</p>
|
|
</div>
|
|
|
|
<dl class="property-list">
|
|
<div>
|
|
<dt><code>label</code></dt>
|
|
<dd>The required text displayed for the menu item.</dd>
|
|
</div>
|
|
<div>
|
|
<dt><code>icon</code></dt>
|
|
<dd>
|
|
An optional Unicode symbol or emoji rendered with <code>CIcon</code> before the label.
|
|
</dd>
|
|
</div>
|
|
<div>
|
|
<dt><code>children</code></dt>
|
|
<dd>
|
|
An optional array of menu entries. Providing children turns the item into a submenu
|
|
trigger. Children can contain regular items, deeper submenus, and separators.
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
|
|
<CCodeBlock class="code-sample" :code="itemStructureJavaScript" language="javascript" />
|
|
</section>
|
|
|
|
<section class="example">
|
|
<div class="description">
|
|
<h2>Disabled and hidden items</h2>
|
|
<p>
|
|
Set <code>disabled</code> when an action is currently unavailable. The item remains
|
|
visible but cannot be activated, is skipped by menu keyboard navigation, and its command
|
|
will not run. Use <code>hidden</code> when an item should not be shown at all.
|
|
</p>
|
|
</div>
|
|
|
|
<dl class="property-list">
|
|
<div>
|
|
<dt><code>disabled</code></dt>
|
|
<dd>
|
|
Applies disabled styling and <code>aria-disabled="true"</code>. It is commonly derived
|
|
from application state, permissions, or whether an operation can currently run.
|
|
</dd>
|
|
</div>
|
|
<div>
|
|
<dt><code>hidden</code></dt>
|
|
<dd>
|
|
Removes the item from rendering and keyboard navigation. It is commonly derived from
|
|
permissions, feature availability, or application context, and also works on
|
|
separators.
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
|
|
<CCodeBlock
|
|
class="code-sample"
|
|
:code="disabledAndHiddenJavaScript"
|
|
language="javascript"
|
|
/>
|
|
</section>
|
|
|
|
<section class="example">
|
|
<div class="description">
|
|
<h2>Link state and targets</h2>
|
|
<p>
|
|
Menu entries with a <code>url</code> render as links. Use <code>target</code> to choose
|
|
where a link opens and <code>active</code> to identify the current page.
|
|
</p>
|
|
</div>
|
|
|
|
<dl class="property-list">
|
|
<div>
|
|
<dt><code>url</code></dt>
|
|
<dd>
|
|
Becomes the link's <code>href</code>. Without it, the item renders as a button for a
|
|
command or submenu.
|
|
</dd>
|
|
</div>
|
|
<div>
|
|
<dt><code>target</code></dt>
|
|
<dd>
|
|
Becomes the link's HTML <code>target</code>. Omit it for the current tab
|
|
(<code>_self</code>), or use <code>_blank</code> for a new tab. It has no effect without
|
|
<code>url</code>.
|
|
</dd>
|
|
</div>
|
|
<div>
|
|
<dt><code>active</code></dt>
|
|
<dd>
|
|
Applies current-page styling and <code>aria-current="page"</code>. Set it from the
|
|
router or application state; <code>CMenu</code> does not match routes automatically.
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
|
|
<CCodeBlock class="code-sample" :code="linkStateJavaScript" language="javascript" />
|
|
</section>
|
|
|
|
</article>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.component-page {
|
|
width: min(100%, 1100px);
|
|
margin: 0 auto;
|
|
|
|
.page-header {
|
|
display: grid;
|
|
grid-template-columns: minmax(220px, 0.7fr) minmax(320px, 1.3fr);
|
|
align-items: end;
|
|
padding-bottom: 20px;
|
|
gap: 32px;
|
|
|
|
> p {
|
|
margin-bottom: 0;
|
|
color: var(--c-muted-text-color, #626a75);
|
|
line-height: 1.5;
|
|
}
|
|
}
|
|
|
|
.category {
|
|
margin: 0 0 4px;
|
|
color: var(--c-primary-color, #2f6fad);
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
p {
|
|
margin-top: 0;
|
|
}
|
|
|
|
h1 {
|
|
margin-bottom: 0;
|
|
font-size: 25px;
|
|
}
|
|
|
|
h2 {
|
|
margin-bottom: 5px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.example {
|
|
padding: 22px 0;
|
|
|
|
.description {
|
|
margin-bottom: 10px;
|
|
|
|
p {
|
|
margin-bottom: 0;
|
|
color: var(--c-muted-text-color, #626a75);
|
|
line-height: 1.5;
|
|
}
|
|
}
|
|
|
|
.preview {
|
|
overflow: visible;
|
|
background: var(--c-surface-color, #fff);
|
|
border: 1px solid var(--c-border-color, #d5d9df);
|
|
|
|
}
|
|
|
|
.status {
|
|
padding: 6px 9px;
|
|
color: var(--c-muted-text-color, #626a75);
|
|
font-size: 12px;
|
|
background: var(--c-subtle-surface-color, #f7f8fa);
|
|
border-top: 1px solid var(--c-border-color, #d5d9df);
|
|
}
|
|
|
|
.code-sample {
|
|
margin-top: 8px;
|
|
}
|
|
}
|
|
|
|
.property-list {
|
|
margin: 0;
|
|
border: 1px solid var(--c-border-color, #d5d9df);
|
|
|
|
> div {
|
|
display: grid;
|
|
grid-template-columns: 100px minmax(0, 1fr);
|
|
|
|
+ div {
|
|
border-top: 1px solid var(--c-border-color, #d5d9df);
|
|
}
|
|
}
|
|
|
|
dt,
|
|
dd {
|
|
margin: 0;
|
|
padding: 8px 10px;
|
|
}
|
|
|
|
dt {
|
|
font-weight: 600;
|
|
background: var(--c-subtle-surface-color, #f7f8fa);
|
|
border-inline-end: 1px solid var(--c-border-color, #d5d9df);
|
|
}
|
|
|
|
dd {
|
|
line-height: 1.45;
|
|
}
|
|
}
|
|
|
|
.brand {
|
|
padding-inline-end: 4px;
|
|
}
|
|
|
|
.context {
|
|
color: var(--c-muted-text-color, #626a75);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.input {
|
|
box-sizing: border-box;
|
|
width: 150px;
|
|
height: 30px;
|
|
padding: 4px 7px;
|
|
color: inherit;
|
|
font: inherit;
|
|
background: #fff;
|
|
border: 1px solid var(--c-border-color, #bfc5ce);
|
|
border-radius: var(--c-border-radius, 3px);
|
|
|
|
&:focus {
|
|
border-color: var(--c-focus-color, #3578c6);
|
|
outline: 1px solid var(--c-focus-color, #3578c6);
|
|
}
|
|
}
|
|
}
|
|
</style>
|