From a11b878285ca406dfac98d35c251a5e319231505 Mon Sep 17 00:00:00 2001
From: Kasper Seweryn <github@wvffle.net>
Date: Mon, 11 Sep 2023 17:21:50 +0200
Subject: [PATCH] feat(stories): add button stories

---
 histoire.config.ts                     |   1 +
 histoire/setup.ts                      |  20 ++
 histoire/styles.css                    |  11 +
 package.json                           |   4 +-
 src/components/button/Button.story.vue | 330 +++++++++++++++++++++++--
 src/components/button/Button.vue       |  18 +-
 src/types/histoire.d.ts                |   4 +
 tsconfig.json                          |   4 +-
 vite.config.ts                         |  23 +-
 yarn.lock                              | 190 ++++++++------
 10 files changed, 476 insertions(+), 129 deletions(-)
 create mode 100644 histoire/setup.ts
 create mode 100644 histoire/styles.css
 create mode 100644 src/types/histoire.d.ts

diff --git a/histoire.config.ts b/histoire.config.ts
index 2069ca9..483807f 100644
--- a/histoire.config.ts
+++ b/histoire.config.ts
@@ -5,4 +5,5 @@ export default defineConfig({
   plugins: [
     HstVue(),
   ],
+  setupFile: 'histoire/setup.ts'
 })
diff --git a/histoire/setup.ts b/histoire/setup.ts
new file mode 100644
index 0000000..3b4bec8
--- /dev/null
+++ b/histoire/setup.ts
@@ -0,0 +1,20 @@
+import { defineSetupVue3 } from '@histoire/plugin-vue'
+import './styles.css'
+
+export const setupVue3 = defineSetupVue3(({ app, story, variant }) => {
+  app.config.globalProperties.$fwColors = ['primary', 'secondary', 'destructive'] as Color[]
+  app.config.globalProperties.$fwPastels = ['blue', 'red', 'purple', 'green', 'yellow'] as Pastel[]
+
+  app.config.globalProperties.$fwOmitHstProps = (state: Record<string, unknown>) => {
+    const props = Object.create(null)
+
+    for (const key in state) {
+      if (key.startsWith('_')) continue
+      if (key.startsWith('$')) continue
+      if (key.startsWith('variants')) continue
+      props[key] = state[key]
+    }
+
+    return props
+  }
+})
diff --git a/histoire/styles.css b/histoire/styles.css
new file mode 100644
index 0000000..c4c3350
--- /dev/null
+++ b/histoire/styles.css
@@ -0,0 +1,11 @@
+.hst-label {
+  font-size: 0.9em;
+  font-weight: bold;
+  color: #666;
+  padding: 0.5em;
+}
+
+.hst-label:not(:first-child) {
+  border-top: 1px solid rgb(var(--_histoire-color-gray-100));
+  margin-top: 0.5em;
+}
diff --git a/package.json b/package.json
index e838a3e..6948adc 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
     "transliteration": "^2.3.5"
   },
   "devDependencies": {
-    "@histoire/plugin-vue": "^0.16.1",
+    "@histoire/plugin-vue": "^0.17.1",
     "@modyfi/vite-plugin-yaml": "^1.x < 1.0.3 || ^1.x >= 1.0.4",
     "@types/dompurify": "^3.0.0",
     "@types/showdown": "^2.0.1",
@@ -49,7 +49,7 @@
     "@vueuse/core": "^10.3.0",
     "bootstrap-icons": "^1.10.5",
     "conventional-changelog-cli": "^3.0.0",
-    "histoire": "^0.16.2",
+    "histoire": "^0.17.0",
     "jsdom": "^22.1.0",
     "magic-regexp": "^0.7.0",
     "sass": "^1.65.1",
diff --git a/src/components/button/Button.story.vue b/src/components/button/Button.story.vue
index 142cf17..fb8b9b1 100644
--- a/src/components/button/Button.story.vue
+++ b/src/components/button/Button.story.vue
@@ -6,47 +6,337 @@ import { reactive } from 'vue'
 const variants = reactive([
   {
     title: 'Primary button',
+    text: 'Primary button',
     state: () => ({
-      primary: true
+      color: 'primary'
     })
   },
   {
     title: 'Secondary button',
+    text: 'Secondary button',
     state: () => ({
-      secondary: true
+      color: 'secondary'
     })
   },
   {
     title: 'Destructive button',
+    text: 'Destructive button',
     state: () => ({
-      destructive: true
+      color: 'destructive'
     })
-  }
+  },
+  {
+    title: 'Outline button',
+    text: 'Outline button',
+    state: () => ({
+      outline: true
+    })
+  },
+  {
+    title: 'Shadow button',
+    text: 'Shadow button',
+    state: () => ({
+      shadow: true
+    })
+  },
+  {
+    title: 'Active button',
+    text: 'Active button',
+    state: () => ({
+      isActive: true
+    })
+  },
+  {
+    title: 'Loading button',
+    text: 'Loading button',
+    state: () => ({
+      isLoading: true
+    })
+  },
+  {
+    title: 'Icon',
+    text: 'Icon button',
+    state: () => ({
+      icon: 'bi-search'
+    })
+  },
+  {
+    title: 'Icon without content',
+    text: undefined,
+    state: () => ({
+      icon: 'bi-search'
+    })
+  },
 ])
 </script>
 
 <template>
-  <Story title="Buttons">
-    <Variant
-      v-for="v in variants"
-      :key="v.title"
-      :init-state="v.state" 
-      :title="v.title"
-    >
+  <Story title="<FwButton>">
+    <Variant v-for="v in variants" :key="v.title" :init-state="v.state" :title="v.title" auto-props-disabled>
       <template #="{ state }">
-        <FwButton 
-          @click="logEvent('click', $event)"
-          :primary="state.primary"
-          :secondary="state.secondary"
-          :destructive="state.destructive"
-        >
-          Test button
-        </FwButton>
+        <FwButton v-if="v.text" @click="logEvent('click', $event)" v-bind="$fwOmitHstProps(state)">{{ v.text }}</FwButton>
+        <FwButton v-else @click="logEvent('click', $event)" v-bind="$fwOmitHstProps(state)" />
       </template>
 
       <template #controls="{ state }">
-        <!-- TODO: Add HstButtonGroup for color -->
+        <div class="hst-label">Style:</div>
+        <HstSelect v-model="state.color" title="color" :options="$fwColors" />
+        <HstCheckbox v-model="state.outline" title="outline" />
+        <HstCheckbox v-model="state.shadow" title="shadow" />
+        <HstCheckbox v-model="state.round" title="round" />
+
+        <div class="hst-label">State:</div>
+        <HstCheckbox v-model="state.isActive" title="isActive" />
+        <HstCheckbox v-model="state.isLoading" title="isLoading" />
+
+        <div class="hst-label">Icon:</div>
+        <HstText v-model="state.icon" title="icon" />
       </template>
     </Variant>
   </Story>
 </template>
+
+<docs lang="md">
+  # Button
+
+  Buttons are UI elements that users can interact with to perform actions. Funkwhale uses buttons in many contexts.
+
+  ## Button styles
+  Buttons come in different styles that you can use depending on the location or action of the button.
+
+  ### Primary
+
+  Primary buttons represent **positive** actions such as uploading, confirming, and accepting changes.
+
+  ::: info
+  This is the default type. If you don't specify a type, a primary button is rendered.
+  :::
+
+  ```vue-html
+  <FwButton>
+    Primary button
+  </FwButton>
+  ```
+
+  <FwButton>
+    Primary button
+  </FwButton>
+
+  ### Secondary
+
+  Secondary buttons represent **neutral** actions such as cancelling a change or dismissing a notification.
+
+  ```vue-html
+  <FwButton color="secondary">
+    Secondary button
+  </FwButton>
+  ```
+
+  <FwButton color="secondary">
+    Secondary button
+  </FwButton>
+
+  ### Destructive
+
+  Desctrutive buttons represent **dangerous** actions including deleting items or purging domain information.
+
+  ```vue-html
+  <FwButton color="destructive">
+    Destructive button
+  </FwButton>
+  ```
+
+  <FwButton color="destructive">
+    Destructive button
+  </FwButton>
+
+
+  ### Filled
+
+  Filled buttons have a solid background. Use these to emphasize the action the button performs.
+
+  ::: info
+  This is the default style. If you don't specify a style, a filled button is rendered.
+  :::
+
+  ```vue-html
+  <FwButton>
+    Filled button
+  </FwButton>
+  ```
+
+  <FwButton>
+    Filled button
+  </FwButton>
+
+  ### Outline
+
+  Outline buttons have a transparent background. Use these to deemphasize the action the button performs.
+
+  ```vue-html
+  <FwButton outline>
+    Outline button
+  </FwButton>
+  ```
+
+  <FwButton outline>
+    Outline button
+  </FwButton>
+
+  ### Shadow
+
+  You can give a button a shadow to add depth.
+
+  ```vue-html
+  <FwButton shadow>
+    Shadow button
+  </FwButton>
+  ```
+
+  <FwButton shadow>
+    Shadow button
+  </FwButton>
+
+  ## Button shapes
+
+  You can choose different shapes for buttons depending on their location and use.
+
+  ### Normal
+
+  Normal buttons are slightly rounded rectangles.
+
+  ::: info
+  This is the default shape. If you don't specify a type, a normal button is rendered.
+  :::
+
+  ```vue-html
+  <FwButton>
+    Normal button
+  </FwButton>
+  ```
+
+  <FwButton>
+    Normal button
+  </FwButton>
+
+  ### Round
+
+  Round buttons have fully rounded edges.
+
+  ```vue-html
+  <FwButton round>
+    Round button
+  </FwButton>
+  ```
+
+  <FwButton round>
+    Round button
+  </FwButton>
+
+  ## Button states
+
+  You can pass a state to indicate whether a user can interact with a button.
+
+  ### Active
+
+  A button is active when clicked by a user. You can force an active state by passing an `is-active` prop.
+
+  ```vue-html
+  <FwButton is-active>
+    Active button
+  </FwButton>
+  ```
+
+  <FwButton is-active>
+    Active button
+  </FwButton>
+
+  ### Disabled
+
+  Disabled buttons are non-interactive and inherit a less bold color than the one provided. You can apply a disabled state by passing a `disabled` prop.
+
+  ```vue-html
+  <FwButton disabled>
+    Disabled button
+  </FwButton>
+  ```
+
+  <FwButton disabled>
+    Disabled button
+  </FwButton>
+
+  ### Loading
+
+  If a user can't interact with a button until something has finished loading, you can add a spinner by passing the `is-loading` prop.
+
+  ```vue-html
+  <FwButton is-loading>
+    Loading button
+  </FwButton>
+  ```
+
+  <FwButton is-loading>
+    Loading button
+  </FwButton>
+
+  ### Promise handling in `@click`
+
+  When a function passed to `@click` returns a promise, the button automatically toggles a loading state on click. When the promise resolves or is rejected, the loading state turns off.
+
+  ::: danger
+  There is no promise rejection mechanism implemented in the `&lt;FwButton&gt;` component. Make sure the `@click` handler never rejects.
+  :::
+
+  ```vue
+  <script setup lang="ts">
+  const click = () => new Promise((resolve) => setTimeout(resolve, 1000));
+  </script>
+
+  <template>
+    <FwButton @click="click"> Click me </FwButton>
+  </template>
+  ```
+
+  <FwButton @click="click">
+  Click me
+  </FwButton>
+
+  You can override the promise state by passing a false `is-loading` prop.
+
+  ```vue-html
+  <FwButton :is-loading="false">
+    Click me
+  </FwButton>
+  ```
+
+  <FwButton :is-loading="false">
+    Click me
+  </FwButton>
+
+  ## Icons
+
+  You can use [Bootstrap Icons](https://icons.getbootstrap.com/) in your button component
+
+  ::: info
+  Icon buttons shrink down to the icon size if you don't pass any content. If you want to keep the button at full width with just an icon, add `&nbsp;` into the slot.
+  :::
+
+  ```vue-html
+  <FwButton color="secondary" icon="bi-three-dots-vertical" />
+
+  <FwButton color="secondary" is-round icon="bi-x" />
+
+  <FwButton icon="bi-save">&nbsp;</FwButton>
+
+  <FwButton color="destructive" icon="bi-trash">
+    Delete
+  </FwButton>
+  ```
+
+  <FwButton color="secondary" icon="bi-three-dots-vertical" />
+  <FwButton color="secondary" round icon="bi-x" />
+  <FwButton icon="bi-save">&nbsp;</FwButton>
+  <FwButton color="destructive" icon="bi-trash">
+    Delete
+  </FwButton>
+</docs>
diff --git a/src/components/button/Button.vue b/src/components/button/Button.vue
index 7696584..1cf574f 100644
--- a/src/components/button/Button.vue
+++ b/src/components/button/Button.vue
@@ -3,7 +3,7 @@ import { useColor } from '~/composables/colors'
 import { FwLoader } from '~/components'
 import { ref, computed, useSlots } from 'vue'
 
-interface Props {
+export interface Props {
   outline?: boolean
 
   isActive?: boolean
@@ -38,23 +38,15 @@ const click = async (...args: any[]) => {
 </script>
 
 <template>
-  <button
-    class="funkwhale is-colored button"
+  <button class="funkwhale is-colored button"
     :class="[color, { 'is-active': isActive, 'is-outline': outline, 'is-loading': isLoading, 'icon-only': iconOnly, 'is-round': round, 'is-shadow': shadow }]"
-    @click="click"
-  >
+    @click="click">
     <span>
-      <i
-        v-if="icon"
-        :class="['bi', icon]"
-      />
+      <i v-if="icon" :class="['bi', icon]" />
       <slot />
     </span>
 
-    <fw-loader
-      v-if="isLoading"
-      :container="false"
-    />
+    <fw-loader v-if="isLoading" :container="false" />
   </button>
 </template>
 
diff --git a/src/types/histoire.d.ts b/src/types/histoire.d.ts
new file mode 100644
index 0000000..0e80ef5
--- /dev/null
+++ b/src/types/histoire.d.ts
@@ -0,0 +1,4 @@
+declare interface HstVariant {
+  title: string
+  props: Record<string, unknown>
+}
diff --git a/tsconfig.json b/tsconfig.json
index de6d68b..161ec5c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -27,7 +27,9 @@
     "./test/**/*.ts",
     "./vite.config.ts",
     "./vitest.config.ts",
-    "./docs/vite.config.ts"
+    "./docs/vite.config.ts",
+    "./histoire.config.ts",
+    "./histoire/**/*.ts"
   ],
   "vueCompilerOptions": {
     "plugins": [
diff --git a/vite.config.ts b/vite.config.ts
index 5bbaf11..140012d 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -17,7 +17,8 @@ export default defineConfig({
             // @ts-expect-error upstream types are outdated
             globalTypeFiles: [
               fileURLToPath(new URL('./src/types/models.d.ts', import.meta.url)),
-              fileURLToPath(new URL('./src/types/common-props.d.ts', import.meta.url))
+              fileURLToPath(new URL('./src/types/common-props.d.ts', import.meta.url)),
+              fileURLToPath(new URL('./src/types/histoire.d.ts', import.meta.url))
             ]
           }
         })
@@ -45,22 +46,22 @@ export default defineConfig({
       }
     }
   },
-	build: {
-	  sourcemap: 'inline',
-		lib: {
+  build: {
+    sourcemap: 'inline',
+    lib: {
       entry: fileURLToPath(new URL('./src/main.ts', import.meta.url)),
-		  name: '@funkwhale/vui',
+      name: '@funkwhale/vui',
       fileName: 'vui'
-		},
-		rollupOptions: {
-		  external: ['vue', 'vue-i18n', '@vueuse/core', 'vue-router'],
-		  output: {
+    },
+    rollupOptions: {
+      external: ['vue', 'vue-i18n', '@vueuse/core', 'vue-router'],
+      output: {
         exports: 'named',
         globals: {
           vue: 'Vue'
         },
         sourcemap: 'hidden'
-		  }
-		}
+      }
+    }
   }
 })
diff --git a/yarn.lock b/yarn.lock
index d33ab67..30b096f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -197,17 +197,17 @@
   resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
   integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
 
-"@codemirror/commands@^6.1.1":
-  version "6.2.4"
-  resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-6.2.4.tgz#b8a0e5ce72448c092ba4c4b1d902e6f183948aec"
-  integrity sha512-42lmDqVH0ttfilLShReLXsDfASKLXzfyC36bzwcqzox9PlHulMcsUOfHXNo2X2aFMVNUoQ7j+d4q5bnfseYoOA==
+"@codemirror/commands@^6.2.4":
+  version "6.2.5"
+  resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-6.2.5.tgz#e889f93f9cc85b32f6b2844d85d08688f695a6b8"
+  integrity sha512-dSi7ow2P2YgPBZflR9AJoaTHvqmeGIgkhignYMd5zK5y6DANTvxKxp6eMEpIDUJkRAaOY/TFZ4jP1ADIO/GLVA==
   dependencies:
     "@codemirror/language" "^6.0.0"
     "@codemirror/state" "^6.2.0"
     "@codemirror/view" "^6.0.0"
     "@lezer/common" "^1.0.0"
 
-"@codemirror/lang-json@^6.0.0":
+"@codemirror/lang-json@^6.0.1":
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/@codemirror/lang-json/-/lang-json-6.0.1.tgz#0a0be701a5619c4b0f8991f9b5e95fe33f462330"
   integrity sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==
@@ -215,7 +215,7 @@
     "@codemirror/language" "^6.0.0"
     "@lezer/json" "^1.0.0"
 
-"@codemirror/language@^6.0.0", "@codemirror/language@^6.2.1":
+"@codemirror/language@^6.0.0":
   version "6.8.0"
   resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.8.0.tgz#f2d7eea6b338c25593d800f2293b062d9f9856db"
   integrity sha512-r1paAyWOZkfY0RaYEZj3Kul+MiQTEbDvYqf8gPGaRvNneHXCmfSaAVFjwRUPlgxS8yflMxw2CTu6uCMp8R8A2g==
@@ -227,21 +227,33 @@
     "@lezer/lr" "^1.0.0"
     style-mod "^4.0.0"
 
-"@codemirror/lint@^6.0.0":
-  version "6.4.0"
-  resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-6.4.0.tgz#3507e937aa9415ef0831ff04734ef0e736e75014"
-  integrity sha512-6VZ44Ysh/Zn07xrGkdtNfmHCbGSHZzFBdzWi0pbd7chAQ/iUcpLGX99NYRZTa7Ugqg4kEHCqiHhcZnH0gLIgSg==
+"@codemirror/language@^6.8.0":
+  version "6.9.0"
+  resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.9.0.tgz#c471ce1fdb9b1577f312bb68ef54fb4b76f7a420"
+  integrity sha512-nFu311/0ne/qGuGCL3oKuktBgzVOaxCHZPZv1tLSZkNjPYxxvkjSbzno3MlErG2tgw1Yw1yF8BxMCegeMXqpiw==
+  dependencies:
+    "@codemirror/state" "^6.0.0"
+    "@codemirror/view" "^6.0.0"
+    "@lezer/common" "^1.0.0"
+    "@lezer/highlight" "^1.0.0"
+    "@lezer/lr" "^1.0.0"
+    style-mod "^4.0.0"
+
+"@codemirror/lint@^6.4.0":
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-6.4.1.tgz#e4626ff8890c745209c6c24840843ff7933bc64f"
+  integrity sha512-2Hx945qKX7FBan5/gUdTM8fsMYrNG9clIgEcPXestbLVFAUyQYFAuju/5BMNf/PwgpVaX5pvRm4+ovjbp9D9gQ==
   dependencies:
     "@codemirror/state" "^6.0.0"
     "@codemirror/view" "^6.0.0"
     crelt "^1.0.5"
 
-"@codemirror/state@^6.0.0", "@codemirror/state@^6.1.2", "@codemirror/state@^6.1.4", "@codemirror/state@^6.2.0":
+"@codemirror/state@^6.0.0", "@codemirror/state@^6.1.4", "@codemirror/state@^6.2.0", "@codemirror/state@^6.2.1":
   version "6.2.1"
   resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.2.1.tgz#6dc8d8e5abb26b875e3164191872d69a5e85bd73"
   integrity sha512-RupHSZ8+OjNT38zU9fKH2sv+Dnlr8Eb8sl4NOnnqz95mCFTZUaiRP8Xv5MeeaG0px2b8Bnfe7YGwCV3nsBhbuw==
 
-"@codemirror/theme-one-dark@^6.1.0":
+"@codemirror/theme-one-dark@^6.1.2":
   version "6.1.2"
   resolved "https://registry.yarnpkg.com/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz#fcef9f9cfc17a07836cb7da17c9f6d7231064df8"
   integrity sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==
@@ -251,7 +263,7 @@
     "@codemirror/view" "^6.0.0"
     "@lezer/highlight" "^1.0.0"
 
-"@codemirror/view@^6.0.0", "@codemirror/view@^6.3.0":
+"@codemirror/view@^6.0.0":
   version "6.16.0"
   resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.16.0.tgz#047001b8dd04e104776c476e45ee9c4eed9f99fa"
   integrity sha512-1Z2HkvkC3KR/oEZVuW9Ivmp8TWLzGEd8T8TA04TTwPvqogfkHBdYSlflytDOqmkUxM2d1ywTg7X2dU5mC+SXvg==
@@ -260,6 +272,15 @@
     style-mod "^4.0.0"
     w3c-keyname "^2.2.4"
 
+"@codemirror/view@^6.16.0":
+  version "6.18.1"
+  resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.18.1.tgz#816782f19354a688b6dc33b30ab25570003f1089"
+  integrity sha512-xcsXcMkIMd7l3WZEWoc4ljteAiqzxb5gVerRxk5132p5cLix6rTydWTQjsj2oxORepfsrwy1fC4r20iMa9plrg==
+  dependencies:
+    "@codemirror/state" "^6.1.4"
+    style-mod "^4.1.0"
+    w3c-keyname "^2.2.4"
+
 "@docsearch/css@3.5.1", "@docsearch/css@^3.5.1":
   version "3.5.1"
   resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.5.1.tgz#4adf9884735bbfea621c3716e80ea97baa419b73"
@@ -393,62 +414,62 @@
   resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
   integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==
 
-"@histoire/app@^0.16.1":
-  version "0.16.1"
-  resolved "https://registry.yarnpkg.com/@histoire/app/-/app-0.16.1.tgz#57807df1877e52c993eff7f9735f50b237d46fcb"
-  integrity sha512-13komnhVk1Pk0wMmkJKDPWT8RKpA5HfAbeeXSHAq29pvFP9Faq+dAa62g1wqOpoyJD5C7SkI0OPI3eJwJHgTiQ==
+"@histoire/app@^0.17.0":
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/@histoire/app/-/app-0.17.0.tgz#4dab512876a6d7f3b5beff000d9a979b373c88d7"
+  integrity sha512-rZyuIw3Ck6RqwBSkCfIAZAzKZTclzWmLcCNtSCbUHF/wcEUbFdfAhGTW56+NhzNeURq+QxNuklHhSnE+x0PiEQ==
   dependencies:
-    "@histoire/controls" "^0.16.1"
-    "@histoire/shared" "^0.16.1"
-    "@histoire/vendors" "^0.16.0"
+    "@histoire/controls" "^0.17.0"
+    "@histoire/shared" "^0.17.0"
+    "@histoire/vendors" "^0.17.0"
     "@types/flexsearch" "^0.7.3"
     flexsearch "0.7.21"
     shiki-es "^0.2.0"
 
-"@histoire/controls@^0.16.1":
-  version "0.16.1"
-  resolved "https://registry.yarnpkg.com/@histoire/controls/-/controls-0.16.1.tgz#a1119339adce917895a7a60f37f18e9051b0d3b0"
-  integrity sha512-Ot/J/LPzUexn+fLrJrWu3jUakx9aVSJWKnriiJSmEodAxJq+4mrprX3xS0bnzieud19pJc3mzC/MSD94urTbHA==
-  dependencies:
-    "@codemirror/commands" "^6.1.1"
-    "@codemirror/lang-json" "^6.0.0"
-    "@codemirror/language" "^6.2.1"
-    "@codemirror/lint" "^6.0.0"
-    "@codemirror/state" "^6.1.2"
-    "@codemirror/theme-one-dark" "^6.1.0"
-    "@codemirror/view" "^6.3.0"
-    "@histoire/shared" "^0.16.1"
-    "@histoire/vendors" "^0.16.0"
-
-"@histoire/plugin-vue@^0.16.1":
-  version "0.16.1"
-  resolved "https://registry.yarnpkg.com/@histoire/plugin-vue/-/plugin-vue-0.16.1.tgz#f29be803dc86736ed832b22687ab873fd909fb03"
-  integrity sha512-K7ZZl5tA8PWHjQsWFmFX3xa4HlRs+S8+nxym1Smh4dudQ6XSVwdF+gsPQ+RE4zwf6YQ8HDPsvOobI31dz6F4Tg==
-  dependencies:
-    "@histoire/controls" "^0.16.1"
-    "@histoire/shared" "^0.16.1"
-    "@histoire/vendors" "^0.16.0"
+"@histoire/controls@^0.17.0":
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/@histoire/controls/-/controls-0.17.0.tgz#9a7a524fcf53600b5f5585b2c54fa601f1064cf3"
+  integrity sha512-zdZB4kLthWIZxe/GxUdYM18y7lXiF13g8BMomE9d/YudY1KFkfmMKSbrNlDyM9yKiTd6ycSRJIWEXlxOSuyNrw==
+  dependencies:
+    "@codemirror/commands" "^6.2.4"
+    "@codemirror/lang-json" "^6.0.1"
+    "@codemirror/language" "^6.8.0"
+    "@codemirror/lint" "^6.4.0"
+    "@codemirror/state" "^6.2.1"
+    "@codemirror/theme-one-dark" "^6.1.2"
+    "@codemirror/view" "^6.16.0"
+    "@histoire/shared" "^0.17.0"
+    "@histoire/vendors" "^0.17.0"
+
+"@histoire/plugin-vue@^0.17.1":
+  version "0.17.1"
+  resolved "https://registry.yarnpkg.com/@histoire/plugin-vue/-/plugin-vue-0.17.1.tgz#1ddd1c5ec0572469261dba2ceff2cb0afdecf4fb"
+  integrity sha512-p9MkCy1Ohe0RkAwpTB6XcOoAhlSsONBSWorhVukwi4HnIMUsPbTjOVkLfKhALW9QtVux+7reWXRocJdLECNwGA==
+  dependencies:
+    "@histoire/controls" "^0.17.0"
+    "@histoire/shared" "^0.17.0"
+    "@histoire/vendors" "^0.17.0"
     change-case "^4.1.2"
-    globby "^13.1.1"
+    globby "^13.2.2"
     launch-editor "^2.6.0"
     pathe "^0.2.0"
 
-"@histoire/shared@^0.16.1":
-  version "0.16.1"
-  resolved "https://registry.yarnpkg.com/@histoire/shared/-/shared-0.16.1.tgz#9002f4453b190005ddbf16913e21ac329fa7cf07"
-  integrity sha512-bcySHGC6kcZ1U9OZUcBQCROTBygTZ9T9MlqfeGtBtJWXGdmHPZ/64elZOY36O8gUAMF89Q08EIVe5cIQ0SJ3Uw==
+"@histoire/shared@^0.17.0":
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/@histoire/shared/-/shared-0.17.0.tgz#c9ba36cb36caab7d74b4f51e9ef4f63c08e7af3e"
+  integrity sha512-itdEqD+V+WNkCqhSsgoSRuoHeXBH3/bNjY98vHz/89prKeqEVMGbBbGPUpgm1IpKEYrNcLhfDAREJHnwsKxKCg==
   dependencies:
-    "@histoire/vendors" "^0.16.0"
+    "@histoire/vendors" "^0.17.0"
     "@types/fs-extra" "^9.0.13"
     "@types/markdown-it" "^12.2.3"
     chokidar "^3.5.3"
     pathe "^0.2.0"
     picocolors "^1.0.0"
 
-"@histoire/vendors@^0.16.0":
-  version "0.16.0"
-  resolved "https://registry.yarnpkg.com/@histoire/vendors/-/vendors-0.16.0.tgz#2f92b5ba6b8f835607ada3ab7e27d807236cb02c"
-  integrity sha512-MVr3P2q5Q9UiLJJT99b+j2ABCSerQG4VnUeCr5+eOxyEmoIFz1a0KGJimzqQM0EoVnMQmiaNN3WhuUYG+DWh/w==
+"@histoire/vendors@^0.17.0":
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/@histoire/vendors/-/vendors-0.17.0.tgz#12eff1055eacee80fea05bdb0eadb6d30a6541f8"
+  integrity sha512-HI2hkai3p5+gAoWZAGWoAdDLlu0ujfYtGhykGChTQyKAroocizRG21l/2w71cN1K+oigbAccqtxP8BkpqX74cQ==
 
 "@hutson/parse-repository-url@^3.0.0":
   version "3.0.2"
@@ -1834,7 +1855,7 @@ deep-eql@^4.1.2:
   dependencies:
     type-detect "^4.0.0"
 
-defu@^6.0.0:
+defu@^6.1.2:
   version "6.1.2"
   resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.2.tgz#1217cba167410a1765ba93893c6dbac9ed9d9e5c"
   integrity sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==
@@ -2102,7 +2123,7 @@ form-data@^4.0.0:
     combined-stream "^1.0.8"
     mime-types "^2.1.12"
 
-fs-extra@^10.0.1:
+fs-extra@^10.1.0:
   version "10.1.0"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
   integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
@@ -2208,7 +2229,7 @@ glob@^8.1.0:
     minimatch "^5.0.1"
     once "^1.3.0"
 
-globby@^13.1.1:
+globby@^13.2.2:
   version "13.2.2"
   resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592"
   integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==
@@ -2281,41 +2302,41 @@ header-case@^2.0.4:
     capital-case "^1.0.4"
     tslib "^2.0.3"
 
-histoire@^0.16.2:
-  version "0.16.2"
-  resolved "https://registry.yarnpkg.com/histoire/-/histoire-0.16.2.tgz#cff4e11ef18782902a7f840db3b9031263ec9861"
-  integrity sha512-7/dErREfXEqAq69KuVMThu+uYzBfIuc6R13z5uylNm+rGO6WX62Y70DeKTxnp4NsYPXBNPcBhWY6UGICCuhHaw==
+histoire@^0.17.0:
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/histoire/-/histoire-0.17.0.tgz#98c3de2be7fbedff206ccd3a3e2b824bb20f1d1b"
+  integrity sha512-q5fX2yAiw1vjlC0xaEcm8uqiJzdKmItozyNa24ujFVD+YjKpBE1R6tl/zpUIfqOBgpifMgh7xQD5uU4ZFd7v/g==
   dependencies:
     "@akryum/tinypool" "^0.3.1"
-    "@histoire/app" "^0.16.1"
-    "@histoire/controls" "^0.16.1"
-    "@histoire/shared" "^0.16.1"
-    "@histoire/vendors" "^0.16.0"
+    "@histoire/app" "^0.17.0"
+    "@histoire/controls" "^0.17.0"
+    "@histoire/shared" "^0.17.0"
+    "@histoire/vendors" "^0.17.0"
     "@types/flexsearch" "^0.7.3"
     "@types/markdown-it" "^12.2.3"
     birpc "^0.1.1"
     change-case "^4.1.2"
     chokidar "^3.5.3"
     connect "^3.7.0"
-    defu "^6.0.0"
+    defu "^6.1.2"
     diacritics "^1.3.0"
     flexsearch "0.7.21"
-    fs-extra "^10.0.1"
-    globby "^13.1.1"
+    fs-extra "^10.1.0"
+    globby "^13.2.2"
     gray-matter "^4.0.3"
-    jiti "^1.18.2"
+    jiti "^1.19.1"
     jsdom "^20.0.3"
     markdown-it "^12.3.2"
-    markdown-it-anchor "^8.6.2"
-    markdown-it-attrs "^4.1.3"
-    markdown-it-emoji "^2.0.0"
+    markdown-it-anchor "^8.6.7"
+    markdown-it-attrs "^4.1.6"
+    markdown-it-emoji "^2.0.2"
     micromatch "^4.0.5"
-    mrmime "^1.0.0"
+    mrmime "^1.0.1"
     pathe "^0.2.0"
     picocolors "^1.0.0"
     sade "^1.8.1"
     shiki-es "^0.2.0"
-    sirv "^2.0.2"
+    sirv "^2.0.3"
     vite-node "0.28.4"
 
 hosted-git-info@^2.1.4:
@@ -2503,10 +2524,10 @@ istanbul-reports@^3.1.5:
     html-escaper "^2.0.0"
     istanbul-lib-report "^3.0.0"
 
-jiti@^1.18.2:
-  version "1.19.1"
-  resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.19.1.tgz#fa99e4b76a23053e0e7cde098efe1704a14c16f1"
-  integrity sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==
+jiti@^1.19.1:
+  version "1.20.0"
+  resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.20.0.tgz#2d823b5852ee8963585c8dd8b7992ffc1ae83b42"
+  integrity sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==
 
 js-beautify@1.14.9:
   version "1.14.9"
@@ -2762,17 +2783,17 @@ mark.js@8.11.1:
   resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5"
   integrity sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==
 
-markdown-it-anchor@^8.6.2:
+markdown-it-anchor@^8.6.7:
   version "8.6.7"
   resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz#ee6926daf3ad1ed5e4e3968b1740eef1c6399634"
   integrity sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==
 
-markdown-it-attrs@^4.1.3:
+markdown-it-attrs@^4.1.6:
   version "4.1.6"
   resolved "https://registry.yarnpkg.com/markdown-it-attrs/-/markdown-it-attrs-4.1.6.tgz#2bc331c7649d8c6396a0613c2bba1093f3e64da9"
   integrity sha512-O7PDKZlN8RFMyDX13JnctQompwrrILuz2y43pW2GagcwpIIElkAdfeek+erHfxUOlXWPsjFeWmZ8ch1xtRLWpA==
 
-markdown-it-emoji@^2.0.0:
+markdown-it-emoji@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/markdown-it-emoji/-/markdown-it-emoji-2.0.2.tgz#cd42421c2fda1537d9cc12b9923f5c8aeb9029c8"
   integrity sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ==
@@ -2907,7 +2928,7 @@ mri@^1.1.0:
   resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
   integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
 
-mrmime@^1.0.0:
+mrmime@^1.0.0, mrmime@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27"
   integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==
@@ -3454,7 +3475,7 @@ siginfo@^2.0.0:
   resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
   integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==
 
-sirv@^2.0.2, sirv@^2.0.3:
+sirv@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.3.tgz#ca5868b87205a74bef62a469ed0296abceccd446"
   integrity sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==
@@ -3613,6 +3634,11 @@ style-mod@^4.0.0:
   resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.0.3.tgz#136c4abc905f82a866a18b39df4dc08ec762b1ad"
   integrity sha512-78Jv8kYJdjbvRwwijtCevYADfsI0lGzYJe4mMFdceO8l75DFFDoqBhR1jVDicDRRaX4//g1u9wKeo+ztc2h1Rw==
 
+style-mod@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.1.0.tgz#a313a14f4ae8bb4d52878c0053c4327fb787ec09"
+  integrity sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==
+
 supports-color@^5.3.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
-- 
GitLab