lumina-slides 9.0.5 → 9.0.6

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 (35) hide show
  1. package/dist/lumina-slides.js +21706 -19324
  2. package/dist/lumina-slides.umd.cjs +223 -223
  3. package/dist/style.css +1 -1
  4. package/package.json +1 -1
  5. package/src/components/LandingPage.vue +1 -1
  6. package/src/components/LuminaDeck.vue +237 -232
  7. package/src/components/base/LuminaElement.vue +2 -0
  8. package/src/components/layouts/LayoutFeatures.vue +123 -123
  9. package/src/components/layouts/LayoutFlex.vue +212 -212
  10. package/src/components/layouts/LayoutStatement.vue +5 -2
  11. package/src/components/layouts/LayoutSteps.vue +108 -108
  12. package/src/components/parts/FlexHtml.vue +65 -65
  13. package/src/components/parts/FlexImage.vue +81 -81
  14. package/src/components/site/SiteDocs.vue +3313 -3314
  15. package/src/components/site/SiteExamples.vue +66 -66
  16. package/src/components/studio/EditorLayoutChart.vue +18 -0
  17. package/src/components/studio/EditorLayoutCustom.vue +18 -0
  18. package/src/components/studio/EditorLayoutVideo.vue +18 -0
  19. package/src/components/studio/LuminaStudioEmbed.vue +68 -0
  20. package/src/components/studio/StudioEmbedRoot.vue +19 -0
  21. package/src/components/studio/StudioInspector.vue +1113 -7
  22. package/src/components/studio/StudioSettings.vue +658 -7
  23. package/src/components/studio/StudioToolbar.vue +20 -2
  24. package/src/composables/useElementState.ts +12 -1
  25. package/src/composables/useFlexLayout.ts +128 -128
  26. package/src/core/Lumina.ts +174 -113
  27. package/src/core/animationConfig.ts +10 -0
  28. package/src/core/elementController.ts +18 -0
  29. package/src/core/elementResolver.ts +4 -2
  30. package/src/core/schema.ts +503 -503
  31. package/src/core/store.ts +465 -465
  32. package/src/core/types.ts +26 -11
  33. package/src/index.ts +2 -2
  34. package/src/utils/templateInterpolation.ts +52 -52
  35. package/src/views/DeckView.vue +313 -313
@@ -1,108 +1,108 @@
1
- <template>
2
- <BaseSlide :data="data" :slide-index="slideIndex">
3
- <div :class="['w-full flex flex-col justify-center', data.sizing === 'container' ? 'min-h-full py-12 lg:py-16' : 'min-h-screen']"
4
- :style="{ padding: 'var(--lumina-space-xl) var(--lumina-space-3xl)' }">
5
- <!-- Header: wrapper (header) + title and subtitle as separate controllable elements -->
6
- <LuminaElement :id="headerId" :class="['text-center max-w-4xl mx-auto', data.class || '']"
7
- :style="{ marginBottom: 'var(--lumina-space-2xl)' }">
8
- <LuminaElement :id="titleId" tag="h2" class="font-bold" :style="{
9
- fontFamily: 'var(--lumina-font-heading)',
10
- fontSize: 'var(--lumina-text-5xl)',
11
- marginBottom: 'var(--lumina-space-md)',
12
- letterSpacing: 'var(--lumina-tracking-tighter)',
13
- color: 'var(--lumina-color-text-safe, var(--lumina-color-text))'
14
- }">
15
- {{ data.title }}
16
- </LuminaElement>
17
- <LuminaElement v-if="data.subtitle" :id="subtitleId" tag="p" :style="{
18
- color: 'var(--lumina-color-text-safe, var(--lumina-color-text))',
19
- opacity: 0.9,
20
- fontSize: 'var(--lumina-text-xl)'
21
- }">
22
- {{ data.subtitle }}
23
- </LuminaElement>
24
- </LuminaElement>
25
-
26
- <!-- Steps Grid -->
27
- <div :style="{
28
- display: 'grid',
29
- gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
30
- gap: 'var(--lumina-space-xl)',
31
- maxWidth: '72rem',
32
- margin: '0 auto',
33
- width: '100%'
34
- }">
35
- <LuminaElement v-for="(step, i) in data.steps" :key="i" :id="stepId(i)"
36
- :class="['glass-panel group relative overflow-hidden', step.class || '']" :style="{
37
- borderRadius: 'var(--lumina-card-radius)',
38
- padding: 'var(--lumina-card-padding)'
39
- }">
40
- <!-- Step Number -->
41
- <div :style="{
42
- display: 'flex',
43
- alignItems: 'center',
44
- gap: 'var(--lumina-space-md)',
45
- marginBottom: 'var(--lumina-space-md)'
46
- }">
47
- <div class="flex items-center justify-center font-bold" :style="{
48
- width: 'var(--lumina-step-badge-size, 2.5rem)',
49
- height: 'var(--lumina-step-badge-size, 2.5rem)',
50
- fontSize: 'var(--lumina-step-font-size, 1rem)',
51
- background: 'linear-gradient(135deg, var(--lumina-color-primary), var(--lumina-color-secondary))',
52
- color: 'white',
53
- borderRadius: 'var(--lumina-radius-xl)',
54
- boxShadow: '0 4px 12px rgba(var(--lumina-color-primary-rgb), 0.4)'
55
- }">
56
- {{ step.step }}
57
- </div>
58
- <h3 class="font-bold" :style="{
59
- fontSize: 'var(--lumina-text-xl)',
60
- color: 'var(--lumina-surface-text, var(--lumina-color-text))'
61
- }">
62
- {{ step.title }}
63
- </h3>
64
- </div>
65
-
66
- <!-- Description -->
67
- <p v-if="step.description" :style="{
68
- color: 'var(--lumina-color-text-secondary)',
69
- lineHeight: 'var(--lumina-leading-relaxed)',
70
- fontSize: 'var(--lumina-text-base)',
71
- opacity: 0.8
72
- }">
73
- {{ step.description }}
74
- </p>
75
-
76
- <!-- Icon (optional) -->
77
- <div v-if="step.icon" class="absolute top-4 right-4" :style="{
78
- color: 'var(--lumina-color-primary)',
79
- opacity: 0.1,
80
- fontSize: 'var(--lumina-text-4xl)'
81
- }">
82
- <i :class="['ph-thin', step.icon.startsWith('ph-') ? step.icon : `ph-${step.icon}`]"></i>
83
- </div>
84
- </LuminaElement>
85
- </div>
86
- </div>
87
- </BaseSlide>
88
- </template>
89
-
90
- <script setup lang="ts">
91
- import { computed, inject } from 'vue';
92
- import BaseSlide from '../base/BaseSlide.vue';
93
- import { resolveId } from '../../core/elementResolver';
94
- import { StoreKey } from '../../core/store';
95
- import type { SlideSteps } from '../../core/types';
96
-
97
- const props = defineProps<{
98
- data: SlideSteps;
99
- slideIndex?: number;
100
- }>();
101
-
102
- const store = inject(StoreKey);
103
- const slideIndex = computed(() => props.slideIndex ?? store?.state.currentIndex ?? 0);
104
- const headerId = computed(() => resolveId(props.data, slideIndex.value, ['header']));
105
- const titleId = computed(() => resolveId(props.data, slideIndex.value, ['title']));
106
- const subtitleId = computed(() => resolveId(props.data, slideIndex.value, ['subtitle']));
107
- const stepId = (i: number) => resolveId(props.data, slideIndex.value, ['steps', i]);
108
- </script>
1
+ <template>
2
+ <BaseSlide :data="data" :slide-index="slideIndex">
3
+ <div :class="['w-full flex flex-col justify-center', data.sizing === 'container' ? 'min-h-full py-12 lg:py-16' : 'min-h-screen']"
4
+ :style="{ padding: 'var(--lumina-space-xl) var(--lumina-space-3xl)' }">
5
+ <!-- Header: wrapper (header) + title and subtitle as separate controllable elements -->
6
+ <LuminaElement :id="headerId" :class="['text-center max-w-4xl mx-auto', data.class || '']"
7
+ :style="{ marginBottom: 'var(--lumina-space-2xl)' }">
8
+ <LuminaElement :id="titleId" tag="h2" class="font-bold" :style="{
9
+ fontFamily: 'var(--lumina-font-heading)',
10
+ fontSize: 'var(--lumina-text-5xl)',
11
+ marginBottom: 'var(--lumina-space-md)',
12
+ letterSpacing: 'var(--lumina-tracking-tighter)',
13
+ color: 'var(--lumina-color-text-safe, var(--lumina-color-text))'
14
+ }">
15
+ {{ data.title }}
16
+ </LuminaElement>
17
+ <LuminaElement v-if="data.subtitle" :id="subtitleId" tag="p" :style="{
18
+ color: 'var(--lumina-color-text-safe, var(--lumina-color-text))',
19
+ opacity: 0.9,
20
+ fontSize: 'var(--lumina-text-xl)'
21
+ }">
22
+ {{ data.subtitle }}
23
+ </LuminaElement>
24
+ </LuminaElement>
25
+
26
+ <!-- Steps Grid -->
27
+ <div :style="{
28
+ display: 'grid',
29
+ gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
30
+ gap: 'var(--lumina-space-xl)',
31
+ maxWidth: '72rem',
32
+ margin: '0 auto',
33
+ width: '100%'
34
+ }">
35
+ <LuminaElement v-for="(step, i) in data.steps" :key="i" :id="stepId(i)"
36
+ :class="['glass-panel group relative overflow-hidden', step.class || '']" :style="{
37
+ borderRadius: 'var(--lumina-card-radius)',
38
+ padding: 'var(--lumina-card-padding)'
39
+ }">
40
+ <!-- Step Number -->
41
+ <div :style="{
42
+ display: 'flex',
43
+ alignItems: 'center',
44
+ gap: 'var(--lumina-space-md)',
45
+ marginBottom: 'var(--lumina-space-md)'
46
+ }">
47
+ <div class="flex items-center justify-center font-bold" :style="{
48
+ width: 'var(--lumina-step-badge-size, 2.5rem)',
49
+ height: 'var(--lumina-step-badge-size, 2.5rem)',
50
+ fontSize: 'var(--lumina-step-font-size, 1rem)',
51
+ background: 'linear-gradient(135deg, var(--lumina-color-primary), var(--lumina-color-secondary))',
52
+ color: 'white',
53
+ borderRadius: 'var(--lumina-radius-xl)',
54
+ boxShadow: '0 4px 12px rgba(var(--lumina-color-primary-rgb), 0.4)'
55
+ }">
56
+ {{ step.step }}
57
+ </div>
58
+ <h3 class="font-bold" :style="{
59
+ fontSize: 'var(--lumina-text-xl)',
60
+ color: 'var(--lumina-surface-text, var(--lumina-color-text))'
61
+ }">
62
+ {{ step.title }}
63
+ </h3>
64
+ </div>
65
+
66
+ <!-- Description -->
67
+ <p v-if="step.description" :style="{
68
+ color: 'var(--lumina-color-text-secondary)',
69
+ lineHeight: 'var(--lumina-leading-relaxed)',
70
+ fontSize: 'var(--lumina-text-base)',
71
+ opacity: 0.8
72
+ }">
73
+ {{ step.description }}
74
+ </p>
75
+
76
+ <!-- Icon (optional) -->
77
+ <div v-if="step.icon" class="absolute top-4 right-4" :style="{
78
+ color: 'var(--lumina-color-primary)',
79
+ opacity: 0.1,
80
+ fontSize: 'var(--lumina-text-4xl)'
81
+ }">
82
+ <i :class="['ph-thin', step.icon.startsWith('ph-') ? step.icon : `ph-${step.icon}`]"></i>
83
+ </div>
84
+ </LuminaElement>
85
+ </div>
86
+ </div>
87
+ </BaseSlide>
88
+ </template>
89
+
90
+ <script setup lang="ts">
91
+ import { computed, inject } from 'vue';
92
+ import BaseSlide from '../base/BaseSlide.vue';
93
+ import { resolveId } from '../../core/elementResolver';
94
+ import { StoreKey } from '../../core/store';
95
+ import type { SlideSteps } from '../../core/types';
96
+
97
+ const props = defineProps<{
98
+ data: SlideSteps;
99
+ slideIndex?: number;
100
+ }>();
101
+
102
+ const store = inject(StoreKey);
103
+ const slideIndex = computed(() => props.slideIndex ?? store?.state.currentIndex ?? 0);
104
+ const headerId = computed(() => resolveId(props.data, slideIndex.value, ['header']));
105
+ const titleId = computed(() => resolveId(props.data, slideIndex.value, ['title']));
106
+ const subtitleId = computed(() => resolveId(props.data, slideIndex.value, ['subtitle']));
107
+ const stepId = (i: number) => resolveId(props.data, slideIndex.value, ['steps', i]);
108
+ </script>
@@ -1,65 +1,65 @@
1
- <template>
2
- <div
3
- v-html="html"
4
- :class="customClass"
5
- :style="computedStyle"
6
- ></div>
7
- </template>
8
-
9
- <script setup lang="ts">
10
- import { computed } from 'vue';
11
-
12
- const props = defineProps<{
13
- html: string;
14
- class?: string;
15
- style?: Record<string, string>;
16
- }>();
17
-
18
- const customClass = computed(() => props.class || '');
19
-
20
- const computedStyle = computed(() => {
21
- return {
22
- width: '100%',
23
- ...props.style,
24
- };
25
- });
26
- </script>
27
-
28
- <style scoped>
29
- /* Ensure HTML content respects container boundaries */
30
- :deep(*) {
31
- max-width: 100%;
32
- }
33
-
34
- /* Style common HTML elements to match Lumina theme */
35
- :deep(h1), :deep(h2), :deep(h3), :deep(h4), :deep(h5), :deep(h6) {
36
- color: var(--lumina-color-text);
37
- font-family: var(--lumina-font-heading);
38
- margin-top: var(--lumina-space-md);
39
- margin-bottom: var(--lumina-space-sm);
40
- }
41
-
42
- :deep(p) {
43
- color: var(--lumina-color-text);
44
- line-height: var(--lumina-leading-relaxed);
45
- margin-bottom: var(--lumina-space-md);
46
- }
47
-
48
- :deep(a) {
49
- color: var(--lumina-color-primary);
50
- text-decoration: none;
51
- }
52
-
53
- :deep(a:hover) {
54
- text-decoration: underline;
55
- }
56
-
57
- :deep(ul), :deep(ol) {
58
- margin-left: var(--lumina-space-lg);
59
- margin-bottom: var(--lumina-space-md);
60
- }
61
-
62
- :deep(li) {
63
- margin-bottom: var(--lumina-space-xs);
64
- }
65
- </style>
1
+ <template>
2
+ <div
3
+ v-html="html"
4
+ :class="customClass"
5
+ :style="computedStyle"
6
+ ></div>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import { computed } from 'vue';
11
+
12
+ const props = defineProps<{
13
+ html: string;
14
+ class?: string;
15
+ style?: Record<string, string>;
16
+ }>();
17
+
18
+ const customClass = computed(() => props.class || '');
19
+
20
+ const computedStyle = computed(() => {
21
+ return {
22
+ width: '100%',
23
+ ...props.style,
24
+ };
25
+ });
26
+ </script>
27
+
28
+ <style scoped>
29
+ /* Ensure HTML content respects container boundaries */
30
+ :deep(*) {
31
+ max-width: 100%;
32
+ }
33
+
34
+ /* Style common HTML elements to match Lumina theme */
35
+ :deep(h1), :deep(h2), :deep(h3), :deep(h4), :deep(h5), :deep(h6) {
36
+ color: var(--lumina-color-text);
37
+ font-family: var(--lumina-font-heading);
38
+ margin-top: var(--lumina-space-md);
39
+ margin-bottom: var(--lumina-space-sm);
40
+ }
41
+
42
+ :deep(p) {
43
+ color: var(--lumina-color-text);
44
+ line-height: var(--lumina-leading-relaxed);
45
+ margin-bottom: var(--lumina-space-md);
46
+ }
47
+
48
+ :deep(a) {
49
+ color: var(--lumina-color-primary);
50
+ text-decoration: none;
51
+ }
52
+
53
+ :deep(a:hover) {
54
+ text-decoration: underline;
55
+ }
56
+
57
+ :deep(ul), :deep(ol) {
58
+ margin-left: var(--lumina-space-lg);
59
+ margin-bottom: var(--lumina-space-md);
60
+ }
61
+
62
+ :deep(li) {
63
+ margin-bottom: var(--lumina-space-xs);
64
+ }
65
+ </style>
@@ -1,81 +1,81 @@
1
- <template>
2
- <component :is="href ? 'a' : 'div'" :href="href || undefined" :target="href ? (target || '_blank') : undefined"
3
- :rel="href ? 'noopener noreferrer' : undefined"
4
- :class="[containerClass, customClass, href && 'cursor-pointer hover:opacity-90 transition-opacity']"
5
- :style="containerStyle">
6
- <img :src="src" :alt="alt || ''" :class="[
7
- 'w-full h-full transition-opacity duration-700',
8
- objectFitClass,
9
- roundedClass,
10
- isLoaded ? 'opacity-100' : 'opacity-0'
11
- ]" :style="imageStyle" @load="onLoad" @error="onError" />
12
- </component>
13
- </template>
14
-
15
- <script setup lang="ts">
16
- import { ref, computed } from 'vue';
17
-
18
- const props = defineProps<{
19
- src: string;
20
- alt?: string;
21
- fill?: boolean;
22
- fit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
23
- position?: string;
24
- rounded?: string;
25
- containerClass?: any;
26
- containerStyle?: any;
27
- href?: string;
28
- target?: '_blank' | '_self';
29
- class?: any;
30
- style?: Record<string, string>;
31
- }>();
32
-
33
- const isLoaded = ref(false);
34
- const customClass = computed(() => props.class || '');
35
-
36
- const onLoad = () => {
37
- isLoaded.value = true;
38
- };
39
-
40
- const onError = (e: any) => {
41
- isLoaded.value = true;
42
- e.target.src = 'https://placehold.co/800x600/1a1a1a/666?text=Image+Not+Found';
43
- };
44
-
45
- const objectFitClass = computed(() => {
46
- if (props.fit) {
47
- const fitMap: Record<string, string> = {
48
- 'cover': 'object-cover',
49
- 'contain': 'object-contain',
50
- 'fill': 'object-fill',
51
- 'none': 'object-none',
52
- 'scale-down': 'object-scale-down',
53
- };
54
- return fitMap[props.fit] || 'object-cover';
55
- }
56
- return props.fill !== false ? 'object-cover' : 'object-contain';
57
- });
58
-
59
- const imageStyle = computed(() => {
60
- const style: Record<string, string> = {
61
- ...props.style,
62
- };
63
- if (props.position) {
64
- style.objectPosition = props.position;
65
- }
66
- return style;
67
- });
68
-
69
- const roundedClass = computed(() => {
70
- if (props.fill !== false && !props.rounded) return '';
71
- const map: Record<string, string> = {
72
- 'none': 'rounded-none',
73
- 'sm': 'rounded-sm',
74
- 'md': 'rounded-md',
75
- 'lg': 'rounded-lg',
76
- 'xl': 'rounded-xl',
77
- 'full': 'rounded-full',
78
- };
79
- return map[props.rounded || 'lg'] || 'rounded-lg';
80
- });
81
- </script>
1
+ <template>
2
+ <component :is="href ? 'a' : 'div'" :href="href || undefined" :target="href ? (target || '_blank') : undefined"
3
+ :rel="href ? 'noopener noreferrer' : undefined"
4
+ :class="[containerClass, customClass, href && 'cursor-pointer hover:opacity-90 transition-opacity']"
5
+ :style="containerStyle">
6
+ <img :src="src" :alt="alt || ''" :class="[
7
+ 'w-full h-full transition-opacity duration-700',
8
+ objectFitClass,
9
+ roundedClass,
10
+ isLoaded ? 'opacity-100' : 'opacity-0'
11
+ ]" :style="imageStyle" @load="onLoad" @error="onError" />
12
+ </component>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { ref, computed } from 'vue';
17
+
18
+ const props = defineProps<{
19
+ src: string;
20
+ alt?: string;
21
+ fill?: boolean;
22
+ fit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
23
+ position?: string;
24
+ rounded?: string;
25
+ containerClass?: any;
26
+ containerStyle?: any;
27
+ href?: string;
28
+ target?: '_blank' | '_self';
29
+ class?: any;
30
+ style?: Record<string, string>;
31
+ }>();
32
+
33
+ const isLoaded = ref(false);
34
+ const customClass = computed(() => props.class || '');
35
+
36
+ const onLoad = () => {
37
+ isLoaded.value = true;
38
+ };
39
+
40
+ const onError = (e: any) => {
41
+ isLoaded.value = true;
42
+ e.target.src = 'https://placehold.co/800x600/1a1a1a/666?text=Image+Not+Found';
43
+ };
44
+
45
+ const objectFitClass = computed(() => {
46
+ if (props.fit) {
47
+ const fitMap: Record<string, string> = {
48
+ 'cover': 'object-cover',
49
+ 'contain': 'object-contain',
50
+ 'fill': 'object-fill',
51
+ 'none': 'object-none',
52
+ 'scale-down': 'object-scale-down',
53
+ };
54
+ return fitMap[props.fit] || 'object-cover';
55
+ }
56
+ return props.fill !== false ? 'object-cover' : 'object-contain';
57
+ });
58
+
59
+ const imageStyle = computed(() => {
60
+ const style: Record<string, string> = {
61
+ ...props.style,
62
+ };
63
+ if (props.position) {
64
+ style.objectPosition = props.position;
65
+ }
66
+ return style;
67
+ });
68
+
69
+ const roundedClass = computed(() => {
70
+ if (props.fill !== false && !props.rounded) return '';
71
+ const map: Record<string, string> = {
72
+ 'none': 'rounded-none',
73
+ 'sm': 'rounded-sm',
74
+ 'md': 'rounded-md',
75
+ 'lg': 'rounded-lg',
76
+ 'xl': 'rounded-xl',
77
+ 'full': 'rounded-full',
78
+ };
79
+ return map[props.rounded || 'lg'] || 'rounded-lg';
80
+ });
81
+ </script>