Skip to content

Commit

Permalink
Merge branch 'release/v0.26.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
holtwick committed Jan 3, 2025
2 parents c523af7 + 5a55d9b commit ca2c87f
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 45 deletions.
11 changes: 8 additions & 3 deletions lib/basic/oui-file.demo.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { OuiCheckbox, OuiDemo, OuiFile, OuiInput } from '@/lib'
import { OuiDemo, OuiFile, OuiInput } from '@/lib'
import { useLocalStorage } from '@vueuse/core'
const state = useLocalStorage('oui.demo.file', {
Expand All @@ -20,12 +20,17 @@ const state = useLocalStorage('oui.demo.file', {
:accept="state.accept"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-image-up"><path d="M10.3 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10l-3.1-3.1a2 2 0 0 0-2.814.014L6 21" /><path d="m14 19.5 3-3 3 3" /><path d="M17 22v-5.5" /><circle cx="9" cy="9" r="2" /></svg>
Upload an image
<template #preview="{ filename }">
{{ filename }}
<img :src="state.value" alt="Preview" height="100">
</template>
</OuiFile>
</div>
<OuiDemo :state="state">
<OuiInput v-model="state.title" title="Title" />
<OuiInput v-model="state.accept" title="Accept file types (MIME)" />
<OuiCheckbox v-model="state.multiple" switch title="Multiple files" />
<OuiCheckbox v-model="state.preview" switch title="Preview" />
<!-- <OuiCheckbox v-model="state.multiple" switch title="Multiple files" /> -->
<!-- <OuiCheckbox v-model="state.preview" switch title="Preview" /> -->
</OuiDemo>
</template>
43 changes: 24 additions & 19 deletions lib/basic/oui-file.styl
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
@require "../../stylus/index.styl";

.oui-file {
use: stack-x;
gap: 8;
oui-input-default();
color: var(--fg);
border-style: dashed;
cursor: pointer;
width: 100%;
vertical-align: middle;
user-select: none;
-webkit-user-select: none;
font-size: 16;
padding-y: 6;

img {
size: 64;
border-radius: 4;
> * {
display: inline-flex;
flex: none;
}

button {
use: center;
color: var(--fg);
border: 1px dashed var(--s2-fg);
border-radius: 4;
padding: 8;
height: 64;
min-width: 64;
cursor: pointer;
// icon
svg, img {
size: 1em;
}

._content {
stack-x(8);
}

&:hover {
color: var(--p1-fg);
border-color: var(--p1-fg);
// background: var(--t3-bg);
}
&:hover, &._over {
color: var(--p1-fg);
border-color: var(--input-border-hover);
}
}
63 changes: 49 additions & 14 deletions lib/basic/oui-file.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
<script lang="ts" setup>
import type { LoggerInterface } from 'zeed'
import { useFileDialog } from '@vueuse/core'
import { useDropZone, useFileDialog } from '@vueuse/core'
import { ref } from 'vue'
import { createPromise, Logger } from 'zeed'
import OuiClose from './oui-close.vue'
import './oui-form.styl'
import './oui-file.styl'
import './oui-form.styl'
const props = withDefaults(defineProps<{
title?: string
accept?: string
multiple?: boolean
preview?: boolean
// multiple?: boolean
// preview?: boolean
}>(), {
accept: 'image/*',
multiple: false,
Expand All @@ -22,10 +24,11 @@ const emit = defineEmits<{
const log: LoggerInterface = Logger('oui-file')
const { files, open, reset, onChange } = useFileDialog({
accept: props.accept,
multiple: props.multiple,
})
const dropZoneRef = ref<HTMLDivElement>()
const filename = ref<string>()
const filesize = ref<string>()
const filetype = ref<string>()
const model = defineModel<string | undefined | null>({ required: true })
Expand All @@ -36,9 +39,35 @@ async function fileToDataURI(file: File): Promise<string | undefined> {
fileReader.addEventListener('abort', resolve)
fileReader.addEventListener('loadend', e => resolve(fileReader.result))
fileReader.readAsDataURL(file)
return promise
const datauri = await promise
if (datauri) {
filename.value = file.name
filesize.value = `${(file.size / 1024).toFixed(2)} KB`
filetype.value = file.type
// return `${datauri}?type=${encodeURIComponent(file.type)}&name=${encodeURIComponent(file.name)}&size=${file.size}`
}
return datauri
}
async function onDrop(files: File[] | null) {
const file = files?.[0]
if (file) {
model.value = await fileToDataURI(file)
}
}
const { isOverDropZone } = useDropZone(dropZoneRef, {
onDrop,
dataTypes: [props.accept],
multiple: false, // props.multiple,
preventDefaultForUnhandled: false,
})
const { files, open, reset, onChange } = useFileDialog({
accept: props.accept,
multiple: false, // props.multiple,
})
onChange(async () => {
const file = files.value?.[0]
if (file) {
Expand All @@ -56,10 +85,16 @@ function doSelect() {
</script>

<template>
<div class="oui-file">
<img v-if="model" :src="model">
<button class="oui-file-button" @click.prevent="doSelect">
<slot>{{ title ?? 'Choose file...' }}</slot>
</button>
<div ref="dropZoneRef" class="oui-file" :class="{ _over: isOverDropZone }" @click.prevent="doSelect">
<div class="_content">
<template v-if="!model">
<slot>{{ title ?? 'Choose file...' }}</slot>
</template>
<template v-else>
<slot name="preview" :filename="filename">
{{ filename ?? 'File' }} <OuiClose @click="model = undefined" />
</slot>
</template>
</div>
</div>
</template>
3 changes: 2 additions & 1 deletion lib/basic/oui-tabs.demo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import { reactive } from 'vue'
import { OuiClose, OuiDemo, OuiInput, OuiTabs } from '../lib'
const state = reactive({
selected: '',
selected: 'one',
})
const tabs: OuiTab[] = [
{ title: 'One', name: 'one', icon: OuiClose },
{ title: 'Two', name: 'two' },
{ title: 'Three', name: 'three' },
]
</script>

Expand Down
41 changes: 40 additions & 1 deletion lib/basic/oui-tabs.styl
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,56 @@

.oui-tabs {
use: stack-y;
border-radius: 4;

> ._nav {
use: stack-x;
gap: 8;
position: relative;
background: var(--p1-bg);
border-radius: 4;
padding: 4;
gap: 4;

._pill {
position: absolute;
height: calc(100% - 16px);
transition: left 0.25s, width 0.25s;
border-radius: 4;
background: var(--p1-fg);
}

display: flex;
justify-content: space-evenly;
width: 100%;

button {
use: stack-item-grow;
padding: 4 8;
cursor: pointer;
position: relative;
z-index: 1;
white-space: nowrap;
font-weight: normal;
color: var(--t3-fg);
}

button {
&:hover {
color: var(--p1-fg);
}
}

._active {
color: var(--bg);
font-weight: bold;

&:hover {
color: var(--bg);
}
}
}

svg {
size: 1em;
}
}
37 changes: 33 additions & 4 deletions lib/basic/oui-tabs.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts" setup generic="K extends string">
import type { OuiTab } from './_types'
import { computed } from 'vue'
import { useElementBounding, useElementSize } from '@vueuse/core'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import './oui-tabs.styl'
const props = defineProps<{
Expand All @@ -12,13 +12,42 @@ const props = defineProps<{
const modelValue = defineModel<K>({ required: false })
const name = computed<string>(() => modelValue.value ?? props.tabs?.[0]?.name ?? 'default')
const tabsRef = ref<HTMLElement>()
const activeTabRefs = ref<HTMLElement>()
const activeTabLeft = ref(0)
const activeTabTop = ref(0)
const activeTabWidth = ref(0)
const activeTabHeight = ref(0)
// const { x, y, width, height } = useElementBounding(activeTabRefs)
function setActiveTab() {
const activeTab = tabsRef.value?.querySelector('._active')
if (activeTab instanceof HTMLElement) {
activeTabLeft.value = activeTab.offsetLeft
activeTabTop.value = activeTab.offsetTop
activeTabWidth.value = activeTab.offsetWidth
activeTabHeight.value = activeTab.offsetHeight
}
}
watch(modelValue, async () => {
await nextTick()
setActiveTab()
})
onMounted(setActiveTab)
</script>

<template>
<div class="oui-tabs">
<div ref="tabsRef" class="oui-tabs">
<nav class="oui-tabs-nav _nav">
<div class="_pill" :style="{ left: `${activeTabLeft}px`, width: `${activeTabWidth}px`, top: `${activeTabTop}px`, height: `${activeTabHeight}px` }" />
<template v-for="tab in tabs" :key="tab.name">
<button
ref="tabRefs"
:class="{ _active: modelValue === tab.name }"
@click="modelValue = tab.name"
>
Expand All @@ -28,7 +57,7 @@ const name = computed<string>(() => modelValue.value ?? props.tabs?.[0]?.name ??
{{ tab.title ?? tab.name }}
</button>
</template>
<div class="ogrow" />
<!-- <div class="ogrow" /> -->
</nav>

<div>
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "oui-kit",
"type": "module",
"version": "0.25.7",
"version": "0.26.0",
"author": {
"email": "[email protected]",
"name": "Dirk Holtwick",
Expand Down Expand Up @@ -81,14 +81,14 @@
"devDependencies": {
"@antfu/eslint-config": "^3.12.1",
"@antfu/ni": "^0.23.2",
"@shikijs/markdown-it": "^1.25.1",
"@shikijs/markdown-it": "^1.26.1",
"@vitejs/plugin-vue": "^5.2.1",
"@vitest/browser": "^2.1.8",
"eslint": "^9.17.0",
"stylus": "^0.64.0",
"tsup": "^8.3.5",
"unplugin-vue-markdown": "^0.28.0",
"vite": "^6.0.6",
"vite": "^6.0.7",
"vite-plugin-dts": "^4.4.0",
"vite-plugin-qrcode": "^0.2.3",
"vite-plugin-vue-devtools": "^7.6.8",
Expand Down

0 comments on commit ca2c87f

Please sign in to comment.