@astrake/lumora-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/package.json +54 -0
  2. package/src/components/LuAvatar.vue +22 -0
  3. package/src/components/LuBadge.vue +15 -0
  4. package/src/components/LuButton.vue +58 -0
  5. package/src/components/LuCard.vue +20 -0
  6. package/src/components/LuCollapsible.vue +34 -0
  7. package/src/components/LuDivider.vue +18 -0
  8. package/src/components/LuIcon.vue +39 -0
  9. package/src/components/LuInput.vue +30 -0
  10. package/src/components/LuLink.vue +47 -0
  11. package/src/components/LuPageHeader.vue +24 -0
  12. package/src/components/LuProgressBar.vue +21 -0
  13. package/src/components/LuSelect.vue +35 -0
  14. package/src/components/LuSwitch.vue +40 -0
  15. package/src/components/LuTab.vue +26 -0
  16. package/src/components/LuTabList.vue +15 -0
  17. package/src/components/LuTabPanel.vue +19 -0
  18. package/src/components/LuTable.vue +15 -0
  19. package/src/components/LuTableBody.vue +15 -0
  20. package/src/components/LuTableCell.vue +15 -0
  21. package/src/components/LuTableHead.vue +15 -0
  22. package/src/components/LuTableHeadCell.vue +15 -0
  23. package/src/components/LuTableRow.vue +15 -0
  24. package/src/components/LuTabs.vue +30 -0
  25. package/src/components/LuText.vue +18 -0
  26. package/src/components/LuThemeSelect.vue +26 -0
  27. package/src/components/LuThemeSwitch.vue +22 -0
  28. package/src/components/LuTooltip.vue +36 -0
  29. package/src/components/index.ts +27 -0
  30. package/src/composables/index.ts +4 -0
  31. package/src/composables/useRail.ts +24 -0
  32. package/src/composables/useSplit.ts +17 -0
  33. package/src/composables/useTheme.ts +36 -0
  34. package/src/context.ts +36 -0
  35. package/src/index.ts +8 -0
  36. package/src/layout/LuDock.vue +23 -0
  37. package/src/layout/LuDockItem.vue +18 -0
  38. package/src/layout/LuFill.vue +17 -0
  39. package/src/layout/LuFixed.vue +17 -0
  40. package/src/layout/LuGrid.vue +22 -0
  41. package/src/layout/LuOverlay.vue +17 -0
  42. package/src/layout/LuScroll.vue +17 -0
  43. package/src/layout/LuSplit.vue +23 -0
  44. package/src/layout/LuSplitPane.vue +32 -0
  45. package/src/layout/LuSplitResizer.vue +17 -0
  46. package/src/layout/LuStack.vue +24 -0
  47. package/src/layout/index.ts +25 -0
  48. package/src/plugin.ts +27 -0
  49. package/src/shell/desktop/LuDesktopRailBar.vue +23 -0
  50. package/src/shell/desktop/LuDesktopRailItem.vue +23 -0
  51. package/src/shell/desktop/LuDesktopShell.vue +25 -0
  52. package/src/shell/desktop/LuDesktopSidebar.vue +36 -0
  53. package/src/shell/desktop/LuDesktopStatusBar.vue +15 -0
  54. package/src/shell/desktop/LuDesktopTopBar.vue +15 -0
  55. package/src/shell/embedded/LuEmbeddedShell.vue +20 -0
  56. package/src/shell/index.ts +10 -0
  57. package/src/shell/mobile/LuMobileShell.vue +21 -0
  58. package/src/skins/default.ts +94 -0
  59. package/src/skins/index.ts +1 -0
  60. package/src/types.ts +18 -0
  61. package/tsconfig.json +10 -0
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@astrake/lumora-ui",
3
+ "version": "0.1.0",
4
+ "description": "Headless Vue 3 component framework for three surface targets — Mobile, Desktop, and Embedded — with a unified --lu-* design token system.",
5
+ "author": "Anuvab Chakraborty (https://github.com/madlybong)",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": "./src/index.ts",
10
+ "./layout": "./src/layout/index.ts",
11
+ "./shell": "./src/shell/index.ts",
12
+ "./components": "./src/components/index.ts",
13
+ "./composables": "./src/composables/index.ts",
14
+ "./skins": "./src/skins/index.ts"
15
+ },
16
+ "keywords": [
17
+ "vue",
18
+ "vue3",
19
+ "ui",
20
+ "components",
21
+ "mobile",
22
+ "desktop",
23
+ "embedded",
24
+ "headless",
25
+ "design-tokens",
26
+ "lumora",
27
+ "astrake",
28
+ "typescript"
29
+ ],
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/madlybong/LumoraUI.git"
33
+ },
34
+ "homepage": "https://ui.lumora.astrake.com",
35
+ "bugs": {
36
+ "url": "https://github.com/madlybong/LumoraUI/issues"
37
+ },
38
+ "funding": {
39
+ "type": "github",
40
+ "url": "https://github.com/sponsors/madlybong"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "registry": "https://registry.npmjs.org/"
45
+ },
46
+ "scripts": {
47
+ "build": "bun run ../../tools/build.ts",
48
+ "check": "vue-tsc -p ./tsconfig.json",
49
+ "test": "vitest run"
50
+ },
51
+ "peerDependencies": {
52
+ "vue": "^3.5.0"
53
+ }
54
+ }
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <div v-bind="$attrs" :class="containerSkin" :data-size="size">
3
+ <img v-if="src && !hasError" :src="src" :alt="alt" :class="imageSkin" @error="hasError = true" />
4
+ <div v-else :class="fallbackSkin">
5
+ <slot>{{ fallback }}</slot>
6
+ </div>
7
+ </div>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { computed, ref } from "vue";
12
+ import { useLumoraConfig } from "../context";
13
+
14
+ const props = defineProps<{ variant?: string; src?: string; fallback?: string; alt?: string; size?: string }>();
15
+
16
+ const hasError = ref(false);
17
+
18
+ const { resolveSkin } = useLumoraConfig();
19
+ const containerSkin = computed(() => resolveSkin("LuAvatar", props.variant));
20
+ const imageSkin = computed(() => resolveSkin("LuAvatarImage", props.variant));
21
+ const fallbackSkin = computed(() => resolveSkin("LuAvatarFallback", props.variant));
22
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <component :is="as || 'span'" v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </component>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string; as?: string }>();
12
+
13
+ const { resolveSkin } = useLumoraConfig();
14
+ const resolvedSkin = computed(() => resolveSkin("LuBadge", props.variant));
15
+ </script>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <component
3
+ :is="componentType"
4
+ v-bind="bindingProps"
5
+ :class="resolvedSkin"
6
+ @click="emit('click', $event)"
7
+ >
8
+ <slot />
9
+ </component>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { computed, resolveComponent, useAttrs } from "vue";
14
+ import { useLumoraConfig } from "../context";
15
+
16
+ const props = defineProps<{
17
+ type?: "button" | "submit" | "reset";
18
+ disabled?: boolean;
19
+ variant?: string;
20
+ as?: string;
21
+ to?: any;
22
+ href?: string;
23
+ }>();
24
+
25
+ const emit = defineEmits<{
26
+ (e: "click", event: MouseEvent): void;
27
+ }>();
28
+
29
+ const componentType = computed(() => {
30
+ if (props.as) return props.as;
31
+ if (props.to) {
32
+ const routerLink = resolveComponent("RouterLink");
33
+ return typeof routerLink === 'string' ? 'a' : routerLink;
34
+ }
35
+ if (props.href) return "a";
36
+ return "button";
37
+ });
38
+
39
+ const bindingProps = computed(() => {
40
+ const p: any = { ...props, ...useAttrs() };
41
+ delete p.variant;
42
+ delete p.as;
43
+
44
+ if (componentType.value !== 'button') {
45
+ delete p.type;
46
+ }
47
+ if (props.disabled && componentType.value !== 'button') {
48
+ // Buttons can be natively disabled, but links cannot easily.
49
+ // However, if disabled is true, we keep it so CSS can style it.
50
+ // To prevent click navigation on disabled links, we would need to handle the click event.
51
+ // For now, we pass it down so styling works.
52
+ }
53
+ return p;
54
+ });
55
+
56
+ const { resolveSkin } = useLumoraConfig();
57
+ const resolvedSkin = computed(() => resolveSkin("LuButton", props.variant));
58
+ </script>
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <component :is="as" v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </component>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = withDefaults(defineProps<{
12
+ variant?: string;
13
+ as?: string;
14
+ }>(), {
15
+ as: "div"
16
+ });
17
+
18
+ const { resolveSkin } = useLumoraConfig();
19
+ const resolvedSkin = computed(() => resolveSkin("LuCard", props.variant));
20
+ </script>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <div v-bind="$attrs" :class="wrapperSkin">
3
+ <button :class="triggerSkin" @click="toggle">
4
+ <slot name="trigger" :isOpen="isOpen" />
5
+ </button>
6
+ <div v-show="isOpen" :class="contentSkin">
7
+ <slot name="content" />
8
+ </div>
9
+ </div>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { computed, ref, watch } from "vue";
14
+ import { useLumoraConfig } from "../context";
15
+
16
+ const props = defineProps<{ variant?: string; modelValue?: boolean; defaultOpen?: boolean }>();
17
+ const emit = defineEmits<{ (e: "update:modelValue", val: boolean): void }>();
18
+
19
+ const isOpen = ref(props.modelValue ?? props.defaultOpen ?? false);
20
+
21
+ watch(() => props.modelValue, (val) => {
22
+ if (val !== undefined) isOpen.value = val;
23
+ });
24
+
25
+ const toggle = () => {
26
+ isOpen.value = !isOpen.value;
27
+ emit("update:modelValue", isOpen.value);
28
+ };
29
+
30
+ const { resolveSkin } = useLumoraConfig();
31
+ const wrapperSkin = computed(() => resolveSkin("LuCollapsible", props.variant));
32
+ const triggerSkin = computed(() => resolveSkin("LuCollapsibleTrigger", props.variant));
33
+ const contentSkin = computed(() => resolveSkin("LuCollapsibleContent", props.variant));
34
+ </script>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <div v-bind="$attrs" :class="resolvedSkin"></div>
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { computed } from "vue";
7
+ import { useLumoraConfig } from "../context";
8
+
9
+ const props = withDefaults(defineProps<{
10
+ variant?: string;
11
+ orientation?: "horizontal" | "vertical";
12
+ }>(), {
13
+ orientation: "horizontal"
14
+ });
15
+
16
+ const { resolveSkin } = useLumoraConfig();
17
+ const resolvedSkin = computed(() => resolveSkin("LuDivider", props.variant || props.orientation));
18
+ </script>
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <span v-bind="$attrs" :class="resolvedSkin" aria-hidden="true" :style="sizeStyle">
3
+ <slot>
4
+ <component
5
+ v-if="resolvedIcon"
6
+ :is="resolvedIcon"
7
+ :size="size"
8
+ :stroke-width="strokeWidth"
9
+ />
10
+ </slot>
11
+ </span>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { computed, useSlots } from "vue";
16
+ import { useLumoraConfig } from "../context";
17
+
18
+ const props = defineProps<{
19
+ variant?: string;
20
+ name?: string;
21
+ size?: number;
22
+ strokeWidth?: number;
23
+ }>();
24
+
25
+ const slots = useSlots();
26
+ const { resolveSkin, resolveIcon } = useLumoraConfig();
27
+
28
+ const resolvedSkin = computed(() => resolveSkin("LuIcon", props.variant));
29
+
30
+ const resolvedIcon = computed(() => {
31
+ if (slots.default) return null;
32
+ if (!props.name) return null;
33
+ return resolveIcon(props.name, props.size);
34
+ });
35
+
36
+ const sizeStyle = computed(() =>
37
+ props.size ? { width: `${props.size}px`, height: `${props.size}px` } : undefined
38
+ );
39
+ </script>
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <input
3
+ v-bind="$attrs"
4
+ :class="resolvedSkin"
5
+ :value="modelValue"
6
+ @input="onInput"
7
+ />
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { computed } from "vue";
12
+ import { useLumoraConfig } from "../context";
13
+
14
+ const props = defineProps<{
15
+ modelValue?: string | number;
16
+ variant?: string;
17
+ }>();
18
+
19
+ const emit = defineEmits<{
20
+ (e: "update:modelValue", value: string): void;
21
+ }>();
22
+
23
+ const onInput = (event: Event) => {
24
+ const target = event.target as HTMLInputElement;
25
+ emit("update:modelValue", target.value);
26
+ };
27
+
28
+ const { resolveSkin } = useLumoraConfig();
29
+ const resolvedSkin = computed(() => resolveSkin("LuInput", props.variant));
30
+ </script>
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <component
3
+ :is="componentType"
4
+ v-bind="bindingProps"
5
+ :class="resolvedSkin"
6
+ @click="emit('click', $event)"
7
+ >
8
+ <slot />
9
+ </component>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { computed, resolveComponent } from "vue";
14
+ import { useLumoraConfig } from "../context";
15
+
16
+ const props = defineProps<{
17
+ variant?: string;
18
+ as?: string;
19
+ to?: any;
20
+ href?: string;
21
+ target?: string;
22
+ }>();
23
+
24
+ const emit = defineEmits<{
25
+ (e: "click", event: MouseEvent): void;
26
+ }>();
27
+
28
+ const componentType = computed(() => {
29
+ if (props.as) return props.as;
30
+ if (props.to) {
31
+ // Attempt to resolve RouterLink if available, otherwise fallback to 'a'
32
+ const routerLink = resolveComponent("RouterLink");
33
+ return typeof routerLink === 'string' ? 'a' : routerLink;
34
+ }
35
+ return "a";
36
+ });
37
+
38
+ const bindingProps = computed(() => {
39
+ const p: any = { ...props };
40
+ delete p.variant;
41
+ delete p.as;
42
+ return p;
43
+ });
44
+
45
+ const { resolveSkin } = useLumoraConfig();
46
+ const resolvedSkin = computed(() => resolveSkin("LuLink", props.variant));
47
+ </script>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <div v-bind="$attrs" :class="resolvedSkin">
3
+ <LuText :as="level" variant="page-title">{{ title }}</LuText>
4
+ <LuText v-if="description" as="p" variant="page-subtitle" class="mt-2">{{ description }}</LuText>
5
+ </div>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ import { computed } from "vue";
10
+ import { useLumoraConfig } from "../context";
11
+ import LuText from "./LuText.vue";
12
+
13
+ const props = withDefaults(defineProps<{
14
+ variant?: string;
15
+ title: string;
16
+ description?: string;
17
+ level?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
18
+ }>(), {
19
+ level: "h1"
20
+ });
21
+
22
+ const { resolveSkin } = useLumoraConfig();
23
+ const resolvedSkin = computed(() => resolveSkin("LuPageHeader", props.variant));
24
+ </script>
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <div v-bind="$attrs" :class="trackSkin">
3
+ <div :class="fillSkin" :style="{ width: `${percent}%` }"></div>
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string; value: number; max?: number }>();
12
+
13
+ const percent = computed(() => {
14
+ const m = props.max ?? 100;
15
+ return Math.max(0, Math.min(100, (props.value / m) * 100));
16
+ });
17
+
18
+ const { resolveSkin } = useLumoraConfig();
19
+ const trackSkin = computed(() => resolveSkin("LuProgressBar", props.variant));
20
+ const fillSkin = computed(() => resolveSkin("LuProgressBarFill", props.variant));
21
+ </script>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <select
3
+ v-bind="$attrs"
4
+ :class="resolvedSkin"
5
+ :value="modelValue"
6
+ @change="onChange"
7
+ >
8
+ <option v-for="opt in options" :key="opt.value" :value="opt.value">
9
+ {{ opt.label }}
10
+ </option>
11
+ </select>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { computed } from "vue";
16
+ import { useLumoraConfig } from "../context";
17
+
18
+ const props = defineProps<{
19
+ modelValue?: string | number;
20
+ variant?: string;
21
+ options: Array<{ value: string | number; label: string }>;
22
+ }>();
23
+
24
+ const emit = defineEmits<{
25
+ (e: "update:modelValue", value: string): void;
26
+ }>();
27
+
28
+ const onChange = (event: Event) => {
29
+ const target = event.target as HTMLSelectElement;
30
+ emit("update:modelValue", target.value);
31
+ };
32
+
33
+ const { resolveSkin } = useLumoraConfig();
34
+ const resolvedSkin = computed(() => resolveSkin("LuSelect", props.variant));
35
+ </script>
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <button
3
+ role="switch"
4
+ type="button"
5
+ :aria-checked="modelValue"
6
+ :disabled="disabled"
7
+ :class="[resolvedSkin, modelValue ? activeSkin : '']"
8
+ @click="toggle"
9
+ >
10
+ <span :class="[thumbSkin, modelValue ? thumbActiveSkin : '']" />
11
+ </button>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { computed } from "vue";
16
+ import { useLumoraConfig } from "../context";
17
+
18
+ const props = defineProps<{
19
+ modelValue?: boolean;
20
+ variant?: string;
21
+ disabled?: boolean;
22
+ }>();
23
+
24
+ const emit = defineEmits<{
25
+ (e: "update:modelValue", value: boolean): void;
26
+ }>();
27
+
28
+ const toggle = () => {
29
+ if (props.disabled) return;
30
+ emit("update:modelValue", !props.modelValue);
31
+ };
32
+
33
+ const { resolveSkin } = useLumoraConfig();
34
+
35
+ const resolvedSkin = computed(() => resolveSkin("LuSwitch", props.variant));
36
+ const activeSkin = computed(() => resolveSkin("LuSwitch", "active"));
37
+
38
+ const thumbSkin = computed(() => resolveSkin("LuSwitchThumb", props.variant));
39
+ const thumbActiveSkin = computed(() => resolveSkin("LuSwitchThumb", "active"));
40
+ </script>
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <button v-bind="$attrs" :class="[resolvedSkin, isActive ? activeSkin : '']" @click="onClick">
3
+ <slot />
4
+ </button>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed, inject, type Ref } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string; value: string | number }>();
12
+
13
+ const activeTab = inject<Ref<string | number | undefined>>("lu-tabs-active");
14
+
15
+ const isActive = computed(() => activeTab?.value === props.value);
16
+
17
+ const onClick = () => {
18
+ if (activeTab) {
19
+ activeTab.value = props.value;
20
+ }
21
+ };
22
+
23
+ const { resolveSkin } = useLumoraConfig();
24
+ const resolvedSkin = computed(() => resolveSkin("LuTab", props.variant));
25
+ const activeSkin = computed(() => resolveSkin("LuTab", "active"));
26
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <div v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string }>();
12
+
13
+ const { resolveSkin } = useLumoraConfig();
14
+ const resolvedSkin = computed(() => resolveSkin("LuTabList", props.variant));
15
+ </script>
@@ -0,0 +1,19 @@
1
+ <template>
2
+ <div v-show="isActive" v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed, inject, type Ref } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string; value: string | number }>();
12
+
13
+ const activeTab = inject<Ref<string | number | undefined>>("lu-tabs-active");
14
+
15
+ const isActive = computed(() => activeTab?.value === props.value);
16
+
17
+ const { resolveSkin } = useLumoraConfig();
18
+ const resolvedSkin = computed(() => resolveSkin("LuTabPanel", props.variant));
19
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <table v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </table>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string }>();
12
+
13
+ const { resolveSkin } = useLumoraConfig();
14
+ const resolvedSkin = computed(() => resolveSkin("LuTable", props.variant));
15
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <tbody v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </tbody>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string }>();
12
+
13
+ const { resolveSkin } = useLumoraConfig();
14
+ const resolvedSkin = computed(() => resolveSkin("LuTableBody", props.variant));
15
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <td v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </td>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string }>();
12
+
13
+ const { resolveSkin } = useLumoraConfig();
14
+ const resolvedSkin = computed(() => resolveSkin("LuTableCell", props.variant));
15
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <thead v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </thead>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string }>();
12
+
13
+ const { resolveSkin } = useLumoraConfig();
14
+ const resolvedSkin = computed(() => resolveSkin("LuTableHead", props.variant));
15
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <th v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </th>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string }>();
12
+
13
+ const { resolveSkin } = useLumoraConfig();
14
+ const resolvedSkin = computed(() => resolveSkin("LuTableHeadCell", props.variant));
15
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <tr v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </tr>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{ variant?: string }>();
12
+
13
+ const { resolveSkin } = useLumoraConfig();
14
+ const resolvedSkin = computed(() => resolveSkin("LuTableRow", props.variant));
15
+ </script>
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <div v-bind="$attrs" :class="resolvedSkin">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed, provide, ref, watch } from "vue";
9
+ import { useLumoraConfig } from "../context";
10
+
11
+ const props = defineProps<{
12
+ variant?: string;
13
+ modelValue?: string | number;
14
+ }>();
15
+
16
+ const emit = defineEmits<{
17
+ (e: "update:modelValue", value: string | number): void;
18
+ }>();
19
+
20
+ const activeTab = ref(props.modelValue);
21
+ watch(() => props.modelValue, (val) => activeTab.value = val);
22
+ watch(activeTab, (val) => {
23
+ if (val !== undefined) emit("update:modelValue", val);
24
+ });
25
+
26
+ provide("lu-tabs-active", activeTab);
27
+
28
+ const { resolveSkin } = useLumoraConfig();
29
+ const resolvedSkin = computed(() => resolveSkin("LuTabs", props.variant));
30
+ </script>