@wishbone-media/spark 0.37.0 → 0.38.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wishbone-media/spark",
3
- "version": "0.37.0",
3
+ "version": "0.38.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -65,6 +65,7 @@
65
65
  "access": "public"
66
66
  },
67
67
  "dependencies": {
68
+ "@fancyapps/ui": "^6.1.13",
68
69
  "@floating-ui/vue": "^1.1.11",
69
70
  "@googlemaps/js-api-loader": "^2.0.2"
70
71
  }
@@ -0,0 +1,7 @@
1
+ /*
2
+ * SparkFancybox styles — re-exports Fancybox CSS.
3
+ *
4
+ * Import in consuming app:
5
+ * @import '@wishbone-media/spark/assets/css/spark-fancybox.css';
6
+ */
7
+ @import '@fancyapps/ui/dist/fancybox/fancybox.css';
@@ -0,0 +1,59 @@
1
+ <template>
2
+ <div ref="containerRef">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ /**
9
+ * Vue 3 wrapper around Fancybox.
10
+ * Wraps a container element and binds Fancybox to all [data-fancybox] children.
11
+ * Handles lifecycle: bind on mount, rebind on update (for dynamic content), unbind on unmount.
12
+ *
13
+ * Usage:
14
+ * <SparkFancybox>
15
+ * <a href="full.jpg" data-fancybox="gallery" data-caption="Photo #1">
16
+ * <img src="thumb.jpg" />
17
+ * </a>
18
+ * </SparkFancybox>
19
+ */
20
+ import { ref, onMounted, onUpdated, onUnmounted } from 'vue'
21
+ import { Fancybox } from '@fancyapps/ui/dist/fancybox/'
22
+
23
+ const props = defineProps({
24
+ /** Fancybox configuration options */
25
+ options: {
26
+ type: Object,
27
+ default: () => ({}),
28
+ },
29
+ /** CSS selector for bindable elements within the container */
30
+ selector: {
31
+ type: String,
32
+ default: '[data-fancybox]',
33
+ },
34
+ })
35
+
36
+ const containerRef = ref(null)
37
+
38
+ function bind() {
39
+ if (!containerRef.value) return
40
+ Fancybox.bind(containerRef.value, props.selector, { ...props.options })
41
+ }
42
+
43
+ function unbind() {
44
+ if (!containerRef.value) return
45
+ Fancybox.unbind(containerRef.value)
46
+ }
47
+
48
+ onMounted(() => bind())
49
+
50
+ onUpdated(() => {
51
+ unbind()
52
+ bind()
53
+ })
54
+
55
+ onUnmounted(() => {
56
+ unbind()
57
+ Fancybox.close()
58
+ })
59
+ </script>
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <div>
3
3
  <label v-if="label" class="block text-sm font-medium text-gray-700 mb-2">{{ label }}</label>
4
- <div v-if="modelValue" class="mb-2 relative inline-block">
5
- <a :href="modelValue" target="_blank" rel="noopener noreferrer">
4
+ <SparkFancybox v-if="modelValue" class="mb-2 relative inline-block">
5
+ <a :href="modelValue" data-fancybox="image-upload" :data-caption="label || ''">
6
6
  <img
7
7
  :src="modelValue"
8
8
  :alt="label || 'Image preview'"
@@ -17,7 +17,7 @@
17
17
  >
18
18
  <font-awesome-icon :icon="Icons.farXmark" class="text-xs" />
19
19
  </button>
20
- </div>
20
+ </SparkFancybox>
21
21
  <div
22
22
  v-if="!modelValue"
23
23
  class="relative rounded-md transition-colors"
@@ -51,6 +51,7 @@
51
51
  import { ref, inject } from 'vue'
52
52
  import { Icons } from '@/plugins/fontawesome'
53
53
  import { sparkNotificationService } from '@/composables/sparkNotificationService'
54
+ import SparkFancybox from './SparkFancybox.vue'
54
55
 
55
56
  const props = defineProps({
56
57
  modelValue: {
@@ -8,6 +8,7 @@ export { default as SparkButton } from './SparkButton.vue'
8
8
  export { default as SparkButtonGroup } from './SparkButtonGroup.vue'
9
9
  export { default as SparkCard } from './SparkCard.vue'
10
10
  export { default as SparkEntityBadge } from './SparkEntityBadge.vue'
11
+ export { default as SparkFancybox } from './SparkFancybox.vue'
11
12
  export { default as SparkTooltip } from './SparkTooltip.vue'
12
13
  export { default as SparkFileDragUpload } from './SparkFileDragUpload.vue'
13
14
  export { default as SparkImageUpload } from './SparkImageUpload.vue'
@@ -1,16 +1,18 @@
1
1
  /**
2
2
  * Spark Image Renderer
3
3
  *
4
- * Renders image thumbnails
4
+ * Renders image thumbnails with optional Fancybox lightbox support.
5
5
  *
6
6
  * Usage:
7
7
  * {
8
8
  * data: 'avatar',
9
9
  * renderer: 'spark.image',
10
10
  * rendererConfig: {
11
- * size: 'md', // 'sm' | 'md' | 'lg'
12
- * rounded: true, // true for circular, false for square
13
- * alt: 'User avatar' // Alt text (or use row property name)
11
+ * size: 'md', // 'sm' | 'md' | 'lg'
12
+ * rounded: true, // true for circular, false for square
13
+ * alt: 'User avatar', // Alt text (or use row property name)
14
+ * lightbox: true, // Enable click-to-enlarge via Fancybox (requires SparkFancybox wrapper)
15
+ * group: 'gallery', // Fancybox gallery group name (default: 'table-images')
14
16
  * }
15
17
  * }
16
18
  */
@@ -37,6 +39,8 @@ export const imageRenderer = (sparkTable) => {
37
39
  const size = config.size || 'md'
38
40
  const sizeClass = sizes[size] || sizes.md
39
41
  const rounded = config.rounded !== false // Default to true
42
+ const lightbox = config.lightbox || false
43
+ const group = config.group || 'table-images'
40
44
 
41
45
  // Determine alt text
42
46
  let alt = config.alt || ''
@@ -56,6 +60,17 @@ export const imageRenderer = (sparkTable) => {
56
60
  img.classList.add('rounded')
57
61
  }
58
62
 
59
- td.appendChild(img)
63
+ if (lightbox) {
64
+ // Wrap in <a> with data-fancybox for lightbox support
65
+ const link = document.createElement('a')
66
+ link.href = value
67
+ link.setAttribute('data-fancybox', group)
68
+ if (alt) link.setAttribute('data-caption', alt)
69
+ img.classList.add('cursor-pointer', 'hover:opacity-80', 'transition-opacity')
70
+ link.appendChild(img)
71
+ td.appendChild(link)
72
+ } else {
73
+ td.appendChild(img)
74
+ }
60
75
  }
61
76
  }