refactor: update hopp-ui to be independent (#2927)

Co-authored-by: Anwarul Islam <anwaarulislaam@gmail.com>
This commit is contained in:
Andrew Bastin
2023-02-24 13:20:12 +05:30
committed by GitHub
parent 82c6f6f6bc
commit cae1840506
165 changed files with 2134 additions and 2069 deletions

View File

@@ -19,7 +19,7 @@ Welcome to hoppscotch-ui, a collection of presentational components for our web
To use the components in project, simply name the component with `directory` name as alias:
For example `Primary Button` component is in `button` directory and the file name is `Primary.vue`. So, use that you have to write `<ButtonPrimary />`
For example `Primary Button` component is in `button` directory and the file name is `Primary.vue`. So, use that you have to write `<HoppButtonPrimary />`
## Histoire

View File

@@ -2,13 +2,20 @@
"name": "@hoppscotch/ui",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"build": "vite build",
"story:dev": "histoire dev",
"story:build": "histoire build",
"story:preview": "histoire preview",
"do-build-prod": "pnpm run build",
"postinstall": "pnpm run build",
"do-build-ui": "pnpm run story:build"
},
"peerDependencies": {
"vue": "^3.2.25",
"vue-router": "^4.0.16"
},
"dependencies": {
"@hoppscotch/vue-toasted": "^0.1.0",
"@lezer/highlight": "^1.0.0",
@@ -30,11 +37,10 @@
"url": "^0.11.0",
"util": "^0.12.4",
"vite-plugin-eslint": "^1.8.1",
"vue": "^3.2.25",
"vue-github-button": "^3.0.3",
"vue-router": "^4.0.16",
"vue-tippy": "6.0.0-alpha.58",
"vuedraggable": "^4.1.0"
"vuedraggable-es": "^4.1.1"
},
"devDependencies": {
"@esbuild-plugins/node-globals-polyfill": "^0.1.1",
@@ -60,10 +66,11 @@
"rollup-plugin-polyfill-node": "^0.10.1",
"sass": "^1.53.0",
"typescript": "^4.5.4",
"unplugin-icons": "^0.14.9",
"unplugin-icons": "^0.15.3",
"unplugin-vue-components": "^0.21.0",
"vite": "^3.2.3",
"vite": "^4.1.3",
"vite-plugin-checker": "^0.5.1",
"vite-plugin-dts": "2.0.0-beta.3",
"vite-plugin-fonts": "^0.6.0",
"vite-plugin-html-config": "^1.0.10",
"vite-plugin-inspect": "^0.7.4",
@@ -77,7 +84,13 @@
"windicss": "^3.5.6"
},
"files": [
"src",
"dist"
]
],
"module": "./dist/index.es.js",
"main": "./dist/index.es.js",
"exports": {
".": "./dist/index.es.js",
"./style.css": "./dist/style.css"
},
"types": "./dist/index.d.ts"
}

View File

@@ -1,64 +1,3 @@
* {
@apply backface-hidden;
@apply before:backface-hidden;
@apply after:backface-hidden;
@apply selection:bg-accentDark;
@apply selection:text-accentContrast;
}
:root {
@apply antialiased;
accent-color: var(--accent-color);
font-variant-ligatures: common-ligatures;
}
::-webkit-scrollbar-track {
@apply bg-transparent;
@apply border-solid border-l border-dividerLight border-t-0 border-b-0 border-r-0;
}
::-webkit-scrollbar-thumb {
@apply bg-divider bg-clip-content;
@apply rounded-full;
@apply border-solid border-transparent border-4;
@apply hover:bg-dividerDark;
@apply hover:bg-clip-content;
}
::-webkit-scrollbar {
@apply w-4;
@apply h-0;
}
input::placeholder,
textarea::placeholder,
.cm-placeholder {
@apply text-secondary;
@apply opacity-50;
}
input,
textarea {
@apply text-secondaryDark;
@apply font-medium;
}
html {
scroll-behavior: smooth;
}
body {
@apply bg-primary;
@apply text-secondary text-body;
@apply font-medium;
@apply select-none;
@apply overflow-x-hidden;
@apply leading-body;
animation: fade 300ms forwards;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
}
@keyframes fade {
0% {
@apply opacity-0;
@@ -130,89 +69,6 @@ a {
}
}
.cm-tooltip {
.tippy-box {
@apply shadow-none;
@apply fixed;
@apply inline-flex;
@apply -mt-8;
}
}
.tippy-box[data-theme~="tooltip"] {
@apply bg-tooltip;
@apply border-solid border-tooltip;
@apply rounded;
@apply shadow;
.tippy-content {
@apply flex;
@apply text-tiny text-primary;
@apply font-semibold;
@apply py-1 px-2;
@apply truncate;
@apply leading-normal;
@apply items-center;
kbd {
@apply hidden;
@apply font-sans;
@apply bg-gray-500/45;
@apply text-primaryLight;
@apply rounded-sm;
@apply px-1;
@apply my-0 ml-1;
@apply truncate;
@apply sm:inline-flex;
}
.env-icon {
@apply transition;
@apply inline-flex;
@apply items-center;
}
}
.tippy-svg-arrow {
svg:first-child {
@apply fill-tooltip;
}
svg:last-child {
@apply fill-tooltip;
}
}
}
.tippy-box[data-theme~="popover"] {
@apply bg-popover;
@apply border-solid border-dividerDark;
@apply rounded;
@apply shadow-lg;
.tippy-content {
@apply flex flex-col;
@apply max-h-56;
@apply items-stretch;
@apply overflow-y-auto;
@apply text-secondary text-body;
@apply p-2;
@apply leading-normal;
@apply focus:outline-none;
scroll-behavior: smooth;
}
.tippy-svg-arrow {
svg:first-child {
@apply fill-dividerDark;
}
svg:last-child {
@apply fill-popover;
}
}
}
[data-v-tippy] {
@apply flex flex-1;
}
@@ -246,243 +102,6 @@ hr {
@apply focus-visible:border-dividerDark;
}
input,
select,
textarea,
button {
@apply truncate;
@apply transition;
@apply text-body;
@apply leading-body;
@apply focus:outline-none;
@apply disabled:cursor-not-allowed;
}
.input[type="file"],
.input[type="radio"],
#installPWA {
@apply hidden;
}
.floating-input ~ label {
@apply absolute;
@apply px-2 py-0.5;
@apply m-2;
@apply rounded;
@apply transition;
@apply origin-top-left;
}
.floating-input:focus-within ~ label,
.floating-input:not(:placeholder-shown) ~ label {
@apply bg-primary;
@apply transform;
@apply origin-top-left;
@apply scale-75;
@apply translate-x-1 -translate-y-4;
}
.floating-input:focus-within ~ label {
@apply text-secondaryDark;
}
.floating-input ~ .end-actions {
@apply absolute;
@apply right-0.2;
@apply inset-y-0;
@apply flex;
@apply items-center;
}
.floating-input:has(~ .end-actions) {
@apply pr-12;
}
pre.ace_editor {
@apply font-mono;
@apply resize-none;
@apply z-0;
}
.select {
@apply appearance-none;
@apply cursor-pointer;
&::-ms-expand {
@apply hidden;
}
}
.select-wrapper {
@apply flex flex-1;
@apply relative;
@apply after:absolute;
@apply after:flex;
@apply after:inset-y-0;
@apply after:items-center;
@apply after:justify-center;
@apply after:pointer-events-none;
@apply after:font-icon;
@apply after:text-secondaryLight;
@apply after:right-3;
@apply after:content-["\e313"];
}
.info-response {
@apply text-pink-500;
}
.success-response {
@apply text-green-500;
}
.redir-response {
@apply text-yellow-500;
}
.cl-error-response {
@apply text-red-500;
}
.sv-error-response {
@apply text-red-600;
}
.missing-data-response {
@apply text-secondaryLight;
}
.toasted-container {
@apply max-w-md;
.toasted {
&.toasted-primary {
@apply px-4 py-2;
@apply bg-tooltip;
@apply border-secondaryDark;
@apply text-primary text-body;
@apply justify-between;
@apply shadow-lg;
@apply font-semibold;
@apply transition;
@apply leading-body;
@apply sm:rounded;
@apply sm:border;
.action {
@apply relative;
@apply flex flex-shrink-0;
@apply text-body;
@apply px-4;
@apply my-1;
@apply ml-auto;
@apply normal-case;
@apply font-semibold;
@apply leading-body;
@apply tracking-normal;
@apply rounded;
@apply last:ml-4;
@apply sm:ml-8;
@apply before:absolute;
@apply before:bg-current;
@apply before:opacity-10;
@apply before:inset-0;
@apply before:transition;
@apply before:content-DEFAULT;
@apply hover:no-underline;
@apply hover:before:opacity-20;
}
}
&.info {
@apply bg-accent;
@apply text-accentContrast;
@apply border-accentDark;
}
&.error {
@apply bg-red-200;
@apply text-red-800;
@apply border-red-400;
}
&.success {
@apply bg-green-200;
@apply text-green-800;
@apply border-green-400;
}
}
}
.smart-splitter .splitpanes__splitter {
@apply relative;
@apply before:absolute;
@apply before:inset-0;
@apply before:bg-accentLight;
@apply before:opacity-0;
@apply before:z-20;
@apply before:transition;
@apply before:content-DEFAULT;
@apply hover:before:opacity-100;
}
.no-splitter .splitpanes__splitter {
@apply relative;
}
.smart-splitter.splitpanes--vertical > .splitpanes__splitter {
@apply w-0;
@apply before:-left-0.5;
@apply before:-right-0.5;
@apply before:h-full;
@apply bg-divider;
}
.smart-splitter.splitpanes--horizontal > .splitpanes__splitter {
@apply h-0;
@apply before:-top-0.5;
@apply before:-bottom-0.5;
@apply before:w-full;
@apply bg-divider;
}
.no-splitter.splitpanes--vertical > .splitpanes__splitter {
@apply w-0;
@apply pointer-events-none;
@apply bg-dividerLight;
}
.no-splitter.splitpanes--horizontal > .splitpanes__splitter {
@apply h-0;
@apply pointer-events-none;
@apply bg-dividerLight;
}
.cm-focused {
@apply select-auto;
@apply outline-none #{!important};
.cm-activeLine {
@apply bg-primaryLight;
}
.cm-activeLineGutter {
@apply bg-primaryDark;
}
}
.cm-editor {
.cm-line::selection {
@apply bg-accentDark #{!important};
@apply text-accentContrast #{!important};
}
.cm-line ::selection {
@apply bg-accentDark #{!important};
@apply text-accentContrast #{!important};
}
}
.shortcut-key {
@apply inline-flex;
@apply font-sans;
@@ -499,62 +118,3 @@ pre.ace_editor {
@apply shadow-sm;
@apply <sm:hidden;
}
.capitalize-first {
@apply first-letter:capitalize;
}
details {
@apply select-none;
}
details summary::-webkit-details-marker {
@apply hidden;
}
details summary .indicator {
@apply transition;
}
details[open] summary .indicator {
@apply transform;
@apply rotate-90;
}
@media (max-width: 767px) {
main {
margin-bottom: env(safe-area-inset-bottom);
}
}
.env-highlight {
@apply text-accentContrast;
&.env-found {
@apply bg-accentDark;
@apply hover:bg-accent;
}
&.env-not-found {
@apply bg-red-500;
@apply hover:bg-red-600;
}
}
#nprogress .bar {
@apply bg-accent #{!important};
}
.color-picker[type="color"] {
@apply appearance-none;
}
.color-picker[type="color"]::-webkit-color-swatch-wrapper {
@apply rounded;
@apply p-0;
}
.color-picker[type="color"]::-webkit-color-swatch {
@apply rounded;
@apply border-0;
}

View File

@@ -1,37 +0,0 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
ButtonPrimary: typeof import('./components/button/Primary.vue')['default']
ButtonSecondary: typeof import('./components/button/Secondary.vue')['default']
IconLucideLoader: typeof import('~icons/lucide/loader')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SmartAnchor: typeof import('./components/smart/Anchor.vue')['default']
SmartAutoComplete: typeof import('./components/smart/AutoComplete.vue')['default']
SmartCheckbox: typeof import('./components/smart/Checkbox.vue')['default']
SmartConfirmModal: typeof import('./components/smart/ConfirmModal.vue')['default']
SmartExpand: typeof import('./components/smart/Expand.vue')['default']
SmartFileChip: typeof import('./components/smart/FileChip.vue')['default']
SmartIntersection: typeof import('./components/smart/Intersection.vue')['default']
SmartItem: typeof import('./components/smart/Item.vue')['default']
SmartLink: typeof import('./components/smart/Link.vue')['default']
SmartModal: typeof import('./components/smart/Modal.vue')['default']
SmartProgressRing: typeof import('./components/smart/ProgressRing.vue')['default']
SmartRadio: typeof import('./components/smart/Radio.vue')['default']
SmartRadioGroup: typeof import('./components/smart/RadioGroup.vue')['default']
SmartSlideOver: typeof import('./components/smart/SlideOver.vue')['default']
SmartSpinner: typeof import('./components/smart/Spinner.vue')['default']
SmartTab: typeof import('./components/smart/Tab.vue')['default']
SmartTabs: typeof import('./components/smart/Tabs.vue')['default']
SmartToggle: typeof import('./components/smart/Toggle.vue')['default']
SmartWindow: typeof import('./components/smart/Window.vue')['default']
SmartWindows: typeof import('./components/smart/Windows.vue')['default']
}
}

View File

@@ -1,7 +1,5 @@
<template>
<SmartLink
:to="to"
:blank="blank"
<HoppSmartLink :to="to" :blank="blank"
class="relative inline-flex items-center justify-center py-2 font-bold transition focus:outline-none focus-visible:bg-accentDark"
:class="[
color
@@ -21,45 +19,29 @@
'border border-accent hover:border-accentDark focus-visible:border-accentDark':
outline,
},
]"
:disabled="disabled"
:tabindex="loading ? '-1' : '0'"
role="button"
>
<span
class="inline-flex items-center justify-center whitespace-nowrap"
:class="[{ 'flex-row-reverse': reverse }, { 'opacity-50': loading }]"
>
<component
:is="icon"
v-if="icon"
class="svg-icons"
:class="[
{ '!text-2xl': large },
label ? (reverse ? 'ml-2' : 'mr-2') : '',
]"
/>
]" :disabled="disabled" :tabindex="loading ? '-1' : '0'" role="button">
<span class="inline-flex items-center justify-center whitespace-nowrap"
:class="[{ 'flex-row-reverse': reverse }, { 'opacity-50': loading }]">
<component :is="icon" v-if="icon" class="svg-icons" :class="[
{ '!text-2xl': large },
label ? (reverse ? 'ml-2' : 'mr-2') : '',
]" />
{{ label }}
<div v-if="shortcut.length" class="<sm:hidden">
<kbd
v-for="(key, index) in shortcut"
:key="`key-${index}`"
class="shortcut-key !bg-accentDark !border-accentLight"
>
<kbd v-for="(key, index) in shortcut" :key="`key-${index}`"
class="shortcut-key !bg-accentDark !border-accentLight">
{{ key }}
</kbd>
</div>
</span>
<span
v-if="loading"
class="absolute inset-0 flex items-center justify-center"
>
<SmartSpinner />
<span v-if="loading" class="absolute inset-0 flex items-center justify-center">
<HoppSmartSpinner />
</span>
</SmartLink>
</HoppSmartLink>
</template>
<script setup lang="ts">
import { HoppSmartLink, HoppSmartSpinner } from "../smart"
import type { Component } from "vue"
interface Props {
@@ -80,6 +62,7 @@ interface Props {
outline?: boolean
shortcut?: string[]
}
withDefaults(defineProps<Props>(), {
to: "",
exact: true,

View File

@@ -1,8 +1,5 @@
<template>
<SmartLink
:to="to"
:exact="exact"
:blank="blank"
<HoppSmartLink :to="to" :exact="exact" :blank="blank"
class="inline-flex items-center justify-center py-2 font-semibold transition whitespace-nowrap focus:outline-none"
:class="[
color
@@ -22,41 +19,26 @@
'bg-primaryLight hover:bg-primaryDark focus-visible:bg-primaryDark':
filled,
},
]"
:disabled="disabled"
:tabindex="loading ? '-1' : '0'"
role="button"
>
<span
v-if="!loading"
class="inline-flex items-center justify-center whitespace-nowrap"
:class="{ 'flex-row-reverse': reverse }"
>
<component
:is="icon"
v-if="icon"
class="svg-icons"
:class="[
{ '!text-2xl': large },
label ? (reverse ? 'ml-2' : 'mr-2') : '',
]"
/>
]" :disabled="disabled" :tabindex="loading ? '-1' : '0'" role="button">
<span v-if="!loading" class="inline-flex items-center justify-center whitespace-nowrap"
:class="{ 'flex-row-reverse': reverse }">
<component :is="icon" v-if="icon" class="svg-icons" :class="[
{ '!text-2xl': large },
label ? (reverse ? 'ml-2' : 'mr-2') : '',
]" />
{{ label }}
<div v-if="shortcut.length" class="<sm:hidden">
<kbd
v-for="(key, index) in shortcut"
:key="`key-${index}`"
class="shortcut-key !bg-inherit"
>
<kbd v-for="(key, index) in shortcut" :key="`key-${index}`" class="shortcut-key !bg-inherit">
{{ key }}
</kbd>
</div>
</span>
<SmartSpinner v-else />
</SmartLink>
<HoppSmartSpinner v-else />
</HoppSmartLink>
</template>
<script setup lang="ts">
import { HoppSmartLink, HoppSmartSpinner } from "../smart";
import type { Component } from "vue"
interface Props {

View File

@@ -0,0 +1,2 @@
export { default as HoppButtonPrimary } from "./Primary.vue"
export { default as HoppButtonSecondary } from "./Secondary.vue"

View File

@@ -1,47 +1,2 @@
import Primary from "./button/Primary.vue"
import Secondary from "./button/Secondary.vue"
import Anchor from "./smart/Anchor.vue"
import AutoComplete from "./smart/AutoComplete.vue"
import Checkbox from "./smart/Checkbox.vue"
import ConfirmModal from "./smart/ConfirmModal.vue"
import Expand from "./smart/Expand.vue"
import FileChip from "./smart/FileChip.vue"
import Intersection from "./smart/Intersection.vue"
import Item from "./smart/Item.vue"
import Link from "./smart/Link.vue"
import Modal from "./smart/Modal.vue"
import ProgressRing from "./smart/ProgressRing.vue"
import Radio from "./smart/Radio.vue"
import RadioGroup from "./smart/RadioGroup.vue"
import SlideOver from "./smart/SlideOver.vue"
import Spinner from "./smart/Spinner.vue"
import Tab from "./smart/Tab.vue"
import Tabs from "./smart/Tabs.vue"
import Toggle from "./smart/Toggle.vue"
import Window from "./smart/Window.vue"
import Windows from "./smart/Windows.vue"
export default {
Primary,
Secondary,
Anchor,
AutoComplete,
Checkbox,
ConfirmModal,
Expand,
FileChip,
Intersection,
Item,
Link,
Modal,
ProgressRing,
Radio,
RadioGroup,
SlideOver,
Spinner,
Tab,
Tabs,
Toggle,
Window,
Windows,
}
export * from "./button"
export * from "./smart"

View File

@@ -1,33 +1,25 @@
<template>
<SmartLink
:to="to"
:exact="exact"
:blank="blank"
class="inline-flex items-center justify-center focus:outline-none"
<HoppSmartLink :to="to" :exact="exact" :blank="blank" class="inline-flex items-center justify-center focus:outline-none"
:class="[
color
? `text-${color}-500 hover:text-${color}-600 focus-visible:text-${color}-600`
: 'hover:text-secondaryDark focus-visible:text-secondaryDark',
{ 'opacity-75 cursor-not-allowed': disabled },
{ 'flex-row-reverse': reverse },
]"
:disabled="disabled"
tabindex="0"
>
<component
:is="icon"
v-if="icon"
class="svg-icons"
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
/>
]" :disabled="disabled" tabindex="0">
<component :is="icon" v-if="icon" class="svg-icons" :class="label ? (reverse ? 'ml-2' : 'mr-2') : ''" />
{{ label }}
</SmartLink>
</HoppSmartLink>
</template>
<script lang="ts">
import HoppSmartLink from "./Link.vue";
import { Component, defineComponent, PropType } from "vue"
export default defineComponent({
components: {
HoppSmartLink
},
props: {
to: {
type: String,

View File

@@ -1,36 +1,17 @@
<template>
<div class="autocomplete-wrapper">
<input
ref="acInput"
:value="text"
type="text"
autocomplete="off"
:placeholder="placeholder"
:spellcheck="spellcheck"
:autocapitalize="autocapitalize"
:class="styles"
@input.stop="
<input ref="acInput" :value="text" type="text" autocomplete="off" :placeholder="placeholder" :spellcheck="spellcheck"
:autocapitalize="autocapitalize" :class="styles" @input.stop="
(e) => {
$emit('input', e.target.value)
updateSuggestions(e)
}
"
@keyup="updateSuggestions"
@click="updateSuggestions"
@keydown="handleKeystroke"
@change="$emit('change', $event)"
/>
<ul
v-if="suggestions.length > 0 && suggestionsVisible"
class="suggestions"
:style="{ transform: `translate(${suggestionsOffsetLeft}px, 0)` }"
>
<li
v-for="(suggestion, index) in suggestions"
:key="`suggestion-${index}`"
:class="{ active: currentSuggestionIndex === index }"
@click.prevent="forceSuggestion(suggestion)"
>
" @keyup="updateSuggestions" @click="updateSuggestions" @keydown="handleKeystroke"
@change="$emit('change', $event)" />
<ul v-if="suggestions.length > 0 && suggestionsVisible" class="suggestions"
:style="{ transform: `translate(${suggestionsOffsetLeft}px, 0)` }">
<li v-for="(suggestion, index) in suggestions" :key="`suggestion-${index}`"
:class="{ active: currentSuggestionIndex === index }" @click.prevent="forceSuggestion(suggestion)">
{{ suggestion }}
</li>
</ul>
@@ -182,7 +163,7 @@ export default defineComponent({
case "Tab": {
const activeSuggestion =
this.suggestions[
this.currentSuggestionIndex >= 0 ? this.currentSuggestionIndex : 0
this.currentSuggestionIndex >= 0 ? this.currentSuggestionIndex : 0
]
if (!activeSuggestion) {
@@ -205,7 +186,7 @@ export default defineComponent({
@apply relative;
@apply contents;
input:focus + ul.suggestions,
input:focus+ul.suggestions,
ul.suggestions:hover {
@apply block;
}

View File

@@ -1,12 +1,6 @@
<template>
<SmartModal
v-if="show"
dialog
:title="confirm ?? t?.('modal.confirm') ?? 'Confirm'"
role="dialog"
aria-modal="true"
@close="hideModal"
>
<HoppSmartModal v-if="show" dialog :title="confirm ?? t?.('modal.confirm') ?? 'Confirm'" role="dialog" aria-modal="true"
@close="hideModal">
<template #body>
<div class="flex flex-col items-center justify-center">
{{ title }}
@@ -14,27 +8,19 @@
</template>
<template #footer>
<span class="flex space-x-2">
<ButtonPrimary
v-focus
:label="yes ?? t?.('action.yes') ?? 'Yes'"
:loading="!!loadingState"
outline
@click="resolve"
/>
<ButtonSecondary
:label="no ?? t?.('action.no') ?? 'No'"
filled
outline
@click="hideModal"
/>
<HoppButtonPrimary v-focus :label="yes ?? t?.('action.yes') ?? 'Yes'" :loading="!!loadingState" outline
@click="resolve" />
<HoppButtonSecondary :label="no ?? t?.('action.no') ?? 'No'" filled outline @click="hideModal" />
</span>
</template>
</SmartModal>
</HoppSmartModal>
</template>
<script setup lang="ts">
import { inject } from "vue"
import { HoppUIPluginOptions, HOPP_UI_OPTIONS } from "./../../index"
import { HoppButtonPrimary, HoppButtonSecondary } from "../button"
import { HoppSmartModal } from "."
import { HoppUIPluginOptions, HOPP_UI_OPTIONS } from "./../../plugin"
const { t } = inject<HoppUIPluginOptions>(HOPP_UI_OPTIONS) ?? {}

View File

@@ -1,32 +1,22 @@
<template>
<div
class="relative flex flex-col space-y-2 overflow-hidden"
:class="expand ? 'h-full' : 'max-h-32'"
>
<div class="relative flex flex-col space-y-2 overflow-hidden" :class="expand ? 'h-full' : 'max-h-32'">
<slot name="body"></slot>
<div
class="sticky inset-x-0 bottom-0 flex items-center justify-center flex-shrink-0 overflow-x-auto"
>
<ButtonSecondary
:icon="expand ? IconChevronUp : IconChevronDown"
:label="
expand
? less ?? t?.('action.less') ?? 'Less'
: more ?? t?.('action.more') ?? 'More'
"
filled
rounded
@click="expand = !expand"
/>
<div class="sticky inset-x-0 bottom-0 flex items-center justify-center flex-shrink-0 overflow-x-auto">
<HoppButtonSecondary :icon="expand ? IconChevronUp : IconChevronDown" :label="
expand
? less ?? t?.('action.less') ?? 'Less'
: more ?? t?.('action.more') ?? 'More'
" filled rounded @click="expand = !expand" />
</div>
</div>
</template>
<script setup lang="ts">
import { HoppButtonSecondary } from "../button"
import IconChevronUp from "~icons/lucide/chevron-up"
import IconChevronDown from "~icons/lucide/chevron-down"
import { inject, ref } from "vue"
import { HoppUIPluginOptions, HOPP_UI_OPTIONS } from "./../../index"
import { HoppUIPluginOptions, HOPP_UI_OPTIONS } from "./../../plugin"
const { t } = inject<HoppUIPluginOptions>(HOPP_UI_OPTIONS) ?? {}

View File

@@ -1,8 +1,5 @@
<template>
<SmartLink
:to="to"
:exact="exact"
:blank="blank"
<HoppSmartLink :to="to" :exact="exact" :blank="blank"
class="inline-flex items-center flex-shrink-0 px-4 py-2 rounded transition hover:bg-primaryDark hover:text-secondaryDark focus:outline-none focus-visible:bg-primaryDark focus-visible:text-secondaryDark"
:class="[
{ 'opacity-75 cursor-not-allowed': disabled },
@@ -13,31 +10,15 @@
'border border-divider hover:border-dividerDark focus-visible:border-dividerDark':
outline,
},
]"
:disabled="disabled"
:tabindex="loading ? '-1' : '0'"
role="menuitem"
>
<span
v-if="!loading"
class="inline-flex items-center"
:class="{ 'self-start': !!infoIcon }"
>
<component
:is="icon"
v-if="icon"
class="opacity-75 svg-icons"
:class="[
label ? (reverse ? 'ml-4' : 'mr-4') : '',
{ 'text-accent': active },
]"
/>
]" :disabled="disabled" :tabindex="loading ? '-1' : '0'" role="menuitem">
<span v-if="!loading" class="inline-flex items-center" :class="{ 'self-start': !!infoIcon }">
<component :is="icon" v-if="icon" class="opacity-75 svg-icons" :class="[
label ? (reverse ? 'ml-4' : 'mr-4') : '',
{ 'text-accent': active },
]" />
</span>
<SmartSpinner v-else class="mr-4 text-secondaryDark" />
<div
class="inline-flex items-start flex-1 truncate"
:class="{ 'flex-col': description }"
>
<HoppSmartSpinner v-else class="mr-4 text-secondaryDark" />
<div class="inline-flex items-start flex-1 truncate" :class="{ 'flex-col': description }">
<div class="font-semibold truncate">
{{ label }}
</div>
@@ -45,28 +26,26 @@
{{ description }}
</p>
</div>
<component
:is="infoIcon"
v-if="infoIcon"
class="items-center self-center ml-4 svg-icons"
:class="{ 'text-accent': activeInfoIcon }"
/>
<component :is="infoIcon" v-if="infoIcon" class="items-center self-center ml-4 svg-icons"
:class="{ 'text-accent': activeInfoIcon }" />
<div v-if="shortcut.length" class="ml-4 <sm:hidden font-medium">
<kbd
v-for="(key, index) in shortcut"
:key="`key-${index}`"
class="-mr-2 shortcut-key"
>
<kbd v-for="(key, index) in shortcut" :key="`key-${index}`" class="-mr-2 shortcut-key">
{{ key }}
</kbd>
</div>
</SmartLink>
</HoppSmartLink>
</template>
<script lang="ts">
import HoppSmartLink from "./Link.vue"
import HoppSmartSpinner from "./Spinner.vue"
import { defineComponent } from "vue"
export default defineComponent({
components: {
HoppSmartLink,
HoppSmartSpinner
},
props: {
to: {
type: String,

View File

@@ -1,30 +1,12 @@
<template>
<button
v-if="renderedTag === 'BUTTON'"
aria-label="button"
role="button"
v-bind="$attrs"
>
<button v-if="renderedTag === 'BUTTON'" aria-label="button" role="button" v-bind="$attrs">
<slot></slot>
</button>
<a
v-else-if="renderedTag === 'ANCHOR' && !blank"
aria-label="Link"
:href="to"
role="link"
v-bind="updatedAttrs"
>
<a v-else-if="renderedTag === 'ANCHOR' && !blank" aria-label="Link" :href="to" role="link" v-bind="updatedAttrs">
<slot></slot>
</a>
<a
v-else-if="renderedTag === 'ANCHOR' && blank"
aria-label="Link"
:href="to"
role="link"
target="_blank"
rel="noopener"
v-bind="updatedAttrs"
>
<a v-else-if="renderedTag === 'ANCHOR' && blank" aria-label="Link" :href="to" role="link" target="_blank" rel="noopener"
v-bind="updatedAttrs">
<slot></slot>
</a>
<RouterLink v-else :to="to" v-bind="updatedAttrs">
@@ -33,6 +15,7 @@
</template>
<script lang="ts">
import { RouterLink } from "vue-router"
/**
* for preventing the automatic binding of $attrs.
* we are manually binding $attrs or updatedAttrs.

View File

@@ -101,7 +101,7 @@ import {
onBeforeUnmount,
inject,
} from "vue"
import { HoppUIPluginOptions, HOPP_UI_OPTIONS } from "./../../index"
import { HoppUIPluginOptions, HOPP_UI_OPTIONS } from "./../../plugin"
const { t, onModalOpen, onModalClose } =
inject<HoppUIPluginOptions>(HOPP_UI_OPTIONS) ?? {}

View File

@@ -1,15 +1,10 @@
<template>
<SmartItem
:label="label"
:icon="selected ? IconCircleDot : IconCircle"
:active="selected"
role="radio"
:aria-checked="selected"
@click="emit('change', value)"
/>
<HoppSmartItem :label="label" :icon="selected ? IconCircleDot : IconCircle" :active="selected" role="radio"
:aria-checked="selected" @click="emit('change', value)" />
</template>
<script setup lang="ts">
import { HoppSmartItem } from "."
import IconCircleDot from "~icons/lucide/circle-dot"
import IconCircle from "~icons/lucide/circle"

View File

@@ -1,17 +1,13 @@
<template>
<div class="flex flex-col">
<SmartRadio
v-for="(radio, index) in radios"
:key="`radio-${index}`"
:value="radio.value"
:label="radio.label"
:selected="modelValue === radio.value"
@change="emit('update:modelValue', radio.value)"
/>
<HoppSmartRadio v-for="(radio, index) in radios" :key="`radio-${index}`" :value="radio.value" :label="radio.label"
:selected="modelValue === radio.value" @change="emit('update:modelValue', radio.value)" />
</div>
</template>
<script setup lang="ts">
import { HoppSmartRadio } from '.';
const emit = defineEmits<{
(e: "update:modelValue", value: string): void
}>()

View File

@@ -1,30 +1,18 @@
<template>
<div>
<Transition name="fade" appear>
<div
v-if="show"
class="fixed inset-0 z-20 transition-opacity"
@keydown.esc="close()"
>
<div
class="absolute inset-0 bg-primaryLight opacity-90 focus:outline-none"
tabindex="0"
@click="close()"
></div>
<div v-if="show" class="fixed inset-0 z-20 transition-opacity" @keydown.esc="close()">
<div class="absolute inset-0 bg-primaryLight opacity-90 focus:outline-none" tabindex="0" @click="close()"></div>
</div>
</Transition>
<Transition name="slide" appear>
<aside
v-if="show"
class="fixed top-0 right-0 z-30 flex flex-col h-full max-w-full overflow-auto border-l shadow-xl border-dividerDark bg-primary w-96"
>
<div
class="flex items-center justify-between p-2 border-b border-dividerLight"
>
<aside v-if="show"
class="fixed top-0 right-0 z-30 flex flex-col h-full max-w-full overflow-auto border-l shadow-xl border-dividerDark bg-primary w-96">
<div class="flex items-center justify-between p-2 border-b border-dividerLight">
<h3 class="ml-4 heading">{{ title }}</h3>
<span class="flex items-center">
<kbd class="mr-2 shortcut-key">ESC</kbd>
<ButtonSecondary :icon="IconX" @click="close()" />
<HoppButtonSecondary :icon="IconX" @click="close()" />
</span>
</div>
<slot name="content"></slot>
@@ -34,6 +22,7 @@
</template>
<script setup lang="ts">
import { HoppButtonSecondary } from "../button"
import { onMounted, watch } from "vue"
import IconX from "~icons/lucide/x"

View File

@@ -1,9 +1,7 @@
<template>
<icon-lucide-loader class="animate-spin svg-icons" />
<IconLucideLoader class="animate-spin svg-icons" />
</template>
<script lang="ts">
import { defineComponent } from "vue"
export default defineComponent({})
<script setup lang="ts">
import IconLucideLoader from "~icons/lucide/loader"
</script>

View File

@@ -1,71 +1,37 @@
<template>
<div class="flex flex-col flex-1 h-auto overflow-y-hidden flex-nowrap">
<div
class="relative sticky top-0 z-10 flex-shrink-0 overflow-x-auto tabs bg-primaryLight"
>
<div class="relative sticky top-0 z-10 flex-shrink-0 overflow-x-auto tabs bg-primaryLight">
<div class="flex flex-1 flex-shrink-0 w-0 overflow-x-auto">
<div class="flex justify-between divide-x divide-dividerLight">
<div class="flex">
<draggable
v-bind="dragOptions"
:list="tabEntries"
:style="tabStyles"
:item-key="'window-'"
class="flex flex-shrink-0 overflow-x-auto transition divide-x divide-dividerLight"
@sort="sortTabs"
>
<draggable v-bind="dragOptions" :list="tabEntries" :style="tabStyles" :item-key="'window-'"
class="flex flex-shrink-0 overflow-x-auto transition divide-x divide-dividerLight" @sort="sortTabs">
<template #item="{ element: [tabID, tabMeta] }">
<button
:key="`removable-tab-${tabID}`"
class="tab"
:class="[{ active: modelValue === tabID }]"
:aria-label="tabMeta.label || ''"
role="button"
@keyup.enter="selectTab(tabID)"
@click="selectTab(tabID)"
>
<button :key="`removable-tab-${tabID}`" class="tab" :class="[{ active: modelValue === tabID }]"
:aria-label="tabMeta.label || ''" role="button" @keyup.enter="selectTab(tabID)"
@click="selectTab(tabID)">
<div class="flex items-stretch truncate">
<span
v-if="tabMeta.icon"
class="flex items-center justify-center mx-4 cursor-pointer"
>
<span v-if="tabMeta.icon" class="flex items-center justify-center mx-4 cursor-pointer">
<component :is="tabMeta.icon" class="w-4 h-4 svg-icons" />
</span>
<span class="truncate">
{{ tabMeta.label }}
</span>
</div>
<ButtonSecondary
v-tippy="{ theme: 'tooltip', delay: [500, 20] }"
:icon="IconX"
:style="{
visibility: tabMeta.isRemovable ? 'visible' : 'hidden',
}"
:title="closeText ?? t?.('action.close') ?? 'Close'"
:class="[{ active: modelValue === tabID }, 'close']"
class="mx-2 !p-0.5"
@click.stop="emit('removeTab', tabID)"
/>
<HoppButtonSecondary v-tippy="{ theme: 'tooltip', delay: [500, 20] }" :icon="IconX" :style="{
visibility: tabMeta.isRemovable ? 'visible' : 'hidden',
}" :title="closeText ?? t?.('action.close') ?? 'Close'"
:class="[{ active: modelValue === tabID }, 'close']" class="mx-2 !p-0.5"
@click.stop="emit('removeTab', tabID)" />
</button>
</template>
</draggable>
</div>
<div
class="sticky right-0 flex items-center justify-center flex-shrink-0 overflow-x-auto z-8"
>
<div class="sticky right-0 flex items-center justify-center flex-shrink-0 overflow-x-auto z-8">
<slot name="actions">
<span
v-if="canAddNewTab"
class="flex items-center justify-center px-2 py-1.5 bg-primaryLight z-8"
>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="newText ?? t?.('action.new') ?? 'New'"
:icon="IconPlus"
class="rounded !p-1"
filled
@click="addTab"
/>
<span v-if="canAddNewTab" class="flex items-center justify-center px-2 py-1.5 bg-primaryLight z-8">
<ButtonSecondary v-tippy="{ theme: 'tooltip' }" :title="newText ?? t?.('action.new') ?? 'New'"
:icon="IconPlus" class="rounded !p-1" filled @click="addTab" />
</span>
</slot>
</div>
@@ -79,15 +45,15 @@
</template>
<script setup lang="ts">
import { HoppButtonSecondary } from "../button"
import IconPlus from "~icons/lucide/plus"
import IconX from "~icons/lucide/x"
import { pipe } from "fp-ts/function"
import { not } from "fp-ts/Predicate"
import * as A from "fp-ts/Array"
import * as O from "fp-ts/Option"
import { ref, ComputedRef, computed, provide, inject } from "vue"
import type { Slot } from "vue"
import draggable from "vuedraggable"
import { ref, ComputedRef, computed, provide, inject, Slot } from "vue"
import draggable from "vuedraggable-es"
import { HoppUIPluginOptions, HOPP_UI_OPTIONS } from "./../../index"
export type TabMeta = {
@@ -213,13 +179,13 @@ const addTab = () => {
@apply whitespace-nowrap;
@apply overflow-auto;
@apply flex-shrink-0;
@apply after:absolute;
@apply after:inset-x-0;
@apply after:bottom-0;
@apply after:bg-dividerLight;
@apply after:z-10;
@apply after:h-0.25;
@apply after:content-DEFAULT;
@apply after: absolute;
@apply after: inset-x-0;
@apply after: bottom-0;
@apply after: bg-dividerLight;
@apply after: z-10;
@apply after: h-0.25;
@apply after: content-DEFAULT;
.tab {
@apply relative;
@@ -232,23 +198,23 @@ const addTab = () => {
@apply items-center;
@apply justify-between;
@apply text-secondaryLight;
@apply hover:bg-primaryDark;
@apply hover:text-secondary;
@apply focus-visible:text-secondaryDark;
@apply before:absolute;
@apply before:left-0;
@apply before:right-0;
@apply before:top-0;
@apply before:bg-transparent;
@apply before:z-2;
@apply before:h-0.5;
@apply before:content-DEFAULT;
@apply hover: bg-primaryDark;
@apply hover: text-secondary;
@apply focus-visible: text-secondaryDark;
@apply before: absolute;
@apply before: left-0;
@apply before: right-0;
@apply before: top-0;
@apply before: bg-transparent;
@apply before: z-2;
@apply before: h-0.5;
@apply before: content-DEFAULT;
@apply focus: before: bg-divider;
&.active {
@apply text-secondaryDark;
@apply bg-primary;
@apply before:bg-accent;
@apply before: bg-accent;
}
.close {

View File

@@ -0,0 +1,20 @@
export { default as HoppSmartAnchor } from "./Anchor.vue"
export { default as HoppSmartAutoComplete } from "./AutoComplete.vue"
export { default as HoppSmartCheckbox } from "./Checkbox.vue"
export { default as HoppSmartConfirmModal } from "./ConfirmModal.vue"
export { default as HoppSmartExpand } from "./Expand.vue"
export { default as HoppSmartFileChip } from "./FileChip.vue"
export { default as HoppSmartIntersection } from "./Intersection.vue"
export { default as HoppSmartItem } from "./Item.vue"
export { default as HoppSmartLink } from "./Link.vue"
export { default as HoppSmartModal } from "./Modal.vue"
export { default as HoppSmartProgressRing } from "./ProgressRing.vue"
export { default as HoppSmartRadio } from "./Radio.vue"
export { default as HoppSmartRadioGroup } from "./RadioGroup.vue"
export { default as HoppSmartSlideOver } from "./SlideOver.vue"
export { default as HoppSmartSpinner } from "./Spinner.vue"
export { default as HoppSmartTab } from "./Tab.vue"
export { default as HoppSmartTabs } from "./Tabs.vue"
export { default as HoppSmartToggle } from "./Toggle.vue"
export { default as HoppSmartWindow } from "./Window.vue"
export { default as HoppSmartWindows } from "./Windows.vue"

View File

@@ -1,33 +1,2 @@
import { Plugin } from "vue"
import "./assets/scss/styles.scss"
import "virtual:windi.css"
/**
@constant HOPP_UI_OPTIONS
@type {string}
A constant representing the key for storing HoppUI plugin options in the global context.
*/
export const HOPP_UI_OPTIONS = "HOPP_UI_OPTIONS"
/**
@typedef {Object} HoppUIPluginOptions
@property {Function} [t] - A function for handling translations for the plugin.
@property {Function} [onModalOpen] - A callback function that is called when a modal is opened.
@property {Function} [onModalClose] - A callback function that is called when a modal is closed.
*/
export type HoppUIPluginOptions = {
t?: (key: string) => string
onModalOpen?: () => void
onModalClose?: () => void
}
const plugin: Plugin = {
install(app, options: HoppUIPluginOptions = {}) {
app.provide(HOPP_UI_OPTIONS, options)
},
}
export default plugin
export * from "./components"
export * from "./plugin"

View File

@@ -0,0 +1,30 @@
import type { Plugin, App } from "vue"
import "./assets/scss/styles.scss"
import "virtual:windi.css"
/**
@constant HOPP_UI_OPTIONS
A constant representing the key for storing HoppUI plugin options in the global context.
*/
export const HOPP_UI_OPTIONS = "HOPP_UI_OPTIONS"
/**
@typedef {Object} HoppUIPluginOptions
@property [t] - A function for handling translations for the plugin.
@property [onModalOpen] - A callback function that is called when a modal is opened.
@property [onModalClose] - A callback function that is called when a modal is closed.
*/
export type HoppUIPluginOptions = {
t?: (key: string) => string
onModalOpen?: () => void
onModalClose?: () => void
}
export const plugin: Plugin = {
install(app: App, options: HoppUIPluginOptions = {}) {
app.provide(HOPP_UI_OPTIONS, options)
},
}

View File

@@ -2,19 +2,14 @@
<Story title="Anchor">
<div class="text-secondaryLight text-tiny">
By signing in, you are agreeing to our
<SmartAnchor
class="text-red-800 link"
to="https://docs.hoppscotch.io/terms"
blank
label="Terms of Service"
/>
<HoppSmartAnchor class="text-red-800 link" to="https://docs.hoppscotch.io/terms" blank label="Terms of Service" />
and
<SmartAnchor
class="text-red-600 link"
to="https://docs.hoppscotch.io/privacy"
blank
label="Privacy Policy"
/>
<HoppSmartAnchor class="text-red-600 link" to="https://docs.hoppscotch.io/privacy" blank label="Privacy Policy" />
</div>
</Story>
</template>
<script setup lang="ts">
import { HoppSmartAnchor } from "../components/smart"
</script>

View File

@@ -1,28 +1,21 @@
<template>
<Story title="Auto Complete">
<div class="h-[50vh]">
<SmartAutoComplete
placeholder="Select a header"
:source="commonHeaders"
:spellcheck="false"
:value="header[0].key"
autofocus
styles="
bg-transparent
flex
flex-1
py-1
px-4
truncate
"
class="flex-1 !flex"
@input="updateHeader()"
/>
<HoppSmartAutoComplete placeholder="Select a header" :source="commonHeaders" :spellcheck="false"
:value="header[0].key" autofocus styles="
bg-transparent
flex
flex-1
py-1
px-4
truncate
" class="flex-1 !flex" @input="updateHeader()" />
</div>
</Story>
</template>
<script setup lang="ts">
import { HoppSmartAutoComplete } from "../components/smart"
import { ref } from "vue"
type GQLHeader = {

View File

@@ -1,10 +1,14 @@
<template>
<Story title="Button">
<Variant title="Primary">
<ButtonPrimary label="Button" />
<HoppButtonPrimary label="Button" />
</Variant>
<Variant title="Secondary">
<ButtonSecondary label="Button" />
<HoppButtonSecondary label="Button" />
</Variant>
</Story>
</template>
<script setup lang="ts">
import { HoppButtonPrimary, HoppButtonSecondary } from "../components/button"
</script>

View File

@@ -1,12 +1,14 @@
<template>
<Story title="Checkbox">
<Variant title="Single">
<SmartCheckbox :on="on" />
<HoppSmartCheckbox :on="on" />
</Variant>
</Story>
</template>
<script setup lang="ts">
import { HoppSmartCheckbox } from "../components/smart"
import { ref } from "vue"
const on = ref(true)

View File

@@ -1,16 +1,13 @@
<template>
<Story title="Confirm Modal">
<SmartConfirmModal
:show="show"
:title="'Confirm Modal'"
@hide-modal="show = false"
@resolve="resolveConfirmModal"
/>
<HoppSmartConfirmModal :show="show" :title="'Confirm Modal'" @hide-modal="show = false"
@resolve="resolveConfirmModal" />
</Story>
</template>
<script lang="ts" setup>
import { ref } from "vue"
import { HoppSmartConfirmModal } from "../components/smart"
const show = ref(true)

View File

@@ -0,0 +1,20 @@
<template>
<Story title="Expand">
<HoppSmartExpand>
<template #body>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia quas culpa ea nesciunt, magni natus ex numquam perspiciatis,
reprehenderit reiciendis necessitatibus nostrum laudantium illum tempora ducimus! Dignissimos officiis sed nisi.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia quas culpa ea nesciunt, magni natus ex numquam perspiciatis,
reprehenderit reiciendis necessitatibus nostrum laudantium illum tempora ducimus! Dignissimos officiis sed nisi.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia quas culpa ea nesciunt, magni natus ex numquam perspiciatis,
reprehenderit reiciendis necessitatibus nostrum laudantium illum tempora ducimus! Dignissimos officiis sed nisi.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia quas culpa ea nesciunt, magni natus ex numquam perspiciatis,
reprehenderit reiciendis necessitatibus nostrum laudantium illum tempora ducimus! Dignissimos officiis sed nisi.
</template>
</HoppSmartExpand>
</Story>
</template>
<script setup lang="ts">
import { HoppSmartExpand } from "../components/smart"
</script>

View File

@@ -1,7 +1,11 @@
<template>
<Story title="Item">
<Variant title="Single">
<SmartItem :label="'Item'" :active-info-icon="false" />
<HoppSmartItem :label="'Item'" :active-info-icon="false" />
</Variant>
</Story>
</template>
<script setup lang="ts">
import { HoppSmartItem } from "../components/smart"
</script>

View File

@@ -1,17 +1,19 @@
<template>
<Story title="Link">
<Variant title="Text Link">
<SmartLink :to="link" :blank="true"> Click here </SmartLink>
<HoppSmartLink :to="link" :blank="true"> Click here </HoppSmartLink>
</Variant>
<Variant title="Button Link">
<SmartLink :to="link" :blank="true">
<ButtonPrimary label="Click here" />
</SmartLink>
<HoppSmartLink :to="link" :blank="true">
<HoppButtonPrimary label="Click here" />
</HoppSmartLink>
</Variant>
</Story>
</template>
<script setup lang="ts">
import { HoppSmartLink } from "../components/smart"
import { HoppButtonPrimary } from "../components/button"
import { ref } from "vue"
const link = ref("/")

View File

@@ -1,15 +1,11 @@
<template>
<Story title="Modal">
<SmartModal
:show="show"
:title="'Modal Title'"
@hide-modal="show = false"
@resolve="resolveConfirmModal"
/>
<HoppSmartModal :show="show" :title="'Modal Title'" @hide-modal="show = false" @resolve="resolveConfirmModal" />
</Story>
</template>
<script lang="ts" setup>
import { HoppSmartModal } from "../components/smart"
import { ref } from "vue"
const show = ref(true)

View File

@@ -1,15 +1,12 @@
<template>
<Story title="Progress Ring">
<SmartProgressRing
class="mr-2 text-red-500"
:radius="8"
:stroke="1.5"
:progress="(failedTests / totalTests) * 100"
/>
<HoppSmartProgressRing class="mr-2 text-red-500" :radius="8" :stroke="1.5"
:progress="(failedTests / totalTests) * 100" />
</Story>
</template>
<script setup lang="ts">
import { HoppSmartProgressRing } from "../components/smart"
const totalTests = 10
const failedTests = 2
</script>

View File

@@ -1,16 +1,17 @@
<template>
<Story title="Radio">
<Variant title="Single">
<SmartRadio />
<HoppSmartRadio />
</Variant>
<Variant title="Group">
<SmartRadioGroup :radios="radios" :model-value="selected" />
<HoppSmartRadioGroup :radios="radios" :model-value="selected" />
</Variant>
</Story>
</template>
<script setup lang="ts">
import { ref } from "vue"
import { HoppSmartRadio, HoppSmartRadioGroup } from "../components/smart"
const selected = ref("option1")
const radios = [

View File

@@ -1,14 +1,15 @@
<template>
<Story title="Slider Over">
<SmartSlideOver :show="show" :title="'Title'" @close="show = false">
<HoppSmartSlideOver :show="show" :title="'Title'" @close="show = false">
<template #content>
<h1>Content</h1>
</template>
</SmartSlideOver>
</HoppSmartSlideOver>
</Story>
</template>
<script lang="ts" setup>
<script setup lang="ts">
import { HoppSmartSlideOver } from "../components/smart"
import { ref } from "vue"
const show = ref(true)

View File

@@ -1,5 +1,9 @@
<template>
<Story title="Spinner">
<SmartSpinner />
<HoppSmartSpinner />
</Story>
</template>
<script setup lang="ts">
import { HoppSmartSpinner } from "../components/smart"
</script>

View File

@@ -1,19 +1,20 @@
<template>
<Story title="Tab">
<Variant title="Single">
<SmartTabs id="my-tab" v-model="selectedTab" render-inactive-tabs>
<SmartTab id="tab1" label="Tab 1">
<HoppSmartTabs id="my-tab" v-model="selectedTab" render-inactive-tabs>
<HoppSmartTab id="tab1" label="Tab 1">
<h1>Tab 1 content</h1>
</SmartTab>
<SmartTab id="tab2" label="Tab 2">
</HoppSmartTab>
<HoppSmartTab id="tab2" label="Tab 2">
<h1>Tab 2 content</h1>
</SmartTab>
</SmartTabs>
</HoppSmartTab>
</HoppSmartTabs>
</Variant>
</Story>
</template>
<script setup lang="ts">
import { HoppSmartTabs, HoppSmartTab } from "../components/smart"
import { ref } from "vue"
const selectedTab = ref("tab1")

View File

@@ -1,10 +1,11 @@
<template>
<Story title="Toggle">
<SmartToggle :on="on" @change="change"> Turn on </SmartToggle>
<HoppSmartToggle :on="on" @change="change"> Turn on </HoppSmartToggle>
</Story>
</template>
<script setup lang="ts">
import { HoppSmartToggle } from "../components/smart"
import { ref } from "vue"
const on = ref(true)

View File

@@ -1,27 +1,18 @@
<template>
<Story title="Window">
<Variant title="Single">
<SmartWindows
:id="'my-window'"
v-model="selectedWindow"
@add-tab="openNewTab"
@remove-tab="removeTab"
@sort="sortTabs"
>
<SmartWindow
v-for="window in tabs"
:id="window.id"
:key="'tab_' + window.id"
:label="window.name"
:is-removable="window.removable"
>
</SmartWindow>
</SmartWindows>
<HoppSmartWindows :id="'my-window'" v-model="selectedWindow" @add-tab="openNewTab" @remove-tab="removeTab"
@sort="sortTabs">
<HoppSmartWindow v-for="window in tabs" :id="window.id" :key="'tab_' + window.id" :label="window.name"
:is-removable="window.removable">
</HoppSmartWindow>
</HoppSmartWindows>
</Variant>
</Story>
</template>
<script setup lang="ts">
import { HoppSmartWindows, HoppSmartWindow } from "../components/smart"
import { ref } from "vue"
const selectedWindow = ref("window1")

View File

@@ -1,6 +1,7 @@
{
"compilerOptions": {
"target": "ESNext",
"allowJs": true,
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
@@ -10,17 +11,34 @@
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"lib": [
"ESNext",
"DOM"
],
"skipLibCheck": true,
"noUnusedLocals": true,
"paths": {
"~/*": ["./src/*"],
"@composables/*": ["./src/composables/*"],
"@components/*": ["./src/components/*"],
"@helpers/*": ["./src/helpers/*"],
"@modules/*": ["./src/modules/*"],
"@workers/*": ["./src/workers/*"],
"@functional/*": ["./src/helpers/functional/*"]
"~/*": [
"./src/*"
],
"@composables/*": [
"./src/composables/*"
],
"@components/*": [
"./src/components/*"
],
"@helpers/*": [
"./src/helpers/*"
],
"@modules/*": [
"./src/modules/*"
],
"@workers/*": [
"./src/workers/*"
],
"@functional/*": [
"./src/helpers/functional/*"
]
},
"types": [
"vite/client",
@@ -30,5 +48,10 @@
"vite-plugin-pwa/client"
]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
]
}

View File

@@ -1,60 +1,40 @@
import vue from "@vitejs/plugin-vue"
import dts from "vite-plugin-dts"
import path from "path"
import { FileSystemIconLoader } from "unplugin-icons/loaders"
import IconResolver from "unplugin-icons/resolver"
import Icons from "unplugin-icons/vite"
import Components from "unplugin-vue-components/vite"
import { defineConfig } from "vite"
import WindiCSS from "vite-plugin-windicss"
module.exports = defineConfig({
export default defineConfig({
plugins: [
vue(),
dts({
insertTypesEntry: true,
skipDiagnostics: true,
outputDir: ['dist']
}),
WindiCSS({
root: path.resolve(__dirname),
}),
Components({
dts: "./src/components.d.ts",
dirs: ["./src/components"],
directoryAsNamespace: true,
resolvers: [
IconResolver({
prefix: "icon",
customCollections: ["hopp", "auth", "brands"],
}),
],
}),
Icons({
compiler: "vue3",
customCollections: {
hopp: FileSystemIconLoader("../hoppscotch-common/assets/icons"),
auth: FileSystemIconLoader("../hoppscotch-common/assets/icons/auth"),
brands: FileSystemIconLoader(
"../hoppscotch-common/assets/icons/brands"
),
},
compiler: "vue3"
}),
], // to process SFC
build: {
sourcemap: true,
minify: false,
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
name: "hopp-ui",
formats: ["es"], // adding 'umd' requires globals set to every external module
fileName: (format) => `hopp-ui.${format}.js`,
formats: ["es"],
fileName: (format, entry) => `${entry}.${format}.js`,
},
rollupOptions: {
// external modules won't be bundled into HoppUI library
external: ["vue"], // not every external has a global
external: ["vue", "vue-router"],
output: {
// disable warning on src/index.ts using both default and named export
exports: "named",
// Provide global variables to use in the UMD build
// for externalized deps (not useful if 'umd' is not in lib.formats)
globals: {
vue: "Vue",
},
},
},
emptyOutDir: false, // to retain the types folder generated by tsc
emptyOutDir: true
},
})