# Grimoire v3 ![Version](https://img.shields.io/badge/Version-3.0.0-blue)

> A modern, headless Vue 3 component library built with TypeScript and Composition API

Grimoire is a collection of unstyled components that provide structure, accessibility, and logic while letting you control the styling. Perfect for building your own design system or theming components to match any brand.

## ✨ Key Features

- 🎨 **Headless Components** - Unstyled by default, bring your own styles
- ⚡ **Vue 3 Composition API** - Modern, performant, tree-shakeable
- 🔷 **TypeScript First** - Full type safety and IntelliSense support
- 🎭 **Theme System** - Create styled wrappers with `useStyledComponent` composable
- ♿ **Accessible** - Built with accessibility in mind
- 🎯 **Direct Ref Access** - No more `@refMounted` events needed!
- 🧪 **Fully Tested** - Comprehensive unit and E2E test coverage
- 🎨 **Iconify Integration** - Built-in support for 200,000+ icons

## 🌐 Browser Support

Supports all modern browsers (latest versions):

- Chrome/Edge (Chromium)
- Firefox
- Safari
- Opera

**Note:** IE11 is not supported.

---

## 📦 Installation & Setup

### Install the Package

```bash
npm install @twentyfourg/grimoire
# or
pnpm add @twentyfourg/grimoire
# or
yarn add @twentyfourg/grimoire
```

### Basic Setup

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

const value = ref('');
</script>

<template>
  <GButton variant="primary filled">Click Me</GButton>
  <GInput v-model="value" placeholder="Enter text" />
</template>
```

### Setup with Icons (Optional)

Grimoire uses [Iconify](https://iconify.design/) for icons. Register icon collections with the plugin:

```javascript
// main.ts
import { createApp } from 'vue';
import Grimoire from '@twentyfourg/grimoire/plugin';
import { icons } from './icons-bundle'; // Your custom icons (see below)
import App from './App.vue';

const app = createApp(App);

app.use(Grimoire, {
  icons, // Your icon collections
  importBaseIcons: true, // Include Grimoire's base icons (default: true)
});

app.mount('#app');
```

**Build your own icon bundles from SVG files:**

```typescript
// vite.config.ts
import { viteIconBundlePlugin } from '@twentyfourg/grimoire/build-tools';

export default defineConfig({
  plugins: [
    viteIconBundlePlugin({
      sources: [
        {
          type: 'folder',
          path: 'icons', // Your SVG folder
          prefix: 'app', // Icons will be: app:icon-name
          variants: 'both', // Creates colored & monotone versions
        },
      ],
    }),
  ],
});
```

Then use icons in components:

```vue
<template>
  <GIcon icon="app:logo" />
  <GButton icon="app:plus">Add Item</GButton>
  <GInput icon-left="app:search" placeholder="Search..." />
</template>
```

📚 **See:** [Build Tools Documentation](./packages/grimoire/src/build-tools/README.md) for advanced icon options

---

## 📦 Available Components

| Component           | Description                                                   |
| ------------------- | ------------------------------------------------------------- |
| **GAvatar**         | User avatar with loading/error states                         |
| **GBreadcrumbs**    | Breadcrumb navigation                                         |
| **GButton**         | Button with icon support and variants                         |
| **GCheckbox**       | Checkbox with optional label and indeterminate state          |
| **GCollapsible**    | Collapsible/accordion sections                                |
| **GDatepicker**     | Date and datetime picker (powered by @vuepic/vue-datepicker)  |
| **GDivider**        | Visual divider                                                |
| **GDropdown**       | Select/multiselect dropdown (powered by @vueform/multiselect) |
| **GForm**           | Form with validation support                                  |
| **GFormField**      | Form field wrapper with label and error states                |
| **GFormFieldInput** | Complete form field with integrated input                     |
| **GIcon**           | Icon component (Iconify integration)                          |
| **GInput**          | Text input, textarea, number input with masking support       |
| **GModal**          | Modal dialog with backdrop                                    |
| **GPagination**     | Pagination controls                                           |
| **GProgressBar**    | Linear progress bar                                           |
| **GProgressCircle** | Circular progress indicator                                   |
| **GRadio**          | Radio button with optional label                              |
| **GSkeleton**       | Loading skeleton placeholders                                 |
| **GSwitch**         | Toggle switch                                                 |
| **GTable**          | Data table with sorting and pagination                        |
| **GTabs**           | Tab navigation with router support                            |
| **GThumbnail**      | File thumbnail previews                                       |

### Component Documentation

📚 **Documentation site is currently under construction.** Complete API documentation for each component will include:

- Prop references with types
- Event documentation
- Slot descriptions
- HTML structure
- CSS variables
- Usage examples

In the meantime, you can view component source code in [`packages/grimoire/src/components`](./packages/grimoire/src/components) for detailed prop definitions and TypeScript types.

---

## 🎨 Creating Styled Components

Grimoire components are intentionally unstyled. Create themed wrappers for your project using the `useStyledComponent` composable:

```vue
<!-- StyledButton.vue -->
<template>
  <GButton ref="buttonRef" v-bind="mergedProps" class="styled-button">
    <template v-for="(_, name) in $slots" v-slot:[name]="slotData">
      <slot :name="name" v-bind="slotData" />
    </template>
  </GButton>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { GButton, type GButtonExposed } from '@twentyfourg/grimoire';
import { useStyledComponent } from '@twentyfourg/grimoire/composables';

const props = defineProps({
  ...GButton.props,
  // Override base component defaults
  colorVariants: {
    type: Array,
    default: () => ['primary', 'danger'],
  },
  rounded: {
    type: Boolean,
    default: true, // Make rounded default for your project
  },
});

// ✅ Re-export the exposed type for TypeScript consumers
export type StyledButtonExposed = GButtonExposed;

const buttonRef = ref<GButtonExposed>();
const { mergedProps, expose } = useStyledComponent<GButtonExposed>(buttonRef, props);

// ✅ This ensures all GButton methods are available on your styled component
defineExpose(expose);
defineOptions({
  inheritAttrs: false,
});
</script>

<style scoped>
.styled-button {
  /* Your project's button styles */
  background: var(--color-primary);
  color: white;
  padding: 12px 24px;
  border-radius: 8px;
  border: none;
  cursor: pointer;
  transition: background 0.2s;
}

.styled-button:hover {
  background: var(--color-primary-dark);
}

.styled-button.rounded {
  border-radius: 24px;
}
</style>
```

### Benefits of useStyledComponent

- ✅ **Automatic prop merging** - spreads base props, merges overrides
- ✅ **Method forwarding** - all base component methods automatically available
- ✅ **Direct ref access** - no more `@refMounted` events
- ✅ **TypeScript safe** - full type inference
- ✅ **Consistent pattern** across all your styled components

### Advanced: Conditional Props

For conditional logic based on other props, use `computeProps`:

```javascript
const { mergedProps, expose } = useStyledComponent(dropdownRef, props, {
  computeProps(merged, attrs) {
    // Apply smart defaults for multi-select modes
    if (merged.mode === 'tags' || merged.mode === 'multiple') {
      merged.hideSelected = merged.hideSelected ?? false;
      merged.closeOnSelect = merged.closeOnSelect ?? false;
    }
    return merged;
  },
});
```

📚 **See:** [Styled Component Migration Guide](./STYLED_COMPONENT_MIGRATION_GUIDE.md) for complete details

---

## 🎯 Direct Ref Access (v3 Feature!)

One of v3's biggest improvements: access child component methods directly through template refs.

### Old Way (v2)

```vue
<template>
  <StyledDropdown @refMounted="dropdownRef = $event" />
</template>

<script setup>
const dropdownRef = ref(null);

function openDropdown() {
  dropdownRef.value.open(); // Only works after @refMounted fires
}
</script>
```

### New Way (v3)

```vue
<template>
  <StyledDropdown ref="dropdown" />
  <button @click="dropdown.open()">Open</button>
</template>

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

const dropdown = ref(null);

// All base component methods available directly!
function doStuff() {
  dropdown.value.open();
  dropdown.value.close();
  dropdown.value.toggle();
  dropdown.value.$base.value; // Escape hatch to child ref
}
</script>
```

**No more:**

- ❌ `@refMounted` event listeners
- ❌ Waiting for mounted lifecycle
- ❌ Event handler boilerplate

**Just:**

- ✅ Direct ref access like native Vue
- ✅ All base component methods available
- ✅ TypeScript autocomplete support

### Exposed Types for Full TypeScript Support

Every Grimoire component exposes its methods and properties through an `*Exposed` type. This gives you complete TypeScript autocomplete and type safety:

```typescript
import { ref } from 'vue';
import { type GDropdownExposed, type GInputExposed } from '@twentyfourg/grimoire';

const dropdown = ref<GDropdownExposed>();
const input = ref<GInputExposed>();

// ✅ TypeScript knows ALL available methods and properties!

// VueMultiselect methods (from wrapped component)
dropdown.value?.open();
dropdown.value?.close();

// Custom GDropdown properties
dropdown.value?.isDropdownOpen;
dropdown.value?.toggle();

// Native HTMLInputElement methods
input.value?.focus();
input.value?.blur();

// Custom GInput methods
input.value?.clear();
input.value?.validate();
```

**When creating styled wrappers,** you should re-export the base component's exposed type to maintain this TypeScript support. See the styled component example below for how to do this.

---

## 🔷 TypeScript Support

All components are written in TypeScript with full type definitions:

```typescript
import type { GButtonProps } from '@twentyfourg/grimoire';

interface MyButtonProps extends GButtonProps {
  myCustomProp?: string;
}

const props = defineProps<MyButtonProps>();
```

**For component ref types** (to get full TypeScript autocomplete for methods and properties), see the [Exposed Types](#exposed-types-for-full-typescript-support) section above.

---

## 🧪 Testing

Grimoire has comprehensive test coverage:

- **40+ unit tests** per component (Vitest)
- **E2E visual regression tests** (Playwright)
- **100% prop coverage**
- **Accessibility testing**

Run tests:

```bash
# Unit tests
pnpm test

# Unit tests with UI
pnpm test:ui

# E2E tests
pnpm --filter @grimoire/showcase test:e2e

# Coverage report
pnpm test:coverage
```

📚 **See:** [Testing Strategy](./STYLED_COMPONENT_TESTING_STRATEGY.md) for testing styled components

---

## 📚 Documentation

- **[Styled Component Migration Guide](./STYLED_COMPONENT_MIGRATION_GUIDE.md)** - Complete v2 → v3 migration guide
- **[Component Showcase](./@app/showcase)** - Interactive examples of all components
- **[Testing Strategy](./STYLED_COMPONENT_TESTING_STRATEGY.md)** - How to test styled components
- **[Build Tools](./packages/grimoire/src/build-tools/README.md)** - Icon bundling with Iconify
- **[Refactoring Guide](./REFACTORING_SUCCESS_GUIDE.md)** - Safe component refactoring workflow

---

## 🤝 Contributing

Contributions are welcome!

### Development Setup

```bash
# Clone the repo
git clone https://github.com/twentyfourg/grimoire.git

# Install dependencies
pnpm install

# Run showcase in dev mode
pnpm run:showcase

# Run tests
pnpm test
```

### Project Structure

```
grimoire/
├── @app/showcase/         # Component showcase app
├── packages/
│   ├── grimoire/          # Core unstyled components
│   │   └── ai-docs/       # Consumer documentation (shipped with npm)
│   └── themes/            # Styled component examples
├── dev-docs/              # Internal developer documentation
├── ai_docs/               # AI-assisted maintenance documentation
└── scripts/               # Build and utility scripts
```

### Documentation

- **`dev-docs/`** - Internal documentation for Grimoire maintainers
- **`ai_docs/`** - AI-assisted docs for maintaining Grimoire (creating showcases, tests, etc.)
- **`packages/grimoire/ai-docs/`** - Consumer docs shipped with npm (migration guide, component usage, etc.)

---

## 📝 Changelog

See [CHANGELOG.md](./packages/grimoire/CHANGELOG.md) for version history.

We follow [Semantic Versioning](https://semver.org/).

---

## 📄 License

MIT License - see LICENSE file for details

---

**Built with ❤️ by 24G**
