analytica-frontend-lib 1.1.7 → 1.1.9
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/dist/Whiteboard/index.d.mts +34 -0
- package/dist/Whiteboard/index.d.ts +34 -0
- package/dist/Whiteboard/index.js +151 -0
- package/dist/Whiteboard/index.js.map +1 -0
- package/dist/Whiteboard/index.mjs +130 -0
- package/dist/Whiteboard/index.mjs.map +1 -0
- package/dist/index.css +47 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +344 -222
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +313 -193
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +47 -0
- package/dist/styles.css.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Whiteboard image item interface
|
|
6
|
+
*/
|
|
7
|
+
interface WhiteboardImage {
|
|
8
|
+
id: string;
|
|
9
|
+
imageUrl: string;
|
|
10
|
+
title?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Whiteboard component props interface
|
|
14
|
+
*/
|
|
15
|
+
interface WhiteboardProps extends HTMLAttributes<HTMLDivElement> {
|
|
16
|
+
/** Array of images to display in the whiteboard */
|
|
17
|
+
images: WhiteboardImage[];
|
|
18
|
+
/** Whether to show download button on images */
|
|
19
|
+
showDownload?: boolean;
|
|
20
|
+
/** Custom className for the container */
|
|
21
|
+
className?: string;
|
|
22
|
+
/** Callback when download button is clicked */
|
|
23
|
+
onDownload?: (image: WhiteboardImage) => void;
|
|
24
|
+
/** Maximum number of images to display per row on desktop */
|
|
25
|
+
imagesPerRow?: 2 | 3 | 4;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Whiteboard component for displaying classroom board images
|
|
29
|
+
* @param props Component properties
|
|
30
|
+
* @returns Whiteboard component
|
|
31
|
+
*/
|
|
32
|
+
declare const Whiteboard: ({ images, showDownload, className, onDownload, imagesPerRow, ...rest }: WhiteboardProps) => react_jsx_runtime.JSX.Element;
|
|
33
|
+
|
|
34
|
+
export { type WhiteboardImage, type WhiteboardProps, Whiteboard as default };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Whiteboard image item interface
|
|
6
|
+
*/
|
|
7
|
+
interface WhiteboardImage {
|
|
8
|
+
id: string;
|
|
9
|
+
imageUrl: string;
|
|
10
|
+
title?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Whiteboard component props interface
|
|
14
|
+
*/
|
|
15
|
+
interface WhiteboardProps extends HTMLAttributes<HTMLDivElement> {
|
|
16
|
+
/** Array of images to display in the whiteboard */
|
|
17
|
+
images: WhiteboardImage[];
|
|
18
|
+
/** Whether to show download button on images */
|
|
19
|
+
showDownload?: boolean;
|
|
20
|
+
/** Custom className for the container */
|
|
21
|
+
className?: string;
|
|
22
|
+
/** Callback when download button is clicked */
|
|
23
|
+
onDownload?: (image: WhiteboardImage) => void;
|
|
24
|
+
/** Maximum number of images to display per row on desktop */
|
|
25
|
+
imagesPerRow?: 2 | 3 | 4;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Whiteboard component for displaying classroom board images
|
|
29
|
+
* @param props Component properties
|
|
30
|
+
* @returns Whiteboard component
|
|
31
|
+
*/
|
|
32
|
+
declare const Whiteboard: ({ images, showDownload, className, onDownload, imagesPerRow, ...rest }: WhiteboardProps) => react_jsx_runtime.JSX.Element;
|
|
33
|
+
|
|
34
|
+
export { type WhiteboardImage, type WhiteboardProps, Whiteboard as default };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/components/Whiteboard/Whiteboard.tsx
|
|
21
|
+
var Whiteboard_exports = {};
|
|
22
|
+
__export(Whiteboard_exports, {
|
|
23
|
+
default: () => Whiteboard_default
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(Whiteboard_exports);
|
|
26
|
+
var import_react = require("react");
|
|
27
|
+
var import_phosphor_react = require("phosphor-react");
|
|
28
|
+
|
|
29
|
+
// src/utils/utils.ts
|
|
30
|
+
var import_clsx = require("clsx");
|
|
31
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
32
|
+
function cn(...inputs) {
|
|
33
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/components/Whiteboard/Whiteboard.tsx
|
|
37
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
38
|
+
var IMAGE_WIDTH = 225;
|
|
39
|
+
var IMAGE_HEIGHT = 90;
|
|
40
|
+
var Whiteboard = ({
|
|
41
|
+
images,
|
|
42
|
+
showDownload = true,
|
|
43
|
+
className,
|
|
44
|
+
onDownload,
|
|
45
|
+
imagesPerRow = 2,
|
|
46
|
+
...rest
|
|
47
|
+
}) => {
|
|
48
|
+
const [imageErrors, setImageErrors] = (0, import_react.useState)(/* @__PURE__ */ new Set());
|
|
49
|
+
const handleDownload = (0, import_react.useCallback)(
|
|
50
|
+
(image) => {
|
|
51
|
+
if (onDownload) {
|
|
52
|
+
onDownload(image);
|
|
53
|
+
} else {
|
|
54
|
+
const link = document.createElement("a");
|
|
55
|
+
link.href = image.imageUrl;
|
|
56
|
+
link.download = image.title || `whiteboard-${image.id}`;
|
|
57
|
+
link.target = "_blank";
|
|
58
|
+
link.rel = "noopener noreferrer";
|
|
59
|
+
document.body.appendChild(link);
|
|
60
|
+
link.click();
|
|
61
|
+
document.body.removeChild(link);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[onDownload]
|
|
65
|
+
);
|
|
66
|
+
const handleImageError = (0, import_react.useCallback)((imageId) => {
|
|
67
|
+
setImageErrors((prev) => new Set(prev).add(imageId));
|
|
68
|
+
}, []);
|
|
69
|
+
const gridColsClass = images?.length === 1 ? "grid-cols-1" : {
|
|
70
|
+
2: "grid-cols-1 sm:grid-cols-2",
|
|
71
|
+
3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
72
|
+
4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
|
73
|
+
}[imagesPerRow];
|
|
74
|
+
if (!images || images.length === 0) {
|
|
75
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
76
|
+
"div",
|
|
77
|
+
{
|
|
78
|
+
className: cn(
|
|
79
|
+
"flex items-center justify-center p-8 bg-white border border-gray-100 rounded-xl",
|
|
80
|
+
className
|
|
81
|
+
),
|
|
82
|
+
...rest,
|
|
83
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-gray-400 text-sm", children: "Nenhuma imagem dispon\xEDvel" })
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
88
|
+
"div",
|
|
89
|
+
{
|
|
90
|
+
className: cn(
|
|
91
|
+
"flex flex-col bg-white border border-gray-100 p-4 gap-2 rounded-xl w-fit mx-auto",
|
|
92
|
+
className
|
|
93
|
+
),
|
|
94
|
+
...rest,
|
|
95
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("grid gap-4", gridColsClass), children: images.map((image) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
96
|
+
"div",
|
|
97
|
+
{
|
|
98
|
+
className: "relative group overflow-hidden bg-gray-100 rounded-lg",
|
|
99
|
+
style: {
|
|
100
|
+
width: `${IMAGE_WIDTH}px`
|
|
101
|
+
},
|
|
102
|
+
children: [
|
|
103
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
104
|
+
"div",
|
|
105
|
+
{
|
|
106
|
+
className: "relative",
|
|
107
|
+
style: {
|
|
108
|
+
width: `${IMAGE_WIDTH}px`,
|
|
109
|
+
height: `${IMAGE_HEIGHT}px`
|
|
110
|
+
},
|
|
111
|
+
children: imageErrors.has(image.id) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 flex items-center justify-center bg-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-gray-500 text-sm text-center px-2", children: "Imagem indispon\xEDvel" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
112
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
113
|
+
"img",
|
|
114
|
+
{
|
|
115
|
+
src: image.imageUrl,
|
|
116
|
+
alt: image.title || `Whiteboard ${image.id}`,
|
|
117
|
+
className: "absolute inset-0 w-full h-full object-cover",
|
|
118
|
+
loading: "lazy",
|
|
119
|
+
onError: () => handleImageError(image.id)
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 bg-gradient-to-t from-black/20 to-transparent" })
|
|
123
|
+
] })
|
|
124
|
+
}
|
|
125
|
+
),
|
|
126
|
+
showDownload && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
127
|
+
"button",
|
|
128
|
+
{
|
|
129
|
+
type: "button",
|
|
130
|
+
onClick: () => handleDownload(image),
|
|
131
|
+
className: "absolute bottom-3 right-3 flex items-center justify-center bg-black/20 backdrop-blur-sm rounded hover:bg-black/30 transition-colors duration-200 group/button w-6 h-6",
|
|
132
|
+
"aria-label": `Download ${image.title || "imagem"}`,
|
|
133
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
134
|
+
import_phosphor_react.DownloadSimple,
|
|
135
|
+
{
|
|
136
|
+
size: 24,
|
|
137
|
+
weight: "regular",
|
|
138
|
+
className: "text-white group-hover/button:scale-110 transition-transform duration-200"
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
image.id
|
|
146
|
+
)) })
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
var Whiteboard_default = Whiteboard;
|
|
151
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/Whiteboard/Whiteboard.tsx","../../src/utils/utils.ts"],"sourcesContent":["import { HTMLAttributes, useCallback, useState } from 'react';\nimport { DownloadSimple } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\n\n// Design constants for critical layout dimensions\nconst IMAGE_WIDTH = 225;\nconst IMAGE_HEIGHT = 90;\n\n/**\n * Whiteboard image item interface\n */\nexport interface WhiteboardImage {\n id: string;\n imageUrl: string;\n title?: string;\n}\n\n/**\n * Whiteboard component props interface\n */\nexport interface WhiteboardProps extends HTMLAttributes<HTMLDivElement> {\n /** Array of images to display in the whiteboard */\n images: WhiteboardImage[];\n /** Whether to show download button on images */\n showDownload?: boolean;\n /** Custom className for the container */\n className?: string;\n /** Callback when download button is clicked */\n onDownload?: (image: WhiteboardImage) => void;\n /** Maximum number of images to display per row on desktop */\n imagesPerRow?: 2 | 3 | 4;\n}\n\n/**\n * Whiteboard component for displaying classroom board images\n * @param props Component properties\n * @returns Whiteboard component\n */\nconst Whiteboard = ({\n images,\n showDownload = true,\n className,\n onDownload,\n imagesPerRow = 2,\n ...rest\n}: WhiteboardProps) => {\n // State to track images that failed to load\n const [imageErrors, setImageErrors] = useState<Set<string>>(new Set());\n\n /**\n * Handle image download\n */\n const handleDownload = useCallback(\n (image: WhiteboardImage) => {\n if (onDownload) {\n onDownload(image);\n } else {\n const link = document.createElement('a');\n link.href = image.imageUrl;\n link.download = image.title || `whiteboard-${image.id}`;\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n },\n [onDownload]\n );\n\n /**\n * Handle image loading error\n */\n const handleImageError = useCallback((imageId: string) => {\n setImageErrors((prev) => new Set(prev).add(imageId));\n }, []);\n\n const gridColsClass =\n images?.length === 1\n ? 'grid-cols-1'\n : {\n 2: 'grid-cols-1 sm:grid-cols-2',\n 3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',\n 4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',\n }[imagesPerRow];\n\n // Let CSS handle sizing responsively\n\n if (!images || images.length === 0) {\n return (\n <div\n className={cn(\n 'flex items-center justify-center p-8 bg-white border border-gray-100 rounded-xl',\n className\n )}\n {...rest}\n >\n <p className=\"text-gray-400 text-sm\">Nenhuma imagem disponível</p>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'flex flex-col bg-white border border-gray-100 p-4 gap-2 rounded-xl w-fit mx-auto',\n className\n )}\n {...rest}\n >\n <div className={cn('grid gap-4', gridColsClass)}>\n {images.map((image) => (\n <div\n key={image.id}\n className=\"relative group overflow-hidden bg-gray-100 rounded-lg\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n }}\n >\n <div\n className=\"relative\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n height: `${IMAGE_HEIGHT}px`,\n }}\n >\n {imageErrors.has(image.id) ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-gray-200\">\n <p className=\"text-gray-500 text-sm text-center px-2\">\n Imagem indisponível\n </p>\n </div>\n ) : (\n <>\n <img\n src={image.imageUrl}\n alt={image.title || `Whiteboard ${image.id}`}\n className=\"absolute inset-0 w-full h-full object-cover\"\n loading=\"lazy\"\n onError={() => handleImageError(image.id)}\n />\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/20 to-transparent\" />\n </>\n )}\n </div>\n {showDownload && (\n <button\n type=\"button\"\n onClick={() => handleDownload(image)}\n className=\"absolute bottom-3 right-3 flex items-center justify-center bg-black/20 backdrop-blur-sm rounded hover:bg-black/30 transition-colors duration-200 group/button w-6 h-6\"\n aria-label={`Download ${image.title || 'imagem'}`}\n >\n <DownloadSimple\n size={24}\n weight=\"regular\"\n className=\"text-white group-hover/button:scale-110 transition-transform duration-200\"\n />\n </button>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Whiteboard;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// src/components/Whiteboard/Whiteboard.tsx
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
import { DownloadSimple } from "phosphor-react";
|
|
4
|
+
|
|
5
|
+
// src/utils/utils.ts
|
|
6
|
+
import { clsx } from "clsx";
|
|
7
|
+
import { twMerge } from "tailwind-merge";
|
|
8
|
+
function cn(...inputs) {
|
|
9
|
+
return twMerge(clsx(inputs));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/components/Whiteboard/Whiteboard.tsx
|
|
13
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
var IMAGE_WIDTH = 225;
|
|
15
|
+
var IMAGE_HEIGHT = 90;
|
|
16
|
+
var Whiteboard = ({
|
|
17
|
+
images,
|
|
18
|
+
showDownload = true,
|
|
19
|
+
className,
|
|
20
|
+
onDownload,
|
|
21
|
+
imagesPerRow = 2,
|
|
22
|
+
...rest
|
|
23
|
+
}) => {
|
|
24
|
+
const [imageErrors, setImageErrors] = useState(/* @__PURE__ */ new Set());
|
|
25
|
+
const handleDownload = useCallback(
|
|
26
|
+
(image) => {
|
|
27
|
+
if (onDownload) {
|
|
28
|
+
onDownload(image);
|
|
29
|
+
} else {
|
|
30
|
+
const link = document.createElement("a");
|
|
31
|
+
link.href = image.imageUrl;
|
|
32
|
+
link.download = image.title || `whiteboard-${image.id}`;
|
|
33
|
+
link.target = "_blank";
|
|
34
|
+
link.rel = "noopener noreferrer";
|
|
35
|
+
document.body.appendChild(link);
|
|
36
|
+
link.click();
|
|
37
|
+
document.body.removeChild(link);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
[onDownload]
|
|
41
|
+
);
|
|
42
|
+
const handleImageError = useCallback((imageId) => {
|
|
43
|
+
setImageErrors((prev) => new Set(prev).add(imageId));
|
|
44
|
+
}, []);
|
|
45
|
+
const gridColsClass = images?.length === 1 ? "grid-cols-1" : {
|
|
46
|
+
2: "grid-cols-1 sm:grid-cols-2",
|
|
47
|
+
3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
48
|
+
4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
|
49
|
+
}[imagesPerRow];
|
|
50
|
+
if (!images || images.length === 0) {
|
|
51
|
+
return /* @__PURE__ */ jsx(
|
|
52
|
+
"div",
|
|
53
|
+
{
|
|
54
|
+
className: cn(
|
|
55
|
+
"flex items-center justify-center p-8 bg-white border border-gray-100 rounded-xl",
|
|
56
|
+
className
|
|
57
|
+
),
|
|
58
|
+
...rest,
|
|
59
|
+
children: /* @__PURE__ */ jsx("p", { className: "text-gray-400 text-sm", children: "Nenhuma imagem dispon\xEDvel" })
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return /* @__PURE__ */ jsx(
|
|
64
|
+
"div",
|
|
65
|
+
{
|
|
66
|
+
className: cn(
|
|
67
|
+
"flex flex-col bg-white border border-gray-100 p-4 gap-2 rounded-xl w-fit mx-auto",
|
|
68
|
+
className
|
|
69
|
+
),
|
|
70
|
+
...rest,
|
|
71
|
+
children: /* @__PURE__ */ jsx("div", { className: cn("grid gap-4", gridColsClass), children: images.map((image) => /* @__PURE__ */ jsxs(
|
|
72
|
+
"div",
|
|
73
|
+
{
|
|
74
|
+
className: "relative group overflow-hidden bg-gray-100 rounded-lg",
|
|
75
|
+
style: {
|
|
76
|
+
width: `${IMAGE_WIDTH}px`
|
|
77
|
+
},
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ jsx(
|
|
80
|
+
"div",
|
|
81
|
+
{
|
|
82
|
+
className: "relative",
|
|
83
|
+
style: {
|
|
84
|
+
width: `${IMAGE_WIDTH}px`,
|
|
85
|
+
height: `${IMAGE_HEIGHT}px`
|
|
86
|
+
},
|
|
87
|
+
children: imageErrors.has(image.id) ? /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-gray-200", children: /* @__PURE__ */ jsx("p", { className: "text-gray-500 text-sm text-center px-2", children: "Imagem indispon\xEDvel" }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
88
|
+
/* @__PURE__ */ jsx(
|
|
89
|
+
"img",
|
|
90
|
+
{
|
|
91
|
+
src: image.imageUrl,
|
|
92
|
+
alt: image.title || `Whiteboard ${image.id}`,
|
|
93
|
+
className: "absolute inset-0 w-full h-full object-cover",
|
|
94
|
+
loading: "lazy",
|
|
95
|
+
onError: () => handleImageError(image.id)
|
|
96
|
+
}
|
|
97
|
+
),
|
|
98
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/20 to-transparent" })
|
|
99
|
+
] })
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
showDownload && /* @__PURE__ */ jsx(
|
|
103
|
+
"button",
|
|
104
|
+
{
|
|
105
|
+
type: "button",
|
|
106
|
+
onClick: () => handleDownload(image),
|
|
107
|
+
className: "absolute bottom-3 right-3 flex items-center justify-center bg-black/20 backdrop-blur-sm rounded hover:bg-black/30 transition-colors duration-200 group/button w-6 h-6",
|
|
108
|
+
"aria-label": `Download ${image.title || "imagem"}`,
|
|
109
|
+
children: /* @__PURE__ */ jsx(
|
|
110
|
+
DownloadSimple,
|
|
111
|
+
{
|
|
112
|
+
size: 24,
|
|
113
|
+
weight: "regular",
|
|
114
|
+
className: "text-white group-hover/button:scale-110 transition-transform duration-200"
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
image.id
|
|
122
|
+
)) })
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
var Whiteboard_default = Whiteboard;
|
|
127
|
+
export {
|
|
128
|
+
Whiteboard_default as default
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/Whiteboard/Whiteboard.tsx","../../src/utils/utils.ts"],"sourcesContent":["import { HTMLAttributes, useCallback, useState } from 'react';\nimport { DownloadSimple } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\n\n// Design constants for critical layout dimensions\nconst IMAGE_WIDTH = 225;\nconst IMAGE_HEIGHT = 90;\n\n/**\n * Whiteboard image item interface\n */\nexport interface WhiteboardImage {\n id: string;\n imageUrl: string;\n title?: string;\n}\n\n/**\n * Whiteboard component props interface\n */\nexport interface WhiteboardProps extends HTMLAttributes<HTMLDivElement> {\n /** Array of images to display in the whiteboard */\n images: WhiteboardImage[];\n /** Whether to show download button on images */\n showDownload?: boolean;\n /** Custom className for the container */\n className?: string;\n /** Callback when download button is clicked */\n onDownload?: (image: WhiteboardImage) => void;\n /** Maximum number of images to display per row on desktop */\n imagesPerRow?: 2 | 3 | 4;\n}\n\n/**\n * Whiteboard component for displaying classroom board images\n * @param props Component properties\n * @returns Whiteboard component\n */\nconst Whiteboard = ({\n images,\n showDownload = true,\n className,\n onDownload,\n imagesPerRow = 2,\n ...rest\n}: WhiteboardProps) => {\n // State to track images that failed to load\n const [imageErrors, setImageErrors] = useState<Set<string>>(new Set());\n\n /**\n * Handle image download\n */\n const handleDownload = useCallback(\n (image: WhiteboardImage) => {\n if (onDownload) {\n onDownload(image);\n } else {\n const link = document.createElement('a');\n link.href = image.imageUrl;\n link.download = image.title || `whiteboard-${image.id}`;\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n },\n [onDownload]\n );\n\n /**\n * Handle image loading error\n */\n const handleImageError = useCallback((imageId: string) => {\n setImageErrors((prev) => new Set(prev).add(imageId));\n }, []);\n\n const gridColsClass =\n images?.length === 1\n ? 'grid-cols-1'\n : {\n 2: 'grid-cols-1 sm:grid-cols-2',\n 3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',\n 4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',\n }[imagesPerRow];\n\n // Let CSS handle sizing responsively\n\n if (!images || images.length === 0) {\n return (\n <div\n className={cn(\n 'flex items-center justify-center p-8 bg-white border border-gray-100 rounded-xl',\n className\n )}\n {...rest}\n >\n <p className=\"text-gray-400 text-sm\">Nenhuma imagem disponível</p>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'flex flex-col bg-white border border-gray-100 p-4 gap-2 rounded-xl w-fit mx-auto',\n className\n )}\n {...rest}\n >\n <div className={cn('grid gap-4', gridColsClass)}>\n {images.map((image) => (\n <div\n key={image.id}\n className=\"relative group overflow-hidden bg-gray-100 rounded-lg\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n }}\n >\n <div\n className=\"relative\"\n style={{\n width: `${IMAGE_WIDTH}px`,\n height: `${IMAGE_HEIGHT}px`,\n }}\n >\n {imageErrors.has(image.id) ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-gray-200\">\n <p className=\"text-gray-500 text-sm text-center px-2\">\n Imagem indisponível\n </p>\n </div>\n ) : (\n <>\n <img\n src={image.imageUrl}\n alt={image.title || `Whiteboard ${image.id}`}\n className=\"absolute inset-0 w-full h-full object-cover\"\n loading=\"lazy\"\n onError={() => handleImageError(image.id)}\n />\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/20 to-transparent\" />\n </>\n )}\n </div>\n {showDownload && (\n <button\n type=\"button\"\n onClick={() => handleDownload(image)}\n className=\"absolute bottom-3 right-3 flex items-center justify-center bg-black/20 backdrop-blur-sm rounded hover:bg-black/30 transition-colors duration-200 group/button w-6 h-6\"\n aria-label={`Download ${image.title || 'imagem'}`}\n >\n <DownloadSimple\n size={24}\n weight=\"regular\"\n className=\"text-white group-hover/button:scale-110 transition-transform duration-200\"\n />\n </button>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Whiteboard;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.css
CHANGED
|
@@ -547,6 +547,9 @@
|
|
|
547
547
|
.bottom-0 {
|
|
548
548
|
bottom: calc(var(--spacing) * 0);
|
|
549
549
|
}
|
|
550
|
+
.bottom-3 {
|
|
551
|
+
bottom: calc(var(--spacing) * 3);
|
|
552
|
+
}
|
|
550
553
|
.bottom-4 {
|
|
551
554
|
bottom: calc(var(--spacing) * 4);
|
|
552
555
|
}
|
|
@@ -1681,6 +1684,9 @@
|
|
|
1681
1684
|
.border-exam-4 {
|
|
1682
1685
|
border-color: var(--color-exam-4);
|
|
1683
1686
|
}
|
|
1687
|
+
.border-gray-100 {
|
|
1688
|
+
border-color: var(--color-gray-100);
|
|
1689
|
+
}
|
|
1684
1690
|
.border-gray-200 {
|
|
1685
1691
|
border-color: var(--color-gray-200);
|
|
1686
1692
|
}
|
|
@@ -2047,6 +2053,12 @@
|
|
|
2047
2053
|
.bg-background-muted {
|
|
2048
2054
|
background-color: var(--color-background-muted);
|
|
2049
2055
|
}
|
|
2056
|
+
.bg-black\/20 {
|
|
2057
|
+
background-color: color-mix(in srgb, #000 20%, transparent);
|
|
2058
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
2059
|
+
background-color: color-mix(in oklab, var(--color-black) 20%, transparent);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2050
2062
|
.bg-black\/30 {
|
|
2051
2063
|
background-color: color-mix(in srgb, #000 30%, transparent);
|
|
2052
2064
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -2173,6 +2185,9 @@
|
|
|
2173
2185
|
.bg-gray-100 {
|
|
2174
2186
|
background-color: var(--color-gray-100);
|
|
2175
2187
|
}
|
|
2188
|
+
.bg-gray-200 {
|
|
2189
|
+
background-color: var(--color-gray-200);
|
|
2190
|
+
}
|
|
2176
2191
|
.bg-gray-400 {
|
|
2177
2192
|
background-color: var(--color-gray-400);
|
|
2178
2193
|
}
|
|
@@ -2563,6 +2578,13 @@
|
|
|
2563
2578
|
--tw-gradient-position: to top in oklab;
|
|
2564
2579
|
background-image: linear-gradient(var(--tw-gradient-stops));
|
|
2565
2580
|
}
|
|
2581
|
+
.from-black\/20 {
|
|
2582
|
+
--tw-gradient-from: color-mix(in srgb, #000 20%, transparent);
|
|
2583
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
2584
|
+
--tw-gradient-from: color-mix(in oklab, var(--color-black) 20%, transparent);
|
|
2585
|
+
}
|
|
2586
|
+
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
|
2587
|
+
}
|
|
2566
2588
|
.from-black\/70 {
|
|
2567
2589
|
--tw-gradient-from: color-mix(in srgb, #000 70%, transparent);
|
|
2568
2590
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -3730,6 +3752,16 @@
|
|
|
3730
3752
|
}
|
|
3731
3753
|
}
|
|
3732
3754
|
}
|
|
3755
|
+
.group-hover\/button\:scale-110 {
|
|
3756
|
+
&:is(:where(.group\/button):hover *) {
|
|
3757
|
+
@media (hover: hover) {
|
|
3758
|
+
--tw-scale-x: 110%;
|
|
3759
|
+
--tw-scale-y: 110%;
|
|
3760
|
+
--tw-scale-z: 110%;
|
|
3761
|
+
scale: var(--tw-scale-x) var(--tw-scale-y);
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3733
3765
|
.placeholder\:text-text-600 {
|
|
3734
3766
|
&::-moz-placeholder {
|
|
3735
3767
|
color: var(--color-text-600);
|
|
@@ -4616,6 +4648,16 @@
|
|
|
4616
4648
|
}
|
|
4617
4649
|
}
|
|
4618
4650
|
}
|
|
4651
|
+
.hover\:bg-black\/30 {
|
|
4652
|
+
&:hover {
|
|
4653
|
+
@media (hover: hover) {
|
|
4654
|
+
background-color: color-mix(in srgb, #000 30%, transparent);
|
|
4655
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
4656
|
+
background-color: color-mix(in oklab, var(--color-black) 30%, transparent);
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
4619
4661
|
.hover\:bg-blue-600 {
|
|
4620
4662
|
&:hover {
|
|
4621
4663
|
@media (hover: hover) {
|
|
@@ -8176,6 +8218,11 @@
|
|
|
8176
8218
|
max-width: 100px;
|
|
8177
8219
|
}
|
|
8178
8220
|
}
|
|
8221
|
+
.sm\:grid-cols-2 {
|
|
8222
|
+
@media (width >= 40rem) {
|
|
8223
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
8224
|
+
}
|
|
8225
|
+
}
|
|
8179
8226
|
.sm\:flex-row {
|
|
8180
8227
|
@media (width >= 40rem) {
|
|
8181
8228
|
flex-direction: row;
|