# Grimoire Component Usage Guide

Quick reference for using Grimoire components with common patterns and examples.

> 💡 **Tip**: All components are headless (unstyled). Create StyledComponents in your theme to apply your project's design system. See [`styling-guide.md`](./styling-guide.md).

---

## 📦 Table of Contents

- [GButton](#gbutton)
- [GInput](#ginput)
- [GDropdown](#gdropdown)
- [GCheckbox](#gcheckbox)
- [GRadio](#gradio)
- [GSwitch](#gswitch)
- [GModal](#gmodal)
- [GTable](#gtable)
- [GForm](#gform)
- [GIcon](#gicon)
- [GDatepicker](#gdatepicker)
- [Other Components](#other-components)

---

## GButton

Versatile button component with icon support and router integration.

### Basic Usage

```vue
<template>
  <GButton>Click Me</GButton>
  <GButton variant="primary filled">Primary</GButton>
  <GButton variant="outline danger">Delete</GButton>
  <GButton rounded>Rounded</GButton>
  <GButton disabled>Disabled</GButton>
</template>

<script setup>
import { GButton } from '@twentyfourg/grimoire';
</script>
```

### With Icons

```vue
<template>
  <!-- Icon only -->
  <GButton icon="app:plus" />

  <!-- Icon + Text -->
  <GButton icon-left="app:save">Save</GButton>
  <GButton icon-right="app:arrow">Next</GButton>

  <!-- Both sides -->
  <GButton icon-left="app:edit" icon-right="app:check"> Edit Item </GButton>
</template>
```

### Router Links

```vue
<template>
  <!-- Vue Router link -->
  <GButton :to="{ name: 'home' }">Home</GButton>
  <GButton to="/about">About</GButton>

  <!-- Regular anchor -->
  <GButton href="https://example.com">External</GButton>
</template>
```

### Accessing Methods

```vue
<template>
  <GButton ref="buttonRef">Focus Me</GButton>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const buttonRef = ref(null);

onMounted(() => {
  buttonRef.value?.focus();
});
</script>
```

### Props

| Prop        | Type                    | Default             | Description                  |
| ----------- | ----------------------- | ------------------- | ---------------------------- |
| `variant`   | `string`                | `'default primary'` | Space-separated type + color |
| `rounded`   | `boolean`               | `false`             | Apply rounded styling        |
| `label`     | `string`                | -                   | Button text (or use slot)    |
| `disabled`  | `boolean`               | `false`             | Disable button               |
| `to`        | `string \| object`      | -                   | Vue Router destination       |
| `href`      | `string`                | -                   | Anchor link destination      |
| `icon`      | `string \| IconifyIcon` | -                   | Icon (centered if no text)   |
| `iconLeft`  | `string \| IconifyIcon` | -                   | Left icon                    |
| `iconRight` | `string \| IconifyIcon` | -                   | Right icon                   |
| `autoFocus` | `boolean`               | `false`             | Auto-focus on mount          |

See source: [`src/components/GButton/GButton.vue`](../../src/components/GButton/GButton.vue)

---

## GInput

Text input, textarea, and number input with masking support.

### Basic Usage

```vue
<template>
  <GInput v-model="text" placeholder="Enter text" />
  <GInput v-model="email" type="email" label="Email" />
  <GInput v-model="password" type="password" password-reveal />
  <GInput v-model="notes" type="textarea" rows="4" />
</template>

<script setup>
import { ref } from 'vue';
import { GInput } from '@twentyfourg/grimoire';

const text = ref('');
const email = ref('');
const password = ref('');
const notes = ref('');
</script>
```

### With Icons

```vue
<template>
  <GInput v-model="search" icon-left="app:search" placeholder="Search..." />
  <GInput v-model="phone" icon-right="app:phone" placeholder="Phone" />
</template>
```

### Clearable Input

```vue
<template>
  <GInput v-model="query" clearable placeholder="Type to search..." />
</template>

<script setup>
import { ref } from 'vue';

const query = ref('');
</script>
```

### Input Masking

```vue
<template>
  <!-- Phone number -->
  <GInput v-model="phone" mask="(000) 000-0000" />

  <!-- Credit card -->
  <GInput v-model="card" mask="0000 0000 0000 0000" />

  <!-- Date -->
  <GInput v-model="date" mask="00/00/0000" />

  <!-- Custom mask with options -->
  <GInput v-model="custom" mask="AA-0000" :mask-options="{ definitions: { A: /[A-Z]/, '0': /[0-9]/ } }" />
</template>
```

### Accessing Methods

```vue
<template>
  <GInput ref="inputRef" v-model="value" />
  <button @click="focusInput">Focus</button>
</template>

<script setup>
import { ref } from 'vue';

const inputRef = ref(null);
const value = ref('');

function focusInput() {
  inputRef.value?.focus();
}
</script>
```

### Props

| Prop             | Type                                | Default    | Description                |
| ---------------- | ----------------------------------- | ---------- | -------------------------- |
| `modelValue`     | `string \| number`                  | -          | v-model binding            |
| `type`           | `string`                            | `'text'`   | Input type or `'textarea'` |
| `variant`        | `'filled' \| 'outlined' \| 'lined'` | `'filled'` | Visual style               |
| `label`          | `string`                            | -          | Label text                 |
| `error`          | `boolean`                           | `false`    | Error state                |
| `clearable`      | `boolean`                           | `false`    | Show clear button          |
| `passwordReveal` | `boolean`                           | `false`    | Enable password reveal     |
| `autoResize`     | `boolean`                           | `false`    | Auto-resize textarea       |
| `autoFocus`      | `boolean`                           | `false`    | Auto-focus on mount        |
| `iconLeft`       | `string`                            | -          | Left icon                  |
| `iconRight`      | `string`                            | -          | Right icon                 |
| `mask`           | `string`                            | -          | Input mask pattern         |
| `maskOptions`    | `object`                            | `{}`       | IMask options              |

### Events

| Event               | Payload  | Description             |
| ------------------- | -------- | ----------------------- |
| `update:modelValue` | `string` | Emitted on input change |
| `cleared`           | -        | Emitted when cleared    |

See source: [`src/components/GInput/GInput.vue`](../../src/components/GInput/GInput.vue)

---

## GDropdown

Select/multiselect dropdown powered by @vueform/multiselect.

### Basic Usage

```vue
<template>
  <!-- Single select -->
  <GDropdown v-model="selected" :options="options" />

  <!-- Multi-select -->
  <GDropdown v-model="selected" :options="options" mode="multiple" />

  <!-- Tags mode -->
  <GDropdown v-model="tags" :options="options" mode="tags" />

  <!-- Object options -->
  <GDropdown v-model="selected" :options="users" label="name" value-prop="id" />
</template>

<script setup>
import { ref } from 'vue';
import { GDropdown } from '@twentyfourg/grimoire';

const selected = ref(null);
const tags = ref([]);
const options = ref(['Option 1', 'Option 2', 'Option 3']);

const users = ref([
  { id: 1, name: 'John Doe' },
  { id: 2, name: 'Jane Smith' },
]);
</script>
```

### Searchable Dropdown

```vue
<template>
  <GDropdown v-model="selected" :options="countries" searchable placeholder="Search countries..." />
</template>
```

### Accessing Methods

```vue
<template>
  <GDropdown ref="dropdownRef" v-model="selected" :options="options" />
  <button @click="openDropdown">Open</button>
  <button @click="clearDropdown">Clear</button>
</template>

<script setup>
import { ref } from 'vue';

const dropdownRef = ref(null);
const selected = ref(null);

function openDropdown() {
  dropdownRef.value?.open();
}

function clearDropdown() {
  dropdownRef.value?.clear();
}
</script>
```

### Props

| Prop             | Type      | Default    | Description                            |
| ---------------- | --------- | ---------- | -------------------------------------- |
| `modelValue`     | `any`     | -          | v-model binding                        |
| `options`        | `array`   | `[]`       | Options list                           |
| `mode`           | `string`  | `'single'` | `'single'` \| `'multiple'` \| `'tags'` |
| `searchable`     | `boolean` | `false`    | Enable search                          |
| `canClear`       | `boolean` | `true`     | Show clear button                      |
| `canDeselect`    | `boolean` | `true`     | Allow deselecting                      |
| `closeOnSelect`  | `boolean` | `true`     | Close on selection                     |
| `label`          | `string`  | -          | Object option label key                |
| `valueProp`      | `string`  | -          | Object option value key                |
| `error`          | `boolean` | `false`    | Error state                            |
| `preselectFirst` | `boolean` | `false`    | Auto-select first option               |

**Note**: GDropdown extends all [@vueform/multiselect](https://github.com/vueform/multiselect) props.

### Methods

| Method             | Description        |
| ------------------ | ------------------ |
| `open()`           | Open dropdown      |
| `close()`          | Close dropdown     |
| `toggle()`         | Toggle open/close  |
| `clear()`          | Clear selection    |
| `select(option)`   | Select an option   |
| `deselect(option)` | Deselect an option |

See source: [`src/components/GDropdown/GDropdown.vue`](../../src/components/GDropdown/GDropdown.vue)

---

## GCheckbox

Checkbox with optional label and indeterminate state.

### Basic Usage

```vue
<template>
  <GCheckbox v-model="accepted" label="I accept the terms" />

  <!-- Indeterminate state (for "select all" scenarios) -->
  <GCheckbox v-model="selectAll" :indeterminate="someSelected" label="Select All" />

  <!-- Disabled -->
  <GCheckbox v-model="value" label="Disabled" disabled />
</template>

<script setup>
import { ref } from 'vue';
import { GCheckbox } from '@twentyfourg/grimoire';

const accepted = ref(false);
const selectAll = ref(false);
const someSelected = ref(true);
</script>
```

### Props

| Prop            | Type      | Default | Description         |
| --------------- | --------- | ------- | ------------------- |
| `modelValue`    | `boolean` | `false` | v-model binding     |
| `label`         | `string`  | -       | Checkbox label      |
| `indeterminate` | `boolean` | `false` | Indeterminate state |
| `disabled`      | `boolean` | `false` | Disable checkbox    |

---

## GRadio

Radio button with optional label.

### Basic Usage

```vue
<template>
  <div>
    <GRadio v-model="selected" value="option1" label="Option 1" />
    <GRadio v-model="selected" value="option2" label="Option 2" />
    <GRadio v-model="selected" value="option3" label="Option 3" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { GRadio } from '@twentyfourg/grimoire';

const selected = ref('option1');
</script>
```

### Props

| Prop         | Type      | Default | Description     |
| ------------ | --------- | ------- | --------------- |
| `modelValue` | `any`     | -       | v-model binding |
| `value`      | `any`     | -       | Radio value     |
| `label`      | `string`  | -       | Radio label     |
| `disabled`   | `boolean` | `false` | Disable radio   |

---

## GSwitch

Toggle switch component.

### Basic Usage

```vue
<template>
  <GSwitch v-model="enabled" label="Enable Feature" />
  <GSwitch v-model="darkMode" label="Dark Mode" />
</template>

<script setup>
import { ref } from 'vue';
import { GSwitch } from '@twentyfourg/grimoire';

const enabled = ref(false);
const darkMode = ref(true);
</script>
```

---

## GModal

Modal dialog with backdrop.

### Basic Usage

```vue
<template>
  <GButton @click="showModal = true">Open Modal</GButton>

  <GModal v-model="showModal" title="Confirm Action">
    <p>Are you sure you want to proceed?</p>

    <template #footer>
      <GButton @click="showModal = false">Cancel</GButton>
      <GButton variant="primary filled" @click="handleConfirm">Confirm</GButton>
    </template>
  </GModal>
</template>

<script setup>
import { ref } from 'vue';
import { GModal, GButton } from '@twentyfourg/grimoire';

const showModal = ref(false);

function handleConfirm() {
  // Do something
  showModal.value = false;
}
</script>
```

### Props

| Prop              | Type      | Default | Description             |
| ----------------- | --------- | ------- | ----------------------- |
| `modelValue`      | `boolean` | `false` | v-model for open/close  |
| `title`           | `string`  | -       | Modal title             |
| `closeOnBackdrop` | `boolean` | `true`  | Close on backdrop click |
| `closeOnEsc`      | `boolean` | `true`  | Close on Escape key     |

### Slots

| Slot      | Description                     |
| --------- | ------------------------------- |
| `default` | Modal body content              |
| `header`  | Custom header (overrides title) |
| `footer`  | Modal footer (buttons, etc.)    |

---

## GTable

Data table with sorting and pagination.

### Basic Usage

```vue
<template>
  <GTable :columns="columns" :data="users" />
</template>

<script setup>
import { ref } from 'vue';
import { GTable } from '@twentyfourg/grimoire';

const columns = ref([
  { key: 'name', label: 'Name', sortable: true },
  { key: 'email', label: 'Email' },
  { key: 'role', label: 'Role' },
]);

const users = ref([
  { name: 'John Doe', email: 'john@example.com', role: 'Admin' },
  { name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
]);
</script>
```

### With Pagination

```vue
<template>
  <GTable :columns="columns" :data="users" paginated :per-page="10" />
</template>
```

### Custom Cell Rendering

```vue
<template>
  <GTable :columns="columns" :data="users">
    <template #cell-actions="{ row }">
      <GButton @click="editUser(row)">Edit</GButton>
      <GButton @click="deleteUser(row)">Delete</GButton>
    </template>
  </GTable>
</template>

<script setup>
const columns = ref([
  { key: 'name', label: 'Name' },
  { key: 'actions', label: 'Actions' },
]);

function editUser(user) {
  // Edit logic
}

function deleteUser(user) {
  // Delete logic
}
</script>
```

---

## GForm

Form container with validation support.

### Basic Usage

```vue
<template>
  <GForm @submit="handleSubmit">
    <GInput v-model="form.name" label="Name" required />
    <GInput v-model="form.email" type="email" label="Email" required />
    <GButton type="submit">Submit</GButton>
  </GForm>
</template>

<script setup>
import { ref } from 'vue';
import { GForm, GInput, GButton } from '@twentyfourg/grimoire';

const form = ref({
  name: '',
  email: '',
});

function handleSubmit() {
  console.log('Form submitted:', form.value);
}
</script>
```

---

## GIcon

Icon component with Iconify integration.

### Basic Usage

```vue
<template>
  <!-- Using icon collection:name format -->
  <GIcon icon="app:plus" />
  <GIcon icon="app:close" />

  <!-- Using Iconify object -->
  <GIcon :icon="{ name: 'plus', collection: 'app' }" />

  <!-- With size and color -->
  <GIcon icon="app:heart" :width="24" :height="24" color="red" />
</template>

<script setup>
import { GIcon } from '@twentyfourg/grimoire';
</script>
```

### Registering Icons

See [`iconify-setup.md`](./iconify-setup.md) for complete Iconify setup.

---

## GDatepicker

Date and datetime picker powered by @vuepic/vue-datepicker.

### Basic Usage

```vue
<template>
  <!-- Date only -->
  <GDatepicker v-model="date" />

  <!-- Date and time -->
  <GDatepicker v-model="datetime" enable-time-picker />

  <!-- Date range -->
  <GDatepicker v-model="dateRange" range />
</template>

<script setup>
import { ref } from 'vue';
import { GDatepicker } from '@twentyfourg/grimoire';

const date = ref(null);
const datetime = ref(null);
const dateRange = ref([null, null]);
</script>
```

**Note**: GDatepicker extends all [@vuepic/vue-datepicker](https://vue3datepicker.com/) props.

---

## Other Components

### GAvatar

User avatar with loading/error states.

```vue
<GAvatar :src="userImage" :alt="userName" />
```

### GBreadcrumbs

Breadcrumb navigation.

```vue
<GBreadcrumbs :items="breadcrumbItems" />
```

### GCollapsible

Collapsible/accordion sections.

```vue
<GCollapsible title="Section Title">
  <p>Collapsible content</p>
</GCollapsible>
```

### GDivider

Visual divider.

```vue
<GDivider />
<GDivider vertical />
```

### GProgressBar

Linear progress bar.

```vue
<GProgressBar :progress="0.75" show-percentage />
```

### GProgressCircle

Circular progress indicator.

```vue
<GProgressCircle :progress="0.5" />
```

### GSkeleton

Loading skeleton placeholders.

```vue
<GSkeleton :count="3" />
```

### GTabs

Tab navigation with router support.

```vue
<GTabs :tabs="tabs" v-model="activeTab" />
```

### GThumbnail

File thumbnail previews.

```vue
<GThumbnail :file="fileObject" />
```

---

## 🎨 Creating Styled Components

All Grimoire components are headless (unstyled). To use them in your project, create StyledComponents:

```vue
<!-- theme/StyledButton.vue -->
<template>
  <GButton ref="buttonRef" v-bind="mergedProps" class="styled-button">
    <slot />
  </GButton>
</template>

<script setup>
import { ref } from 'vue';
import { GButton } from '@twentyfourg/grimoire';
import { useStyledComponent } from '@twentyfourg/grimoire/composables';

const props = defineProps({
  ...GButton.props,
  variant: {
    type: String,
    default: 'primary filled', // Your project default
  },
});

const buttonRef = ref(null);

const { mergedProps, expose } = useStyledComponent(buttonRef, props, {
  defaults: {
    rounded: true, // Project-wide default
  },
});

defineExpose(expose);
</script>

<style scoped>
.styled-button {
  /* Your styles */
}
</style>
```

See [`styling-guide.md`](./styling-guide.md) for complete styling guide.

---

## 🔗 Related Documentation

- **[TypeScript Usage](./typescript-usage.md)** - TypeScript patterns
- **[Styling Guide](./styling-guide.md)** - CSS variables and theming
- **[Iconify Setup](./iconify-setup.md)** - Icon integration
- **[Migration Guide](./v2-to-v3-migration-guide.md)** - Upgrading from v2

---

## 💡 Tips

### v-model vs :value

All input components support both `v-model` and `:value` + `@update:value`:

```vue
<!-- Preferred -->
<GInput v-model="text" />

<!-- Also works -->
<GInput :value="text" @update:value="text = $event" />
```

### Accessing Ref Methods

Always use optional chaining when accessing methods:

```vue
<script setup>
const inputRef = ref(null);

// ✅ Good - safe
inputRef.value?.focus();

// ❌ Bad - will error if not mounted
inputRef.value.focus();
</script>
```

### Component Props

All components have `.props` exported for use in StyledComponents:

```javascript
import { GButton } from '@twentyfourg/grimoire';

const props = defineProps({
  ...GButton.props, // Inherit all base props
  customProp: String, // Add custom props
});
```

---

**For detailed prop types and interfaces, see component source files in [`src/components/`](../../src/components/).**



