Files
tools/vue/toolkit/src/composables/array/useArrayMap/demo.vue
T
robonen aa2938cb34 refactor(toolkit): type source any with proper types
Genuinely type composable any usages (useStepper/useStorage/useForm/
createEventHook/useSorted/etc.) as proper generics/unknown; keep idiomatic
any-function and overload-impl signatures with comments; skipped test -> .todo.
2026-06-15 16:55:07 +07:00

96 lines
3.0 KiB
Vue

<script setup lang="ts">
import { computed, ref } from 'vue';
import { useArrayMap } from './index';
interface Product {
name: string;
price: number;
}
const products = ref<Product[]>([
{ name: 'Mechanical keyboard', price: 129 },
{ name: 'Ultrawide monitor', price: 549 },
{ name: 'Noise-cancelling headset', price: 299 },
{ name: 'Standing desk', price: 419 },
]);
const taxRate = ref(8);
// Reactive Array.prototype.map — recomputes when products or taxRate change.
const priced = useArrayMap(products, (product) => {
const gross = product.price * (1 + taxRate.value / 100);
return { ...product, gross };
});
const formatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
const total = computed(() => priced.value.reduce((sum, p) => sum + p.gross, 0));
function bump(index: number, delta: number) {
const next = products.value.slice();
next[index] = { ...next[index], price: Math.max(0, next[index].price + delta) };
products.value = next;
}
</script>
<template>
<div class="demo-stack max-w-md">
<div class="flex items-center justify-between">
<span class="demo-label">Cart</span>
<label class="flex items-center gap-2 text-sm text-fg-muted">
Tax {{ taxRate }}%
<input
v-model.number="taxRate"
type="range"
min="0"
max="25"
class="accent-accent"
>
</label>
</div>
<ul class="flex flex-col gap-2">
<li
v-for="(item, index) in priced"
:key="item.name"
class="flex items-center justify-between gap-3 rounded-lg border border-border bg-bg-elevated px-3 py-2"
>
<div class="min-w-0 flex-1">
<p class="truncate text-sm font-medium text-fg">
{{ item.name }}
</p>
<p class="text-xs text-fg-subtle">
base {{ formatter.format(item.price) }}
</p>
</div>
<div class="flex items-center gap-1.5">
<button
class="inline-flex size-7 items-center justify-center rounded-md border border-border bg-bg-elevated text-fg transition hover:bg-bg-inset active:scale-[0.98] cursor-pointer"
aria-label="Decrease price"
@click="bump(index, -10)"
>
&minus;
</button>
<button
class="inline-flex size-7 items-center justify-center rounded-md border border-border bg-bg-elevated text-fg transition hover:bg-bg-inset active:scale-[0.98] cursor-pointer"
aria-label="Increase price"
@click="bump(index, 10)"
>
+
</button>
</div>
<span class="w-20 text-right font-mono text-sm tabular-nums text-fg">
{{ formatter.format(item.gross) }}
</span>
</li>
</ul>
<div class="flex items-center justify-between rounded-lg border border-border bg-bg-inset p-3">
<span class="demo-label">Total with tax</span>
<span class="demo-stat text-xl">
{{ formatter.format(total) }}
</span>
</div>
</div>
</template>