@flightdev/image 0.0.2

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.
@@ -0,0 +1,129 @@
1
+ import * as React from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ // src/components/react.tsx
5
+ function buildOptimizedUrl(src, options, baseUrl = "") {
6
+ const params = new URLSearchParams();
7
+ if (options.width) params.set("w", options.width.toString());
8
+ if (options.height) params.set("h", options.height.toString());
9
+ if (options.format && options.format !== "auto") params.set("f", options.format);
10
+ if (options.quality) params.set("q", options.quality.toString());
11
+ if (options.fit) params.set("fit", options.fit);
12
+ const queryString = params.toString();
13
+ const normalizedSrc = src.startsWith("/") ? src : `/${src}`;
14
+ return `${baseUrl}/_flight/image${normalizedSrc}${queryString ? "?" + queryString : ""}`;
15
+ }
16
+ var Image = React.forwardRef(function Image2({
17
+ src,
18
+ alt,
19
+ width,
20
+ height,
21
+ format = "auto",
22
+ quality = 80,
23
+ fit = "cover",
24
+ priority = false,
25
+ placeholder = "empty",
26
+ blurDataUrl,
27
+ sizes,
28
+ baseUrl = "",
29
+ loading,
30
+ onLoad,
31
+ onError,
32
+ style,
33
+ className,
34
+ ...rest
35
+ }, ref) {
36
+ const [isLoaded, setIsLoaded] = React.useState(false);
37
+ const [error, setError] = React.useState(false);
38
+ const optimizedSrc = buildOptimizedUrl(src, { width, height, format, quality, fit }, baseUrl);
39
+ const srcSet = React.useMemo(() => {
40
+ if (!width) return void 0;
41
+ const widths = [width, width * 1.5, width * 2].map(Math.round);
42
+ return widths.map((w) => `${buildOptimizedUrl(src, { width: w, height: height ? Math.round(height * w / width) : void 0, format, quality, fit }, baseUrl)} ${w}w`).join(", ");
43
+ }, [src, width, height, format, quality, fit, baseUrl]);
44
+ const defaultSizes = width ? `(max-width: ${width}px) 100vw, ${width}px` : void 0;
45
+ const handleLoad = React.useCallback((e) => {
46
+ setIsLoaded(true);
47
+ onLoad?.(e);
48
+ }, [onLoad]);
49
+ const handleError = React.useCallback((e) => {
50
+ setError(true);
51
+ onError?.(e);
52
+ }, [onError]);
53
+ const placeholderStyle = placeholder === "blur" && blurDataUrl && !isLoaded ? {
54
+ backgroundImage: `url(${blurDataUrl})`,
55
+ backgroundSize: "cover",
56
+ backgroundPosition: "center",
57
+ filter: "blur(20px)",
58
+ transform: "scale(1.1)"
59
+ } : void 0;
60
+ const containerStyle = {
61
+ position: "relative",
62
+ overflow: "hidden",
63
+ width: width ? `${width}px` : void 0,
64
+ height: height ? `${height}px` : void 0,
65
+ ...style
66
+ };
67
+ const imageStyle = {
68
+ width: "100%",
69
+ height: "100%",
70
+ objectFit: fit,
71
+ opacity: isLoaded ? 1 : 0,
72
+ transition: "opacity 0.3s ease-in-out"
73
+ };
74
+ return /* @__PURE__ */ jsxs(
75
+ "div",
76
+ {
77
+ style: containerStyle,
78
+ className,
79
+ children: [
80
+ placeholder === "blur" && blurDataUrl && !isLoaded && /* @__PURE__ */ jsx(
81
+ "div",
82
+ {
83
+ style: {
84
+ position: "absolute",
85
+ inset: 0,
86
+ ...placeholderStyle
87
+ },
88
+ "aria-hidden": "true"
89
+ }
90
+ ),
91
+ /* @__PURE__ */ jsx(
92
+ "img",
93
+ {
94
+ ref,
95
+ src: optimizedSrc,
96
+ srcSet,
97
+ sizes: sizes ?? defaultSizes,
98
+ alt,
99
+ width,
100
+ height,
101
+ loading: priority ? "eager" : loading ?? "lazy",
102
+ decoding: priority ? "sync" : "async",
103
+ fetchPriority: priority ? "high" : void 0,
104
+ onLoad: handleLoad,
105
+ onError: handleError,
106
+ style: imageStyle,
107
+ ...rest
108
+ }
109
+ ),
110
+ priority && typeof document !== "undefined" && /* @__PURE__ */ jsx(
111
+ "link",
112
+ {
113
+ rel: "preload",
114
+ as: "image",
115
+ href: optimizedSrc,
116
+ imageSrcSet: srcSet,
117
+ imageSizes: sizes ?? defaultSizes
118
+ }
119
+ )
120
+ ]
121
+ }
122
+ );
123
+ });
124
+ Image.displayName = "FlightImage";
125
+ var react_default = Image;
126
+
127
+ export { Image, react_default as default };
128
+ //# sourceMappingURL=react.js.map
129
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/react.tsx"],"names":["Image"],"mappings":"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","file":"react.js","sourcesContent":["/**\r\n * React Image Component for @flightdev/image\r\n * \r\n * @example\r\n * ```tsx\r\n * import { Image } from '@flightdev/image/react';\r\n * \r\n * <Image\r\n * src=\"/hero.jpg\"\r\n * alt=\"Hero\"\r\n * width={800}\r\n * height={600}\r\n * priority\r\n * placeholder=\"blur\"\r\n * />\r\n * ```\r\n */\r\n\r\nimport * as React from 'react';\r\nimport type { ImageOptimizeOptions, ImageFormat, ImageFit } from '../index.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\r\n /** Image source URL or path */\r\n src: string;\r\n /** Alt text (required for accessibility) */\r\n alt: string;\r\n /** Target width */\r\n width?: number;\r\n /** Target height */\r\n height?: number;\r\n /** Output format */\r\n format?: ImageFormat;\r\n /** Quality (1-100) */\r\n quality?: number;\r\n /** Fit mode */\r\n fit?: ImageFit;\r\n /** Priority loading (preload) */\r\n priority?: boolean;\r\n /** Placeholder type */\r\n placeholder?: 'blur' | 'empty' | 'none';\r\n /** Custom blur data URL */\r\n blurDataUrl?: string;\r\n /** Sizes attribute for responsive images */\r\n sizes?: string;\r\n /** Image service base URL */\r\n baseUrl?: string;\r\n /** Loading behavior */\r\n loading?: 'lazy' | 'eager';\r\n /** Callback when image loads */\r\n onLoad?: React.ReactEventHandler<HTMLImageElement>;\r\n /** Callback on error */\r\n onError?: React.ReactEventHandler<HTMLImageElement>;\r\n}\r\n\r\n// ============================================================================\r\n// URL Builder\r\n// ============================================================================\r\n\r\nfunction buildOptimizedUrl(\r\n src: string,\r\n options: ImageOptimizeOptions,\r\n baseUrl: string = ''\r\n): string {\r\n const params = new URLSearchParams();\r\n\r\n if (options.width) params.set('w', options.width.toString());\r\n if (options.height) params.set('h', options.height.toString());\r\n if (options.format && options.format !== 'auto') params.set('f', options.format);\r\n if (options.quality) params.set('q', options.quality.toString());\r\n if (options.fit) params.set('fit', options.fit);\r\n\r\n const queryString = params.toString();\r\n const normalizedSrc = src.startsWith('/') ? src : `/${src}`;\r\n return `${baseUrl}/_flight/image${normalizedSrc}${queryString ? '?' + queryString : ''}`;\r\n}\r\n\r\n// ============================================================================\r\n// Image Component\r\n// ============================================================================\r\n\r\n/**\r\n * Optimized Image component for React\r\n * \r\n * Automatically handles:\r\n * - Lazy loading with IntersectionObserver\r\n * - Blur placeholder during load\r\n * - Responsive srcset generation\r\n * - Format optimization\r\n */\r\nexport const Image = React.forwardRef<HTMLImageElement, ImageProps>(function Image(\r\n {\r\n src,\r\n alt,\r\n width,\r\n height,\r\n format = 'auto',\r\n quality = 80,\r\n fit = 'cover',\r\n priority = false,\r\n placeholder = 'empty',\r\n blurDataUrl,\r\n sizes,\r\n baseUrl = '',\r\n loading,\r\n onLoad,\r\n onError,\r\n style,\r\n className,\r\n ...rest\r\n },\r\n ref\r\n) {\r\n const [isLoaded, setIsLoaded] = React.useState(false);\r\n const [error, setError] = React.useState(false);\r\n\r\n // Build optimized URL\r\n const optimizedSrc = buildOptimizedUrl(src, { width, height, format, quality, fit }, baseUrl);\r\n\r\n // Generate srcset for responsive images\r\n const srcSet = React.useMemo(() => {\r\n if (!width) return undefined;\r\n\r\n const widths = [width, width * 1.5, width * 2].map(Math.round);\r\n return widths\r\n .map(w => `${buildOptimizedUrl(src, { width: w, height: height ? Math.round(height * w / width) : undefined, format, quality, fit }, baseUrl)} ${w}w`)\r\n .join(', ');\r\n }, [src, width, height, format, quality, fit, baseUrl]);\r\n\r\n // Default sizes if not provided\r\n const defaultSizes = width ? `(max-width: ${width}px) 100vw, ${width}px` : undefined;\r\n\r\n // Handle load\r\n const handleLoad = React.useCallback((e: React.SyntheticEvent<HTMLImageElement>) => {\r\n setIsLoaded(true);\r\n onLoad?.(e);\r\n }, [onLoad]);\r\n\r\n // Handle error\r\n const handleError = React.useCallback((e: React.SyntheticEvent<HTMLImageElement>) => {\r\n setError(true);\r\n onError?.(e);\r\n }, [onError]);\r\n\r\n // Placeholder styles\r\n const placeholderStyle: React.CSSProperties | undefined =\r\n placeholder === 'blur' && blurDataUrl && !isLoaded\r\n ? {\r\n backgroundImage: `url(${blurDataUrl})`,\r\n backgroundSize: 'cover',\r\n backgroundPosition: 'center',\r\n filter: 'blur(20px)',\r\n transform: 'scale(1.1)',\r\n }\r\n : undefined;\r\n\r\n // Container styles for placeholder\r\n const containerStyle: React.CSSProperties = {\r\n position: 'relative',\r\n overflow: 'hidden',\r\n width: width ? `${width}px` : undefined,\r\n height: height ? `${height}px` : undefined,\r\n ...style,\r\n };\r\n\r\n // Image styles\r\n const imageStyle: React.CSSProperties = {\r\n width: '100%',\r\n height: '100%',\r\n objectFit: fit as React.CSSProperties['objectFit'],\r\n opacity: isLoaded ? 1 : 0,\r\n transition: 'opacity 0.3s ease-in-out',\r\n };\r\n\r\n return (\r\n <div\r\n style={containerStyle}\r\n className={className}\r\n >\r\n {/* Blur placeholder background */}\r\n {placeholder === 'blur' && blurDataUrl && !isLoaded && (\r\n <div\r\n style={{\r\n position: 'absolute',\r\n inset: 0,\r\n ...placeholderStyle,\r\n }}\r\n aria-hidden=\"true\"\r\n />\r\n )}\r\n\r\n {/* Actual image */}\r\n <img\r\n ref={ref}\r\n src={optimizedSrc}\r\n srcSet={srcSet}\r\n sizes={sizes ?? defaultSizes}\r\n alt={alt}\r\n width={width}\r\n height={height}\r\n loading={priority ? 'eager' : loading ?? 'lazy'}\r\n decoding={priority ? 'sync' : 'async'}\r\n fetchPriority={priority ? 'high' : undefined}\r\n onLoad={handleLoad}\r\n onError={handleError}\r\n style={imageStyle}\r\n {...rest}\r\n />\r\n\r\n {/* Preload link for priority images */}\r\n {priority && typeof document !== 'undefined' && (\r\n <link\r\n rel=\"preload\"\r\n as=\"image\"\r\n href={optimizedSrc}\r\n imageSrcSet={srcSet}\r\n imageSizes={sizes ?? defaultSizes}\r\n />\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\nImage.displayName = 'FlightImage';\r\n\r\nexport default Image;\r\n"]}
@@ -0,0 +1,44 @@
1
+ import { Component, JSX } from 'solid-js';
2
+ import { ImageFormat, ImageFit } from '../index.js';
3
+ import 'node:buffer';
4
+
5
+ /** @jsxImportSource solid-js */
6
+ /**
7
+ * Solid Image Component for @flightdev/image
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * import { Image } from '@flightdev/image/solid';
12
+ *
13
+ * <Image
14
+ * src="/hero.jpg"
15
+ * alt="Hero"
16
+ * width={800}
17
+ * height={600}
18
+ * priority
19
+ * placeholder="blur"
20
+ * />
21
+ * ```
22
+ */
23
+
24
+ interface ImageProps {
25
+ src: string;
26
+ alt: string;
27
+ width?: number;
28
+ height?: number;
29
+ format?: ImageFormat;
30
+ quality?: number;
31
+ fit?: ImageFit;
32
+ priority?: boolean;
33
+ placeholder?: 'blur' | 'empty' | 'none';
34
+ blurDataUrl?: string;
35
+ sizes?: string;
36
+ baseUrl?: string;
37
+ class?: string;
38
+ style?: JSX.CSSProperties;
39
+ onLoad?: () => void;
40
+ onError?: () => void;
41
+ }
42
+ declare const Image: Component<ImageProps>;
43
+
44
+ export { Image, type ImageProps, Image as default };
@@ -0,0 +1,101 @@
1
+ import { createSignal, createMemo, Show } from 'solid-js';
2
+ import { jsxs, jsx } from 'solid-js/jsx-runtime';
3
+
4
+ // src/components/solid.tsx
5
+ function buildOptimizedUrl(src, options, baseUrl = "") {
6
+ const params = new URLSearchParams();
7
+ if (options.width) params.set("w", options.width.toString());
8
+ if (options.height) params.set("h", options.height.toString());
9
+ if (options.format && options.format !== "auto") params.set("f", options.format);
10
+ if (options.quality) params.set("q", options.quality.toString());
11
+ if (options.fit) params.set("fit", options.fit);
12
+ const queryString = params.toString();
13
+ const normalizedSrc = src.startsWith("/") ? src : `/${src}`;
14
+ return `${baseUrl}/_flight/image${normalizedSrc}${queryString ? "?" + queryString : ""}`;
15
+ }
16
+ var Image = (props) => {
17
+ const [isLoaded, setIsLoaded] = createSignal(false);
18
+ const [error, setError] = createSignal(false);
19
+ const format = () => props.format ?? "auto";
20
+ const quality = () => props.quality ?? 80;
21
+ const fit = () => props.fit ?? "cover";
22
+ const priority = () => props.priority ?? false;
23
+ const placeholder = () => props.placeholder ?? "empty";
24
+ const baseUrl = () => props.baseUrl ?? "";
25
+ const optimizedSrc = createMemo(
26
+ () => buildOptimizedUrl(props.src, {
27
+ width: props.width,
28
+ height: props.height,
29
+ format: format(),
30
+ quality: quality(),
31
+ fit: fit()
32
+ }, baseUrl())
33
+ );
34
+ const srcSet = createMemo(() => {
35
+ if (!props.width) return void 0;
36
+ const widths = [props.width, Math.round(props.width * 1.5), props.width * 2];
37
+ return widths.map((w) => {
38
+ const h = props.height ? Math.round(props.height * w / props.width) : void 0;
39
+ return `${buildOptimizedUrl(props.src, { width: w, height: h, format: format(), quality: quality(), fit: fit() }, baseUrl())} ${w}w`;
40
+ }).join(", ");
41
+ });
42
+ const defaultSizes = createMemo(
43
+ () => props.width ? `(max-width: ${props.width}px) 100vw, ${props.width}px` : void 0
44
+ );
45
+ const handleLoad = () => {
46
+ setIsLoaded(true);
47
+ props.onLoad?.();
48
+ };
49
+ const handleError = () => {
50
+ setError(true);
51
+ props.onError?.();
52
+ };
53
+ const containerStyle = () => ({
54
+ position: "relative",
55
+ overflow: "hidden",
56
+ width: props.width ? `${props.width}px` : void 0,
57
+ height: props.height ? `${props.height}px` : void 0,
58
+ ...props.style
59
+ });
60
+ const imageStyle = () => ({
61
+ width: "100%",
62
+ height: "100%",
63
+ "object-fit": fit(),
64
+ opacity: isLoaded() ? 1 : 0,
65
+ transition: "opacity 0.3s ease-in-out"
66
+ });
67
+ const blurStyle = () => ({
68
+ position: "absolute",
69
+ inset: "0",
70
+ "background-image": props.blurDataUrl ? `url(${props.blurDataUrl})` : void 0,
71
+ "background-size": "cover",
72
+ "background-position": "center",
73
+ filter: "blur(20px)",
74
+ transform: "scale(1.1)"
75
+ });
76
+ return /* @__PURE__ */ jsxs("div", { style: containerStyle(), class: props.class, children: [
77
+ /* @__PURE__ */ jsx(Show, { when: placeholder() === "blur" && props.blurDataUrl && !isLoaded(), children: /* @__PURE__ */ jsx("div", { style: blurStyle(), "aria-hidden": "true" }) }),
78
+ /* @__PURE__ */ jsx(
79
+ "img",
80
+ {
81
+ src: optimizedSrc(),
82
+ srcset: srcSet(),
83
+ sizes: props.sizes ?? defaultSizes(),
84
+ alt: props.alt,
85
+ width: props.width,
86
+ height: props.height,
87
+ loading: priority() ? "eager" : "lazy",
88
+ decoding: priority() ? "sync" : "async",
89
+ fetchpriority: priority() ? "high" : void 0,
90
+ onLoad: handleLoad,
91
+ onError: handleError,
92
+ style: imageStyle()
93
+ }
94
+ )
95
+ ] });
96
+ };
97
+ var solid_default = Image;
98
+
99
+ export { Image, solid_default as default };
100
+ //# sourceMappingURL=solid.js.map
101
+ //# sourceMappingURL=solid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/solid.tsx"],"names":[],"mappings":"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","file":"solid.js","sourcesContent":["/** @jsxImportSource solid-js */\r\n/**\r\n * Solid Image Component for @flightdev/image\r\n * \r\n * @example\r\n * ```tsx\r\n * import { Image } from '@flightdev/image/solid';\r\n * \r\n * <Image\r\n * src=\"/hero.jpg\"\r\n * alt=\"Hero\"\r\n * width={800}\r\n * height={600}\r\n * priority\r\n * placeholder=\"blur\"\r\n * />\r\n * ```\r\n */\r\n\r\nimport { createSignal, createMemo, Show, type JSX, type Component } from 'solid-js';\r\nimport type { ImageFormat, ImageFit } from '../index.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface ImageProps {\r\n src: string;\r\n alt: string;\r\n width?: number;\r\n height?: number;\r\n format?: ImageFormat;\r\n quality?: number;\r\n fit?: ImageFit;\r\n priority?: boolean;\r\n placeholder?: 'blur' | 'empty' | 'none';\r\n blurDataUrl?: string;\r\n sizes?: string;\r\n baseUrl?: string;\r\n class?: string;\r\n style?: JSX.CSSProperties;\r\n onLoad?: () => void;\r\n onError?: () => void;\r\n}\r\n\r\n// ============================================================================\r\n// URL Builder\r\n// ============================================================================\r\n\r\nfunction buildOptimizedUrl(\r\n src: string,\r\n options: { width?: number; height?: number; format?: ImageFormat; quality?: number; fit?: ImageFit },\r\n baseUrl: string = ''\r\n): string {\r\n const params = new URLSearchParams();\r\n\r\n if (options.width) params.set('w', options.width.toString());\r\n if (options.height) params.set('h', options.height.toString());\r\n if (options.format && options.format !== 'auto') params.set('f', options.format);\r\n if (options.quality) params.set('q', options.quality.toString());\r\n if (options.fit) params.set('fit', options.fit);\r\n\r\n const queryString = params.toString();\r\n const normalizedSrc = src.startsWith('/') ? src : `/${src}`;\r\n return `${baseUrl}/_flight/image${normalizedSrc}${queryString ? '?' + queryString : ''}`;\r\n}\r\n\r\n// ============================================================================\r\n// Solid Component\r\n// ============================================================================\r\n\r\nexport const Image: Component<ImageProps> = (props) => {\r\n const [isLoaded, setIsLoaded] = createSignal(false);\r\n const [error, setError] = createSignal(false);\r\n\r\n const format = () => props.format ?? 'auto';\r\n const quality = () => props.quality ?? 80;\r\n const fit = () => props.fit ?? 'cover';\r\n const priority = () => props.priority ?? false;\r\n const placeholder = () => props.placeholder ?? 'empty';\r\n const baseUrl = () => props.baseUrl ?? '';\r\n\r\n const optimizedSrc = createMemo(() =>\r\n buildOptimizedUrl(props.src, {\r\n width: props.width,\r\n height: props.height,\r\n format: format(),\r\n quality: quality(),\r\n fit: fit(),\r\n }, baseUrl())\r\n );\r\n\r\n const srcSet = createMemo(() => {\r\n if (!props.width) return undefined;\r\n\r\n const widths = [props.width, Math.round(props.width * 1.5), props.width * 2];\r\n return widths\r\n .map(w => {\r\n const h = props.height ? Math.round(props.height * w / props.width!) : undefined;\r\n return `${buildOptimizedUrl(props.src, { width: w, height: h, format: format(), quality: quality(), fit: fit() }, baseUrl())} ${w}w`;\r\n })\r\n .join(', ');\r\n });\r\n\r\n const defaultSizes = createMemo(() =>\r\n props.width ? `(max-width: ${props.width}px) 100vw, ${props.width}px` : undefined\r\n );\r\n\r\n const handleLoad = () => {\r\n setIsLoaded(true);\r\n props.onLoad?.();\r\n };\r\n\r\n const handleError = () => {\r\n setError(true);\r\n props.onError?.();\r\n };\r\n\r\n const containerStyle = (): JSX.CSSProperties => ({\r\n position: 'relative',\r\n overflow: 'hidden',\r\n width: props.width ? `${props.width}px` : undefined,\r\n height: props.height ? `${props.height}px` : undefined,\r\n ...props.style,\r\n });\r\n\r\n const imageStyle = (): JSX.CSSProperties => ({\r\n width: '100%',\r\n height: '100%',\r\n 'object-fit': fit() as 'cover' | 'contain' | 'fill' | 'none' | 'scale-down',\r\n opacity: isLoaded() ? 1 : 0,\r\n transition: 'opacity 0.3s ease-in-out',\r\n });\r\n\r\n const blurStyle = (): JSX.CSSProperties => ({\r\n position: 'absolute',\r\n inset: '0',\r\n 'background-image': props.blurDataUrl ? `url(${props.blurDataUrl})` : undefined,\r\n 'background-size': 'cover',\r\n 'background-position': 'center',\r\n filter: 'blur(20px)',\r\n transform: 'scale(1.1)',\r\n });\r\n\r\n return (\r\n <div style={containerStyle()} class={props.class}>\r\n {/* Blur placeholder */}\r\n <Show when={placeholder() === 'blur' && props.blurDataUrl && !isLoaded()}>\r\n <div style={blurStyle()} aria-hidden=\"true\" />\r\n </Show>\r\n\r\n {/* Image */}\r\n <img\r\n src={optimizedSrc()}\r\n srcset={srcSet()}\r\n sizes={props.sizes ?? defaultSizes()}\r\n alt={props.alt}\r\n width={props.width}\r\n height={props.height}\r\n loading={priority() ? 'eager' : 'lazy'}\r\n decoding={priority() ? 'sync' : 'async'}\r\n // @ts-ignore - fetchpriority is valid but not in types\r\n fetchpriority={priority() ? 'high' : undefined}\r\n onLoad={handleLoad}\r\n onError={handleError}\r\n style={imageStyle()}\r\n />\r\n </div>\r\n );\r\n};\r\n\r\nexport default Image;\r\n"]}
@@ -0,0 +1,70 @@
1
+ import { ImageFormat, ImageFit } from '../index.js';
2
+ import 'node:buffer';
3
+
4
+ /**
5
+ * Svelte Image Component for @flightdev/image
6
+ *
7
+ * This file exports utilities and types for a Svelte Image component.
8
+ * The actual Svelte component should be in a .svelte file.
9
+ *
10
+ * @example
11
+ * ```svelte
12
+ * <script>
13
+ * import { createImageProps } from '@flightdev/image/svelte';
14
+ *
15
+ * export let src;
16
+ * export let alt;
17
+ * export let width = undefined;
18
+ * export let height = undefined;
19
+ *
20
+ * $: imageProps = createImageProps({ src, alt, width, height });
21
+ * </script>
22
+ *
23
+ * <img {...imageProps} loading="lazy" />
24
+ * ```
25
+ */
26
+
27
+ interface SvelteImageOptions {
28
+ src: string;
29
+ alt: string;
30
+ width?: number;
31
+ height?: number;
32
+ format?: ImageFormat;
33
+ quality?: number;
34
+ fit?: ImageFit;
35
+ priority?: boolean;
36
+ baseUrl?: string;
37
+ }
38
+ interface SvelteImageProps {
39
+ src: string;
40
+ srcset?: string;
41
+ sizes?: string;
42
+ alt: string;
43
+ width?: number;
44
+ height?: number;
45
+ loading: 'lazy' | 'eager';
46
+ decoding: 'sync' | 'async';
47
+ fetchpriority?: 'high' | 'low' | 'auto';
48
+ style?: string;
49
+ }
50
+ /**
51
+ * Create props object for a Svelte img element
52
+ */
53
+ declare function createImageProps(options: SvelteImageOptions): SvelteImageProps;
54
+ /**
55
+ * Create blur placeholder style string
56
+ */
57
+ declare function createBlurPlaceholderStyle(blurDataUrl: string): string;
58
+ /**
59
+ * Svelte action for lazy loading images with blur placeholder
60
+ */
61
+ declare function lazyImage(node: HTMLImageElement, blurDataUrl?: string): {
62
+ destroy(): void;
63
+ };
64
+ declare const _default: {
65
+ createImageProps: typeof createImageProps;
66
+ createBlurPlaceholderStyle: typeof createBlurPlaceholderStyle;
67
+ lazyImage: typeof lazyImage;
68
+ };
69
+
70
+ export { type SvelteImageOptions, type SvelteImageProps, createBlurPlaceholderStyle, createImageProps, _default as default, lazyImage };
@@ -0,0 +1,89 @@
1
+ // src/components/svelte.ts
2
+ function buildOptimizedUrl(src, options, baseUrl = "") {
3
+ const params = new URLSearchParams();
4
+ if (options.width) params.set("w", options.width.toString());
5
+ if (options.height) params.set("h", options.height.toString());
6
+ if (options.format && options.format !== "auto") params.set("f", options.format);
7
+ if (options.quality) params.set("q", options.quality.toString());
8
+ if (options.fit) params.set("fit", options.fit);
9
+ const queryString = params.toString();
10
+ const normalizedSrc = src.startsWith("/") ? src : `/${src}`;
11
+ return `${baseUrl}/_flight/image${normalizedSrc}${queryString ? "?" + queryString : ""}`;
12
+ }
13
+ function createImageProps(options) {
14
+ const {
15
+ src,
16
+ alt,
17
+ width,
18
+ height,
19
+ format = "auto",
20
+ quality = 80,
21
+ fit = "cover",
22
+ priority = false,
23
+ baseUrl = ""
24
+ } = options;
25
+ const optimizedSrc = buildOptimizedUrl(src, { width, height, format, quality, fit }, baseUrl);
26
+ let srcset;
27
+ if (width) {
28
+ const widths = [width, Math.round(width * 1.5), width * 2];
29
+ srcset = widths.map((w) => {
30
+ const h = height ? Math.round(height * w / width) : void 0;
31
+ return `${buildOptimizedUrl(src, { width: w, height: h, format, quality, fit }, baseUrl)} ${w}w`;
32
+ }).join(", ");
33
+ }
34
+ const sizes = width ? `(max-width: ${width}px) 100vw, ${width}px` : void 0;
35
+ return {
36
+ src: optimizedSrc,
37
+ srcset,
38
+ sizes,
39
+ alt,
40
+ width,
41
+ height,
42
+ loading: priority ? "eager" : "lazy",
43
+ decoding: priority ? "sync" : "async",
44
+ fetchpriority: priority ? "high" : void 0,
45
+ style: `object-fit: ${fit}`
46
+ };
47
+ }
48
+ function createBlurPlaceholderStyle(blurDataUrl) {
49
+ return `
50
+ background-image: url(${blurDataUrl});
51
+ background-size: cover;
52
+ background-position: center;
53
+ filter: blur(20px);
54
+ transform: scale(1.1);
55
+ `.trim();
56
+ }
57
+ function lazyImage(node, blurDataUrl) {
58
+ if (blurDataUrl) {
59
+ const wrapper = node.parentElement;
60
+ if (wrapper) {
61
+ wrapper.style.position = "relative";
62
+ wrapper.style.overflow = "hidden";
63
+ const placeholder = document.createElement("div");
64
+ placeholder.style.cssText = `
65
+ position: absolute;
66
+ inset: 0;
67
+ ${createBlurPlaceholderStyle(blurDataUrl)}
68
+ transition: opacity 0.3s ease-in-out;
69
+ `;
70
+ wrapper.insertBefore(placeholder, node);
71
+ node.style.opacity = "0";
72
+ node.style.transition = "opacity 0.3s ease-in-out";
73
+ node.addEventListener("load", () => {
74
+ node.style.opacity = "1";
75
+ placeholder.style.opacity = "0";
76
+ setTimeout(() => placeholder.remove(), 300);
77
+ }, { once: true });
78
+ }
79
+ }
80
+ return {
81
+ destroy() {
82
+ }
83
+ };
84
+ }
85
+ var svelte_default = { createImageProps, createBlurPlaceholderStyle, lazyImage };
86
+
87
+ export { createBlurPlaceholderStyle, createImageProps, svelte_default as default, lazyImage };
88
+ //# sourceMappingURL=svelte.js.map
89
+ //# sourceMappingURL=svelte.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/svelte.ts"],"names":[],"mappings":"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","file":"svelte.js","sourcesContent":["/**\r\n * Svelte Image Component for @flightdev/image\r\n * \r\n * This file exports utilities and types for a Svelte Image component.\r\n * The actual Svelte component should be in a .svelte file.\r\n * \r\n * @example\r\n * ```svelte\r\n * <script>\r\n * import { createImageProps } from '@flightdev/image/svelte';\r\n * \r\n * export let src;\r\n * export let alt;\r\n * export let width = undefined;\r\n * export let height = undefined;\r\n * \r\n * $: imageProps = createImageProps({ src, alt, width, height });\r\n * </script>\r\n * \r\n * <img {...imageProps} loading=\"lazy\" />\r\n * ```\r\n */\r\n\r\nimport type { ImageFormat, ImageFit } from '../index.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface SvelteImageOptions {\r\n src: string;\r\n alt: string;\r\n width?: number;\r\n height?: number;\r\n format?: ImageFormat;\r\n quality?: number;\r\n fit?: ImageFit;\r\n priority?: boolean;\r\n baseUrl?: string;\r\n}\r\n\r\nexport interface SvelteImageProps {\r\n src: string;\r\n srcset?: string;\r\n sizes?: string;\r\n alt: string;\r\n width?: number;\r\n height?: number;\r\n loading: 'lazy' | 'eager';\r\n decoding: 'sync' | 'async';\r\n fetchpriority?: 'high' | 'low' | 'auto';\r\n style?: string;\r\n}\r\n\r\n// ============================================================================\r\n// URL Builder\r\n// ============================================================================\r\n\r\nfunction buildOptimizedUrl(\r\n src: string,\r\n options: { width?: number; height?: number; format?: ImageFormat; quality?: number; fit?: ImageFit },\r\n baseUrl: string = ''\r\n): string {\r\n const params = new URLSearchParams();\r\n\r\n if (options.width) params.set('w', options.width.toString());\r\n if (options.height) params.set('h', options.height.toString());\r\n if (options.format && options.format !== 'auto') params.set('f', options.format);\r\n if (options.quality) params.set('q', options.quality.toString());\r\n if (options.fit) params.set('fit', options.fit);\r\n\r\n const queryString = params.toString();\r\n const normalizedSrc = src.startsWith('/') ? src : `/${src}`;\r\n return `${baseUrl}/_flight/image${normalizedSrc}${queryString ? '?' + queryString : ''}`;\r\n}\r\n\r\n// ============================================================================\r\n// Svelte Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Create props object for a Svelte img element\r\n */\r\nexport function createImageProps(options: SvelteImageOptions): SvelteImageProps {\r\n const {\r\n src,\r\n alt,\r\n width,\r\n height,\r\n format = 'auto',\r\n quality = 80,\r\n fit = 'cover',\r\n priority = false,\r\n baseUrl = '',\r\n } = options;\r\n\r\n const optimizedSrc = buildOptimizedUrl(src, { width, height, format, quality, fit }, baseUrl);\r\n\r\n let srcset: string | undefined;\r\n if (width) {\r\n const widths = [width, Math.round(width * 1.5), width * 2];\r\n srcset = widths\r\n .map(w => {\r\n const h = height ? Math.round(height * w / width) : undefined;\r\n return `${buildOptimizedUrl(src, { width: w, height: h, format, quality, fit }, baseUrl)} ${w}w`;\r\n })\r\n .join(', ');\r\n }\r\n\r\n const sizes = width ? `(max-width: ${width}px) 100vw, ${width}px` : undefined;\r\n\r\n return {\r\n src: optimizedSrc,\r\n srcset,\r\n sizes,\r\n alt,\r\n width,\r\n height,\r\n loading: priority ? 'eager' : 'lazy',\r\n decoding: priority ? 'sync' : 'async',\r\n fetchpriority: priority ? 'high' : undefined,\r\n style: `object-fit: ${fit}`,\r\n };\r\n}\r\n\r\n/**\r\n * Create blur placeholder style string\r\n */\r\nexport function createBlurPlaceholderStyle(blurDataUrl: string): string {\r\n return `\r\n background-image: url(${blurDataUrl});\r\n background-size: cover;\r\n background-position: center;\r\n filter: blur(20px);\r\n transform: scale(1.1);\r\n `.trim();\r\n}\r\n\r\n/**\r\n * Svelte action for lazy loading images with blur placeholder\r\n */\r\nexport function lazyImage(node: HTMLImageElement, blurDataUrl?: string) {\r\n if (blurDataUrl) {\r\n // Set blur background initially\r\n const wrapper = node.parentElement;\r\n if (wrapper) {\r\n wrapper.style.position = 'relative';\r\n wrapper.style.overflow = 'hidden';\r\n\r\n const placeholder = document.createElement('div');\r\n placeholder.style.cssText = `\r\n position: absolute;\r\n inset: 0;\r\n ${createBlurPlaceholderStyle(blurDataUrl)}\r\n transition: opacity 0.3s ease-in-out;\r\n `;\r\n wrapper.insertBefore(placeholder, node);\r\n\r\n node.style.opacity = '0';\r\n node.style.transition = 'opacity 0.3s ease-in-out';\r\n\r\n node.addEventListener('load', () => {\r\n node.style.opacity = '1';\r\n placeholder.style.opacity = '0';\r\n setTimeout(() => placeholder.remove(), 300);\r\n }, { once: true });\r\n }\r\n }\r\n\r\n return {\r\n destroy() {\r\n // Cleanup if needed\r\n },\r\n };\r\n}\r\n\r\nexport default { createImageProps, createBlurPlaceholderStyle, lazyImage };\r\n"]}
@@ -0,0 +1,133 @@
1
+ import * as vue from 'vue';
2
+ import { PropType } from 'vue';
3
+ import { ImageFormat, ImageFit } from '../index.js';
4
+ import 'node:buffer';
5
+
6
+ interface FlightImageProps {
7
+ src: string;
8
+ alt: string;
9
+ width?: number;
10
+ height?: number;
11
+ format?: ImageFormat;
12
+ quality?: number;
13
+ fit?: ImageFit;
14
+ priority?: boolean;
15
+ placeholder?: 'blur' | 'empty' | 'none';
16
+ blurDataUrl?: string;
17
+ sizes?: string;
18
+ baseUrl?: string;
19
+ }
20
+ declare const FlightImage: vue.DefineComponent<vue.ExtractPropTypes<{
21
+ src: {
22
+ type: StringConstructor;
23
+ required: true;
24
+ };
25
+ alt: {
26
+ type: StringConstructor;
27
+ required: true;
28
+ };
29
+ width: {
30
+ type: NumberConstructor;
31
+ default: undefined;
32
+ };
33
+ height: {
34
+ type: NumberConstructor;
35
+ default: undefined;
36
+ };
37
+ format: {
38
+ type: PropType<ImageFormat>;
39
+ default: string;
40
+ };
41
+ quality: {
42
+ type: NumberConstructor;
43
+ default: number;
44
+ };
45
+ fit: {
46
+ type: PropType<ImageFit>;
47
+ default: string;
48
+ };
49
+ priority: {
50
+ type: BooleanConstructor;
51
+ default: boolean;
52
+ };
53
+ placeholder: {
54
+ type: PropType<"blur" | "empty" | "none">;
55
+ default: string;
56
+ };
57
+ blurDataUrl: {
58
+ type: StringConstructor;
59
+ default: undefined;
60
+ };
61
+ sizes: {
62
+ type: StringConstructor;
63
+ default: undefined;
64
+ };
65
+ baseUrl: {
66
+ type: StringConstructor;
67
+ default: string;
68
+ };
69
+ }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
70
+ [key: string]: any;
71
+ }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
72
+ src: {
73
+ type: StringConstructor;
74
+ required: true;
75
+ };
76
+ alt: {
77
+ type: StringConstructor;
78
+ required: true;
79
+ };
80
+ width: {
81
+ type: NumberConstructor;
82
+ default: undefined;
83
+ };
84
+ height: {
85
+ type: NumberConstructor;
86
+ default: undefined;
87
+ };
88
+ format: {
89
+ type: PropType<ImageFormat>;
90
+ default: string;
91
+ };
92
+ quality: {
93
+ type: NumberConstructor;
94
+ default: number;
95
+ };
96
+ fit: {
97
+ type: PropType<ImageFit>;
98
+ default: string;
99
+ };
100
+ priority: {
101
+ type: BooleanConstructor;
102
+ default: boolean;
103
+ };
104
+ placeholder: {
105
+ type: PropType<"blur" | "empty" | "none">;
106
+ default: string;
107
+ };
108
+ blurDataUrl: {
109
+ type: StringConstructor;
110
+ default: undefined;
111
+ };
112
+ sizes: {
113
+ type: StringConstructor;
114
+ default: undefined;
115
+ };
116
+ baseUrl: {
117
+ type: StringConstructor;
118
+ default: string;
119
+ };
120
+ }>> & Readonly<{}>, {
121
+ fit: ImageFit;
122
+ width: number;
123
+ height: number;
124
+ format: ImageFormat;
125
+ quality: number;
126
+ priority: boolean;
127
+ placeholder: "blur" | "empty" | "none";
128
+ blurDataUrl: string;
129
+ sizes: string;
130
+ baseUrl: string;
131
+ }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
132
+
133
+ export { FlightImage, type FlightImageProps, FlightImage as default };