feat: complete UI/UX improvements, vuesax icons, media server, and section management
This commit is contained in:
@@ -1,112 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { IconName } from "@imen/shared";
|
||||
import {
|
||||
Award,
|
||||
BadgeCheck,
|
||||
Building2,
|
||||
ClipboardCheck,
|
||||
Clock,
|
||||
Construction,
|
||||
Ear,
|
||||
Factory,
|
||||
FileCheck,
|
||||
FileSearch,
|
||||
Flame,
|
||||
Footprints,
|
||||
Fuel,
|
||||
Gauge,
|
||||
Glasses,
|
||||
Globe,
|
||||
GraduationCap,
|
||||
Hand,
|
||||
Handshake,
|
||||
HardHat,
|
||||
Headset,
|
||||
HeartPulse,
|
||||
Leaf,
|
||||
Lock,
|
||||
Mail,
|
||||
MapPin,
|
||||
Medal,
|
||||
Microscope,
|
||||
Package,
|
||||
PackageCheck,
|
||||
Phone,
|
||||
Pickaxe,
|
||||
ShieldAlert,
|
||||
ShieldCheck,
|
||||
ShieldPlus,
|
||||
Shirt,
|
||||
Siren,
|
||||
Star,
|
||||
Target,
|
||||
ThumbsUp,
|
||||
TrafficCone,
|
||||
TriangleAlert,
|
||||
Truck,
|
||||
Users,
|
||||
Warehouse,
|
||||
Wind,
|
||||
Wrench,
|
||||
Zap,
|
||||
} from "lucide-vue-next";
|
||||
import type { Component } from "vue";
|
||||
import VuesaxIcon from "./VuesaxIcon.vue";
|
||||
|
||||
/** Explicit registry (matches ICON_NAMES in @imen/shared) so only these icons are bundled */
|
||||
const ICONS: Record<IconName, Component> = {
|
||||
ShieldCheck,
|
||||
ShieldPlus,
|
||||
ShieldAlert,
|
||||
BadgeCheck,
|
||||
Award,
|
||||
Medal,
|
||||
PackageCheck,
|
||||
Package,
|
||||
Truck,
|
||||
Warehouse,
|
||||
Globe,
|
||||
Users,
|
||||
Handshake,
|
||||
Headset,
|
||||
ClipboardCheck,
|
||||
FileCheck,
|
||||
FileSearch,
|
||||
GraduationCap,
|
||||
Gauge,
|
||||
Microscope,
|
||||
Wrench,
|
||||
HardHat,
|
||||
Shirt,
|
||||
Footprints,
|
||||
Hand,
|
||||
Glasses,
|
||||
Ear,
|
||||
Wind,
|
||||
TriangleAlert,
|
||||
Siren,
|
||||
TrafficCone,
|
||||
Construction,
|
||||
Factory,
|
||||
Building2,
|
||||
Fuel,
|
||||
Pickaxe,
|
||||
Flame,
|
||||
Zap,
|
||||
Leaf,
|
||||
HeartPulse,
|
||||
Clock,
|
||||
Phone,
|
||||
Mail,
|
||||
MapPin,
|
||||
Star,
|
||||
ThumbsUp,
|
||||
Target,
|
||||
Lock,
|
||||
};
|
||||
|
||||
defineProps<{ name: string }>();
|
||||
defineProps<{ name: string; size?: number | string }>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="ICONS[name as IconName] ?? ShieldCheck" />
|
||||
<VuesaxIcon :name="name" :size="size" />
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ImageOff } from "lucide-vue-next";
|
||||
import VuesaxIcon from "~/components/ui/VuesaxIcon.vue";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
@@ -17,7 +17,7 @@ withDefaults(
|
||||
<div
|
||||
:class="`flex flex-col items-center justify-center border border-dashed border-line bg-panel ${aspectClass}`"
|
||||
>
|
||||
<ImageOff class="size-10 text-muted/30" />
|
||||
<VuesaxIcon name="gallery-slash" class="size-10 text-muted/30" />
|
||||
<span class="mt-2 font-mono text-[10px] tracking-wider text-muted/40">{{ label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,7 +34,7 @@ withDefaults(
|
||||
<component
|
||||
:is="`h${level}`"
|
||||
:id="headingId"
|
||||
class="mt-5 text-3xl font-black leading-[1.25] md:text-5xl md:leading-[1.2]"
|
||||
class="mt-3.5 sm:mt-5 text-2xl sm:text-3xl md:text-5xl font-black leading-tight sm:leading-[1.25] md:leading-[1.2]"
|
||||
>
|
||||
<slot name="title">
|
||||
<Lines v-if="title" :text="title" />
|
||||
@@ -46,7 +46,7 @@ withDefaults(
|
||||
</component>
|
||||
</Reveal>
|
||||
<Reveal v-if="desc" :delay="0.16">
|
||||
<p :class="`mt-5 max-w-xl leading-8 text-muted ${center ? 'mx-auto' : ''}`">
|
||||
<p :class="`mt-3 sm:mt-5 max-w-xl text-xs sm:text-base leading-6 sm:leading-8 text-muted ${center ? 'mx-auto' : ''}`">
|
||||
{{ desc }}
|
||||
</p>
|
||||
</Reveal>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { resolveVuesaxIcon } from "@imen/shared";
|
||||
import icons from "~/icons/vuesax-linear.json";
|
||||
|
||||
const props = defineProps<{
|
||||
/** Short name (`add`), alias (`ShieldCheck`, `Phone`), or full key (`vuesax-linear:add`) */
|
||||
name: string;
|
||||
size?: number | string;
|
||||
}>();
|
||||
|
||||
const pack = icons as Record<string, string>;
|
||||
|
||||
const svgHtml = computed(() => {
|
||||
const resolved = resolveVuesaxIcon(props.name || "");
|
||||
const key = resolved.includes(":") ? resolved : `vuesax-linear:${resolved}`;
|
||||
const raw = pack[key] || pack[`vuesax-linear:${resolved.toLowerCase()}`] || pack["vuesax-linear:shield-tick"];
|
||||
if (!raw) return "";
|
||||
return raw
|
||||
.replace(/\swidth="24"/, "")
|
||||
.replace(/\sheight="24"/, "")
|
||||
.replace(/stroke="#[A-Fa-f0-9]{3,8}"/g, 'stroke="currentColor"')
|
||||
.replace(/fill="(?!none)#[A-Fa-f0-9]{3,8}"/g, 'fill="currentColor"');
|
||||
});
|
||||
|
||||
const box = computed(() => {
|
||||
if (!props.size) return undefined;
|
||||
return typeof props.size === "number" ? `${props.size}px` : props.size;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
class="anticon vx-icon"
|
||||
:style="box ? { width: box, height: box } : undefined"
|
||||
:aria-hidden="true"
|
||||
v-html="svgHtml"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vx-icon {
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 0;
|
||||
color: inherit;
|
||||
vertical-align: -0.15em;
|
||||
}
|
||||
|
||||
.vx-icon :deep(svg) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user