@seedgrid/fe-components 2026.11.0 → 2026.11.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.
- package/dist/ai/seedgrid-components.manifest.json +1201 -4
- package/dist/generated/compiledStyles.d.ts +1 -1
- package/dist/generated/compiledStyles.d.ts.map +1 -1
- package/dist/generated/compiledStyles.js +1 -1
- package/dist/i18n/en-US.d.ts.map +1 -1
- package/dist/i18n/en-US.js +31 -0
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +31 -0
- package/dist/i18n/fr.d.ts.map +1 -1
- package/dist/i18n/fr.js +31 -0
- package/dist/i18n/pt-BR.d.ts.map +1 -1
- package/dist/i18n/pt-BR.js +31 -0
- package/dist/i18n/pt-PT.d.ts.map +1 -1
- package/dist/i18n/pt-PT.js +31 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/inputs/SgCameraCapture.d.ts +25 -0
- package/dist/inputs/SgCameraCapture.d.ts.map +1 -0
- package/dist/inputs/SgCameraCapture.js +114 -0
- package/dist/inputs/SgCameraCapture.meta.d.ts +4 -0
- package/dist/inputs/SgCameraCapture.meta.d.ts.map +1 -0
- package/dist/inputs/SgCameraCapture.meta.js +63 -0
- package/dist/inputs/SgDatatable.d.ts +9 -1
- package/dist/inputs/SgDatatable.d.ts.map +1 -1
- package/dist/inputs/SgDatatable.js +62 -6
- package/dist/inputs/SgDatatable.meta.d.ts.map +1 -1
- package/dist/inputs/SgDatatable.meta.js +5 -3
- package/dist/inputs/SgFileUpload.d.ts +69 -0
- package/dist/inputs/SgFileUpload.d.ts.map +1 -0
- package/dist/inputs/SgFileUpload.js +234 -0
- package/dist/inputs/SgFileUpload.meta.d.ts +4 -0
- package/dist/inputs/SgFileUpload.meta.d.ts.map +1 -0
- package/dist/inputs/SgFileUpload.meta.js +71 -0
- package/dist/inputs/SgImageUpload.d.ts +20 -0
- package/dist/inputs/SgImageUpload.d.ts.map +1 -0
- package/dist/inputs/SgImageUpload.js +74 -0
- package/dist/inputs/SgImageUpload.meta.d.ts +4 -0
- package/dist/inputs/SgImageUpload.meta.d.ts.map +1 -0
- package/dist/inputs/SgImageUpload.meta.js +73 -0
- package/dist/inputs/SgInputDateTime.d.ts +29 -0
- package/dist/inputs/SgInputDateTime.d.ts.map +1 -0
- package/dist/inputs/SgInputDateTime.js +130 -0
- package/dist/inputs/SgInputDateTime.meta.d.ts +4 -0
- package/dist/inputs/SgInputDateTime.meta.d.ts.map +1 -0
- package/dist/inputs/SgInputDateTime.meta.js +73 -0
- package/dist/inputs/cameraLogic.d.ts +27 -0
- package/dist/inputs/cameraLogic.d.ts.map +1 -0
- package/dist/inputs/cameraLogic.js +57 -0
- package/dist/inputs/dateTimeLogic.d.ts +22 -0
- package/dist/inputs/dateTimeLogic.d.ts.map +1 -0
- package/dist/inputs/dateTimeLogic.js +140 -0
- package/dist/inputs/fileUploadLogic.d.ts +62 -0
- package/dist/inputs/fileUploadLogic.d.ts.map +1 -0
- package/dist/inputs/fileUploadLogic.js +254 -0
- package/dist/inputs/imageUploadLogic.d.ts +31 -0
- package/dist/inputs/imageUploadLogic.d.ts.map +1 -0
- package/dist/inputs/imageUploadLogic.js +70 -0
- package/dist/menus/SgDockMenu.d.ts.map +1 -1
- package/dist/menus/SgDockMenu.js +2 -0
- package/dist/sandbox.cjs +64 -53
- package/dist/style.css +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// Logica pura do SgFileUpload (P141): tipos de midia (`mediaType`), tamanho com unidade ("1.5MB"), tipo do arquivo
|
|
2
|
+
// pelo CONTEUDO (assinatura) e a regra de quantos arquivos entram (maxFiles, por arquivo, total). Sem DOM: os vetores
|
|
3
|
+
// de paridade com o Flutter (`seedgrid_components/test/widgets/file_upload_vectors`) sao gerados DESTE modulo.
|
|
4
|
+
export const MEDIA_TYPES = {
|
|
5
|
+
image: { group: "image/", extensions: ["png", "jpg", "jpeg", "jfif", "jpe", "gif", "webp", "bmp", "svg", "heic", "heif", "avif", "tif", "tiff", "ico"] },
|
|
6
|
+
video: { group: "video/", extensions: ["mp4", "webm", "mov", "mkv", "avi", "ogv", "m4v"] },
|
|
7
|
+
audio: { group: "audio/", extensions: ["mp3", "wav", "ogg", "oga", "m4a", "aac", "flac", "weba"] },
|
|
8
|
+
pdf: { mime: "application/pdf", extensions: ["pdf"] },
|
|
9
|
+
doc: { mime: "application/msword", extensions: ["doc"] },
|
|
10
|
+
docx: { mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", extensions: ["docx"] },
|
|
11
|
+
xls: { mime: "application/vnd.ms-excel", extensions: ["xls"] },
|
|
12
|
+
xlsx: { mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", extensions: ["xlsx"] },
|
|
13
|
+
ppt: { mime: "application/vnd.ms-powerpoint", extensions: ["ppt"] },
|
|
14
|
+
pptx: { mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation", extensions: ["pptx"] },
|
|
15
|
+
csv: { mime: "text/csv", extensions: ["csv"] },
|
|
16
|
+
txt: { mime: "text/plain", extensions: ["txt"] },
|
|
17
|
+
zip: { mime: "application/zip", extensions: ["zip"] }
|
|
18
|
+
};
|
|
19
|
+
const EXTENSION_MIME = {
|
|
20
|
+
png: "image/png",
|
|
21
|
+
jpg: "image/jpeg",
|
|
22
|
+
jpeg: "image/jpeg",
|
|
23
|
+
jfif: "image/jpeg",
|
|
24
|
+
jpe: "image/jpeg",
|
|
25
|
+
gif: "image/gif",
|
|
26
|
+
webp: "image/webp",
|
|
27
|
+
bmp: "image/bmp",
|
|
28
|
+
svg: "image/svg+xml",
|
|
29
|
+
heic: "image/heic",
|
|
30
|
+
heif: "image/heif",
|
|
31
|
+
avif: "image/avif",
|
|
32
|
+
tif: "image/tiff",
|
|
33
|
+
tiff: "image/tiff",
|
|
34
|
+
ico: "image/x-icon",
|
|
35
|
+
mp4: "video/mp4",
|
|
36
|
+
m4v: "video/mp4",
|
|
37
|
+
webm: "video/webm",
|
|
38
|
+
mov: "video/quicktime",
|
|
39
|
+
mkv: "video/x-matroska",
|
|
40
|
+
avi: "video/x-msvideo",
|
|
41
|
+
ogv: "video/ogg",
|
|
42
|
+
mp3: "audio/mpeg",
|
|
43
|
+
wav: "audio/wav",
|
|
44
|
+
ogg: "audio/ogg",
|
|
45
|
+
oga: "audio/ogg",
|
|
46
|
+
m4a: "audio/mp4",
|
|
47
|
+
aac: "audio/aac",
|
|
48
|
+
flac: "audio/flac",
|
|
49
|
+
weba: "audio/webm",
|
|
50
|
+
pdf: "application/pdf",
|
|
51
|
+
doc: "application/msword",
|
|
52
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
53
|
+
xls: "application/vnd.ms-excel",
|
|
54
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
55
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
56
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
57
|
+
csv: "text/csv",
|
|
58
|
+
txt: "text/plain",
|
|
59
|
+
zip: "application/zip"
|
|
60
|
+
};
|
|
61
|
+
const ZIP = "application/zip";
|
|
62
|
+
const OLE = "application/x-ole-storage";
|
|
63
|
+
const ZIP_BASED = new Set(["docx", "xlsx", "pptx", "zip"]);
|
|
64
|
+
const OLE_BASED = new Set(["doc", "xls", "ppt"]);
|
|
65
|
+
/** Extensao em minusculas (sem ponto), ou "" quando nao ha. */
|
|
66
|
+
export function extensionOf(name) {
|
|
67
|
+
const i = name.lastIndexOf(".");
|
|
68
|
+
return i < 0 || i === name.length - 1 ? "" : name.slice(i + 1).toLowerCase();
|
|
69
|
+
}
|
|
70
|
+
/** Tipo MIME so pela extensao do nome; "" se desconhecida. */
|
|
71
|
+
export function mimeFromName(name) {
|
|
72
|
+
return EXTENSION_MIME[extensionOf(name)] ?? "";
|
|
73
|
+
}
|
|
74
|
+
function startsWith(b, sig, at = 0) {
|
|
75
|
+
if (b.length < at + sig.length)
|
|
76
|
+
return false;
|
|
77
|
+
for (let i = 0; i < sig.length; i += 1)
|
|
78
|
+
if (b[i + at] !== sig[i])
|
|
79
|
+
return false;
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
function ascii(b, from, to) {
|
|
83
|
+
let s = "";
|
|
84
|
+
for (let i = from; i < Math.min(to, b.length); i += 1)
|
|
85
|
+
s += String.fromCharCode(b[i] ?? 0);
|
|
86
|
+
return s;
|
|
87
|
+
}
|
|
88
|
+
/** Tipo MIME pela assinatura do arquivo (primeiros bytes; ate 256 bastam); "" se nao reconhecida. */
|
|
89
|
+
export function sniffMime(b) {
|
|
90
|
+
if (startsWith(b, [0x89, 0x50, 0x4e, 0x47]))
|
|
91
|
+
return "image/png";
|
|
92
|
+
if (startsWith(b, [0xff, 0xd8, 0xff]))
|
|
93
|
+
return "image/jpeg";
|
|
94
|
+
if (startsWith(b, [0x47, 0x49, 0x46, 0x38]))
|
|
95
|
+
return "image/gif";
|
|
96
|
+
if (startsWith(b, [0x52, 0x49, 0x46, 0x46]) && startsWith(b, [0x57, 0x45, 0x42, 0x50], 8))
|
|
97
|
+
return "image/webp";
|
|
98
|
+
if (startsWith(b, [0x52, 0x49, 0x46, 0x46]) && startsWith(b, [0x57, 0x41, 0x56, 0x45], 8))
|
|
99
|
+
return "audio/wav";
|
|
100
|
+
if (startsWith(b, [0x42, 0x4d]))
|
|
101
|
+
return "image/bmp";
|
|
102
|
+
if (startsWith(b, [0x49, 0x49, 0x2a, 0x00]) || startsWith(b, [0x4d, 0x4d, 0x00, 0x2a]))
|
|
103
|
+
return "image/tiff";
|
|
104
|
+
if (startsWith(b, [0x00, 0x00, 0x01, 0x00]))
|
|
105
|
+
return "image/x-icon";
|
|
106
|
+
if (startsWith(b, [0x25, 0x50, 0x44, 0x46]))
|
|
107
|
+
return "application/pdf";
|
|
108
|
+
if (startsWith(b, [0x50, 0x4b, 0x03, 0x04]))
|
|
109
|
+
return ZIP;
|
|
110
|
+
if (startsWith(b, [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1]))
|
|
111
|
+
return OLE;
|
|
112
|
+
if (startsWith(b, [0x1a, 0x45, 0xdf, 0xa3]))
|
|
113
|
+
return "video/webm";
|
|
114
|
+
if (startsWith(b, [0x4f, 0x67, 0x67, 0x53]))
|
|
115
|
+
return "audio/ogg";
|
|
116
|
+
if (startsWith(b, [0x66, 0x4c, 0x61, 0x43]))
|
|
117
|
+
return "audio/flac";
|
|
118
|
+
if (startsWith(b, [0x49, 0x44, 0x33]) || startsWith(b, [0xff, 0xfb]) || startsWith(b, [0xff, 0xf3]) || startsWith(b, [0xff, 0xf2]))
|
|
119
|
+
return "audio/mpeg";
|
|
120
|
+
if (startsWith(b, [0x66, 0x74, 0x79, 0x70], 4)) {
|
|
121
|
+
const brand = ascii(b, 8, 12);
|
|
122
|
+
if (brand.startsWith("avif") || brand.startsWith("avis"))
|
|
123
|
+
return "image/avif";
|
|
124
|
+
if (brand.startsWith("hei") || brand.startsWith("mif"))
|
|
125
|
+
return "image/heic";
|
|
126
|
+
if (brand.startsWith("M4A"))
|
|
127
|
+
return "audio/mp4";
|
|
128
|
+
if (brand.startsWith("qt"))
|
|
129
|
+
return "video/quicktime";
|
|
130
|
+
return "video/mp4";
|
|
131
|
+
}
|
|
132
|
+
if (ascii(b, 0, 256).toLowerCase().includes("<svg"))
|
|
133
|
+
return "image/svg+xml";
|
|
134
|
+
return "";
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Tipo efetivo do arquivo: a assinatura (conteudo) manda; contentores (zip, OLE) e texto puro desempatam pela
|
|
138
|
+
* extensao; sem nenhuma das duas, vale o tipo que o sistema informou (`declared`).
|
|
139
|
+
*/
|
|
140
|
+
export function resolveMime(name, declared, head) {
|
|
141
|
+
const ext = extensionOf(name);
|
|
142
|
+
const byName = EXTENSION_MIME[ext] ?? "";
|
|
143
|
+
const sig = sniffMime(head);
|
|
144
|
+
if (sig === ZIP)
|
|
145
|
+
return ZIP_BASED.has(ext) ? byName : ZIP;
|
|
146
|
+
if (sig === OLE)
|
|
147
|
+
return OLE_BASED.has(ext) ? byName : OLE;
|
|
148
|
+
if (sig)
|
|
149
|
+
return sig;
|
|
150
|
+
return byName || declared.toLowerCase();
|
|
151
|
+
}
|
|
152
|
+
/** Texto `accept` livre: `.png`, `image/png`, `image/*`, separados por virgula. */
|
|
153
|
+
function matchesAcceptToken(name, mime, accept) {
|
|
154
|
+
const tokens = accept
|
|
155
|
+
.split(",")
|
|
156
|
+
.map((t) => t.trim().toLowerCase())
|
|
157
|
+
.filter(Boolean);
|
|
158
|
+
if (tokens.length === 0)
|
|
159
|
+
return false;
|
|
160
|
+
const lowerName = name.toLowerCase();
|
|
161
|
+
return tokens.some((token) => {
|
|
162
|
+
if (token.startsWith("."))
|
|
163
|
+
return lowerName.endsWith(token);
|
|
164
|
+
if (token.endsWith("/*"))
|
|
165
|
+
return mime.startsWith(token.slice(0, -1));
|
|
166
|
+
return mime === token;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/** O tipo efetivo satisfaz `mediaType` (lista) ou `accept` (texto livre)? Sem nenhum dos dois, tudo passa. */
|
|
170
|
+
export function matchesMedia(name, mime, mediaType, accept) {
|
|
171
|
+
const hasMedia = Boolean(mediaType && mediaType.length > 0);
|
|
172
|
+
const hasAccept = Boolean(accept && accept.trim());
|
|
173
|
+
if (!hasMedia && !hasAccept)
|
|
174
|
+
return true;
|
|
175
|
+
if (hasMedia) {
|
|
176
|
+
for (const m of mediaType) {
|
|
177
|
+
if (m === "all")
|
|
178
|
+
return true;
|
|
179
|
+
const info = MEDIA_TYPES[m];
|
|
180
|
+
if (!info)
|
|
181
|
+
continue;
|
|
182
|
+
if (info.group && mime.startsWith(info.group))
|
|
183
|
+
return true;
|
|
184
|
+
if (info.mime && mime === info.mime)
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return hasAccept ? matchesAcceptToken(name, mime, accept) : false;
|
|
189
|
+
}
|
|
190
|
+
const UNITS = { b: 1, kb: 1024, mb: 1024 ** 2, gb: 1024 ** 3, tb: 1024 ** 4 };
|
|
191
|
+
/**
|
|
192
|
+
* Tamanho em bytes: numero puro = bytes; texto com unidade (B, KB, MB, GB, TB; base 1024; maiusculas ou minusculas;
|
|
193
|
+
* decimal com ponto; espaco opcional). Invalido, vazio ou negativo => `undefined` (sem limite).
|
|
194
|
+
*/
|
|
195
|
+
export function parseSize(value) {
|
|
196
|
+
if (value === undefined || value === null)
|
|
197
|
+
return undefined;
|
|
198
|
+
if (typeof value === "number")
|
|
199
|
+
return Number.isFinite(value) && value >= 0 ? Math.floor(value) : undefined;
|
|
200
|
+
const m = /^\s*(\d+(?:\.\d+)?)\s*([a-zA-Z]*)\s*$/.exec(value);
|
|
201
|
+
if (!m)
|
|
202
|
+
return undefined;
|
|
203
|
+
const unitText = m[2] ?? "";
|
|
204
|
+
const unit = unitText === "" ? "b" : unitText.toLowerCase();
|
|
205
|
+
const factor = UNITS[unit];
|
|
206
|
+
return factor === undefined ? undefined : Math.floor(Number(m[1] ?? 0) * factor);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Decide quais dos arquivos escolhidos entram (`current` = tamanhos ja no campo). Cada recusa fica no seu arquivo
|
|
210
|
+
* (`type`, `size`, `count`, `total`); os demais seguem validos. Ordem das regras: tipo, tamanho por arquivo,
|
|
211
|
+
* quantidade, tamanho total.
|
|
212
|
+
*/
|
|
213
|
+
export function planAdd(currentSizes, picked, rules = {}) {
|
|
214
|
+
const maxFiles = Math.max(1, Math.floor(rules.maxFiles ?? 1));
|
|
215
|
+
const perFile = parseSize(rules.maxSizePerFile);
|
|
216
|
+
const total = parseSize(rules.maxSizeTotal);
|
|
217
|
+
const replace = maxFiles === 1;
|
|
218
|
+
let count = replace ? 0 : currentSizes.length;
|
|
219
|
+
let sum = replace ? 0 : currentSizes.reduce((a, b) => a + b, 0);
|
|
220
|
+
const accepted = [];
|
|
221
|
+
const rejected = [];
|
|
222
|
+
picked.forEach((file, index) => {
|
|
223
|
+
const mime = resolveMime(file.name, file.type, file.head);
|
|
224
|
+
let code = null;
|
|
225
|
+
if (!matchesMedia(file.name, mime, rules.mediaType, rules.accept))
|
|
226
|
+
code = "type";
|
|
227
|
+
else if (perFile !== undefined && file.size > perFile)
|
|
228
|
+
code = "size";
|
|
229
|
+
else if (count >= maxFiles)
|
|
230
|
+
code = "count";
|
|
231
|
+
else if (total !== undefined && sum + file.size > total)
|
|
232
|
+
code = "total";
|
|
233
|
+
if (code) {
|
|
234
|
+
rejected.push({ index, code });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
accepted.push(index);
|
|
238
|
+
count += 1;
|
|
239
|
+
sum += file.size;
|
|
240
|
+
});
|
|
241
|
+
return { accepted, rejected, replace };
|
|
242
|
+
}
|
|
243
|
+
/** `1536` => "1,5 KB" (virgula fora do en-US). Base 1024; ate TB. */
|
|
244
|
+
export function formatFileSize(bytes, locale = "en-US") {
|
|
245
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
246
|
+
let value = Math.max(0, bytes);
|
|
247
|
+
let unit = 0;
|
|
248
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
249
|
+
value /= 1024;
|
|
250
|
+
unit += 1;
|
|
251
|
+
}
|
|
252
|
+
const text = unit === 0 ? String(Math.round(value)) : value.toFixed(1);
|
|
253
|
+
return `${locale === "en-US" ? text : text.replace(".", ",")} ${units[unit]}`;
|
|
254
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type ImageErrorCode = "type" | "size" | "dimension" | "aspect";
|
|
2
|
+
export type ImageFacts = {
|
|
3
|
+
name: string;
|
|
4
|
+
/** Tipo MIME (`image/png`); pode vir vazio. */
|
|
5
|
+
type: string;
|
|
6
|
+
size: number;
|
|
7
|
+
/** Dimensoes em px; ausentes quando nao foi possivel ler (as regras de dimensao/proporcao so rodam se existirem). */
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
};
|
|
11
|
+
export type ImageRules = {
|
|
12
|
+
/** Lista separada por virgula: `image/*`, `image/png`, `.png`. Padrao: `image/*`. */
|
|
13
|
+
accept?: string;
|
|
14
|
+
maxSizeBytes?: number;
|
|
15
|
+
minWidth?: number;
|
|
16
|
+
minHeight?: number;
|
|
17
|
+
/** Proporcao largura/altura exigida (`1`, `16 / 9`). */
|
|
18
|
+
aspect?: number;
|
|
19
|
+
/** Tolerancia relativa da proporcao (padrao 0.02 = 2%). */
|
|
20
|
+
aspectTolerance?: number;
|
|
21
|
+
};
|
|
22
|
+
export declare const DEFAULT_ACCEPT = "image/*";
|
|
23
|
+
/** O arquivo satisfaz `accept`? Compara extensao (`.png`), tipo exato (`image/png`) e curinga (`image/*`). */
|
|
24
|
+
export declare function matchesAccept(name: string, type: string, accept?: string): boolean;
|
|
25
|
+
/** Primeiro erro encontrado (ordem: tipo, tamanho, dimensao, proporcao) ou `null`. */
|
|
26
|
+
export declare function validateImage(file: ImageFacts, rules?: ImageRules): ImageErrorCode | null;
|
|
27
|
+
/** `1536` => "1,5 KB" (virgula fora do en-US). Usa 1024 como base; ate GB. */
|
|
28
|
+
export declare function formatBytes(bytes: number, locale?: string): string;
|
|
29
|
+
/** Texto da proporcao para a mensagem: inteiros viram "16:9"; senao 2 casas ("1.78"). */
|
|
30
|
+
export declare function formatAspect(aspect: number): string;
|
|
31
|
+
//# sourceMappingURL=imageUploadLogic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageUploadLogic.d.ts","sourceRoot":"","sources":["../../src/inputs/imageUploadLogic.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEtE,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,qHAAqH;IACrH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,8GAA8G;AAC9G,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAalF;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,GAAE,UAAe,GAAG,cAAc,GAAG,IAAI,CAiB7F;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAAU,GAAG,MAAM,CAUnE;AAED,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAYnD"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Logica pura do SgImageUpload (P133): tipos aceitos, validacao (tipo, tamanho, dimensao minima, proporcao) e
|
|
2
|
+
// texto de tamanho. Sem DOM: os vetores de paridade com o Flutter (`seedgrid_components/test/widgets/
|
|
3
|
+
// image_upload_vectors`) sao gerados DESTE modulo.
|
|
4
|
+
export const DEFAULT_ACCEPT = "image/*";
|
|
5
|
+
/** O arquivo satisfaz `accept`? Compara extensao (`.png`), tipo exato (`image/png`) e curinga (`image/*`). */
|
|
6
|
+
export function matchesAccept(name, type, accept) {
|
|
7
|
+
const tokens = (accept ?? DEFAULT_ACCEPT)
|
|
8
|
+
.split(",")
|
|
9
|
+
.map((t) => t.trim().toLowerCase())
|
|
10
|
+
.filter(Boolean);
|
|
11
|
+
if (tokens.length === 0)
|
|
12
|
+
return true;
|
|
13
|
+
const lowerName = name.toLowerCase();
|
|
14
|
+
const lowerType = type.toLowerCase();
|
|
15
|
+
return tokens.some((token) => {
|
|
16
|
+
if (token.startsWith("."))
|
|
17
|
+
return lowerName.endsWith(token);
|
|
18
|
+
if (token.endsWith("/*"))
|
|
19
|
+
return lowerType.startsWith(token.slice(0, -1));
|
|
20
|
+
return lowerType === token;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/** Primeiro erro encontrado (ordem: tipo, tamanho, dimensao, proporcao) ou `null`. */
|
|
24
|
+
export function validateImage(file, rules = {}) {
|
|
25
|
+
if (!matchesAccept(file.name, file.type, rules.accept))
|
|
26
|
+
return "type";
|
|
27
|
+
if (rules.maxSizeBytes !== undefined && file.size > rules.maxSizeBytes)
|
|
28
|
+
return "size";
|
|
29
|
+
const { width, height } = file;
|
|
30
|
+
if (width !== undefined && height !== undefined) {
|
|
31
|
+
if ((rules.minWidth !== undefined && width < rules.minWidth) ||
|
|
32
|
+
(rules.minHeight !== undefined && height < rules.minHeight)) {
|
|
33
|
+
return "dimension";
|
|
34
|
+
}
|
|
35
|
+
if (rules.aspect !== undefined && rules.aspect > 0 && height > 0) {
|
|
36
|
+
const tolerance = rules.aspectTolerance ?? 0.02;
|
|
37
|
+
if (Math.abs(width / height - rules.aspect) / rules.aspect > tolerance)
|
|
38
|
+
return "aspect";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
/** `1536` => "1,5 KB" (virgula fora do en-US). Usa 1024 como base; ate GB. */
|
|
44
|
+
export function formatBytes(bytes, locale = "en-US") {
|
|
45
|
+
const units = ["B", "KB", "MB", "GB"];
|
|
46
|
+
let value = Math.max(0, bytes);
|
|
47
|
+
let unit = 0;
|
|
48
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
49
|
+
value /= 1024;
|
|
50
|
+
unit += 1;
|
|
51
|
+
}
|
|
52
|
+
const text = unit === 0 ? String(Math.round(value)) : value.toFixed(1);
|
|
53
|
+
return `${locale === "en-US" ? text : text.replace(".", ",")} ${units[unit]}`;
|
|
54
|
+
}
|
|
55
|
+
/** Texto da proporcao para a mensagem: inteiros viram "16:9"; senao 2 casas ("1.78"). */
|
|
56
|
+
export function formatAspect(aspect) {
|
|
57
|
+
const known = [
|
|
58
|
+
[1, "1:1"],
|
|
59
|
+
[4 / 3, "4:3"],
|
|
60
|
+
[3 / 2, "3:2"],
|
|
61
|
+
[16 / 9, "16:9"],
|
|
62
|
+
[3 / 4, "3:4"],
|
|
63
|
+
[2 / 3, "2:3"],
|
|
64
|
+
[9 / 16, "9:16"]
|
|
65
|
+
];
|
|
66
|
+
for (const [value, text] of known)
|
|
67
|
+
if (Math.abs(value - aspect) < 1e-9)
|
|
68
|
+
return text;
|
|
69
|
+
return aspect.toFixed(2);
|
|
70
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SgDockMenu.d.ts","sourceRoot":"","sources":["../../src/menus/SgDockMenu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,MAAM,MAAM,kBAAkB,GAC1B,UAAU,GAAG,aAAa,GAAG,aAAa,GAC1C,YAAY,GAAG,eAAe,GAC9B,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC;AAElD,MAAM,MAAM,qBAAqB,GAAG,YAAY,GAAG,UAAU,CAAC;AAE9D,MAAM,MAAM,cAAc,GAAG;IAC3B,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,sBAAsB;IACtB,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,wBAAwB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,yCAAyC;IACzC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,MAAM,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACxC,mBAAmB;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AA0BD,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"SgDockMenu.d.ts","sourceRoot":"","sources":["../../src/menus/SgDockMenu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,MAAM,MAAM,kBAAkB,GAC1B,UAAU,GAAG,aAAa,GAAG,aAAa,GAC1C,YAAY,GAAG,eAAe,GAC9B,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC;AAElD,MAAM,MAAM,qBAAqB,GAAG,YAAY,GAAG,UAAU,CAAC;AAE9D,MAAM,MAAM,cAAc,GAAG;IAC3B,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,sBAAsB;IACtB,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,wBAAwB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,yCAAyC;IACzC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,MAAM,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACxC,mBAAmB;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AA0BD,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,2CA2f1D"}
|
package/dist/menus/SgDockMenu.js
CHANGED
|
@@ -282,6 +282,8 @@ export function SgDockMenu(props) {
|
|
|
282
282
|
});
|
|
283
283
|
return (_jsxs(_Fragment, { children: [_jsx("div", { ref: containerRef, id: id, className: `sg-dock ${className} ${ELEV_CLS[elevation]}`, style: {
|
|
284
284
|
...containerStyleState.style,
|
|
285
|
+
// A sombra (ELEV_CLS) fica neste wrapper: sem o raio ela desenhava um retangulo quadrado atras do dock (P140).
|
|
286
|
+
borderRadius: `${borderRadius}px`,
|
|
285
287
|
...style,
|
|
286
288
|
transform: containerStyleState.transform,
|
|
287
289
|
cursor: enableDragDrop ? (isDragging ? "grabbing" : "grab") : "default",
|