@jjlmoya/utils-developer 1.17.0 → 1.20.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 +1 -1
- package/src/category/index.ts +2 -1
- package/src/entries.ts +7 -1
- package/src/index.ts +3 -0
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/calculadoraTiempoDatos/data-time-calculator-web-speed-impact.css +498 -499
- package/src/tool/cssBoxShadowGenerator/bibliography.astro +14 -0
- package/src/tool/cssBoxShadowGenerator/bibliography.ts +14 -0
- package/src/tool/cssBoxShadowGenerator/box-shadow-generator.css +376 -0
- package/src/tool/cssBoxShadowGenerator/component.astro +234 -0
- package/src/tool/cssBoxShadowGenerator/entry.ts +29 -0
- package/src/tool/cssBoxShadowGenerator/i18n/de.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/en.ts +71 -0
- package/src/tool/cssBoxShadowGenerator/i18n/es.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/fr.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/id.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/it.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/ja.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/ko.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/nl.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/pl.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/pt.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/ru.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/sv.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/tr.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/i18n/zh.ts +69 -0
- package/src/tool/cssBoxShadowGenerator/index.ts +9 -0
- package/src/tool/cssBoxShadowGenerator/logic.ts +35 -0
- package/src/tool/cssBoxShadowGenerator/seo.astro +15 -0
- package/src/tool/cssBoxShadowGenerator/ui.ts +23 -0
- package/src/tool/visualCssGridFlexboxGenerator/bibliography.astro +14 -0
- package/src/tool/visualCssGridFlexboxGenerator/bibliography.ts +20 -0
- package/src/tool/visualCssGridFlexboxGenerator/canvas.ts +103 -0
- package/src/tool/visualCssGridFlexboxGenerator/component.astro +218 -0
- package/src/tool/visualCssGridFlexboxGenerator/entry.ts +30 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/de.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/en.ts +256 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/es.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/fr.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/id.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/it.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/ja.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/ko.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/nl.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/pl.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/pt.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/ru.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/sv.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/tr.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/i18n/zh.ts +110 -0
- package/src/tool/visualCssGridFlexboxGenerator/index.ts +11 -0
- package/src/tool/visualCssGridFlexboxGenerator/interactions.ts +45 -0
- package/src/tool/visualCssGridFlexboxGenerator/logic.ts +49 -0
- package/src/tool/visualCssGridFlexboxGenerator/presets.ts +63 -0
- package/src/tool/visualCssGridFlexboxGenerator/seo.astro +15 -0
- package/src/tool/visualCssGridFlexboxGenerator/ui.ts +58 -0
- package/src/tool/visualCssGridFlexboxGenerator/visual-css-grid-flexbox-generator.css +816 -0
- package/src/tool/visualCssGridFlexboxGenerator/workspace.ts +192 -0
- package/src/tools.ts +3 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { LayoutMode } from './logic';
|
|
2
|
+
|
|
3
|
+
export function setupSpanHandler(
|
|
4
|
+
canvas: HTMLElement,
|
|
5
|
+
modeFn: () => LayoutMode,
|
|
6
|
+
itemPrefix: string,
|
|
7
|
+
onUpdate: () => void,
|
|
8
|
+
) {
|
|
9
|
+
canvas.addEventListener('dblclick', (e: MouseEvent) => {
|
|
10
|
+
const item = (e.target as HTMLElement).closest('.vcg-item') as HTMLElement | null;
|
|
11
|
+
if (!item || modeFn() !== 'grid') return;
|
|
12
|
+
const cur = Number(item.dataset.span ?? '1');
|
|
13
|
+
const next = cur >= 3 ? 1 : cur + 1;
|
|
14
|
+
item.dataset.span = String(next);
|
|
15
|
+
const label = item.querySelector('span');
|
|
16
|
+
if (label) {
|
|
17
|
+
const num = item.dataset.item;
|
|
18
|
+
label.textContent = next > 1 ? `${itemPrefix} ${num} (${next}x)` : `${itemPrefix} ${num}`;
|
|
19
|
+
}
|
|
20
|
+
onUpdate();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function spawnConfetti(x: number, y: number) {
|
|
25
|
+
const container = document.createElement('div');
|
|
26
|
+
container.className = 'vcg-confetti-container';
|
|
27
|
+
const colors = ['#2dd4bf', '#fbbf24', '#6366f1', '#f472b6', '#34d399', '#f97316', '#a78bfa', '#22d3ee', '#fde047'];
|
|
28
|
+
for (let i = 0; i < 32; i++) {
|
|
29
|
+
const p = document.createElement('div');
|
|
30
|
+
p.className = 'vcg-confetti-piece';
|
|
31
|
+
p.style.left = `${x}px`;
|
|
32
|
+
p.style.top = `${y}px`;
|
|
33
|
+
p.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]!;
|
|
34
|
+
p.style.width = `${5 + Math.random() * 7}px`;
|
|
35
|
+
p.style.height = `${5 + Math.random() * 7}px`;
|
|
36
|
+
const a = Math.random() * Math.PI * 2;
|
|
37
|
+
const d = 40 + Math.random() * 80;
|
|
38
|
+
p.style.setProperty('--cx', `${Math.cos(a) * d}px`);
|
|
39
|
+
p.style.setProperty('--cy', `${Math.sin(a) * d - 30}px`);
|
|
40
|
+
p.style.setProperty('--cr', `${Math.random() * 720 - 360}deg`);
|
|
41
|
+
container.append(p);
|
|
42
|
+
}
|
|
43
|
+
document.body.append(container);
|
|
44
|
+
setTimeout(() => container.remove(), 900);
|
|
45
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type LayoutMode = 'flex' | 'grid';
|
|
2
|
+
|
|
3
|
+
export interface LayoutState {
|
|
4
|
+
mode: LayoutMode;
|
|
5
|
+
gap: number;
|
|
6
|
+
columns: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
itemSize: number;
|
|
10
|
+
justifyContent: string;
|
|
11
|
+
alignItems: string;
|
|
12
|
+
flexDirection: string;
|
|
13
|
+
flexWrap: string;
|
|
14
|
+
alignContent: string;
|
|
15
|
+
itemSpans: Record<string, number>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function buildSpanRules(spans: Record<string, number>): string[] {
|
|
19
|
+
const lines: string[] = [];
|
|
20
|
+
for (const [id, span] of Object.entries(spans)) {
|
|
21
|
+
if (span > 1) {
|
|
22
|
+
lines.push(`.layout-item[data-item="${id}"] {`, ` grid-column: span ${span};`, '}');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return lines;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function buildLayoutCss(state: LayoutState): string {
|
|
29
|
+
const lines: string[] = ['.layout-playground {'];
|
|
30
|
+
|
|
31
|
+
if (state.mode === 'grid') {
|
|
32
|
+
lines.push(' display: grid;', ` grid-template-columns: repeat(${state.columns}, minmax(0, 1fr));`);
|
|
33
|
+
} else {
|
|
34
|
+
lines.push(' display: flex;', ` flex-direction: ${state.flexDirection};`, ` flex-wrap: ${state.flexWrap};`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
lines.push(` gap: ${state.gap}px;`, ` justify-content: ${state.justifyContent};`, ` align-items: ${state.alignItems};`);
|
|
38
|
+
|
|
39
|
+
if (state.mode === 'flex' && state.flexWrap !== 'nowrap') {
|
|
40
|
+
lines.push(` align-content: ${state.alignContent};`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
lines.push(` width: ${state.width}px;`, ` min-height: ${state.height}px;`, '}', '', '.layout-item {', ` min-width: ${state.itemSize}px;`, ` min-height: ${state.itemSize}px;`, '}');
|
|
44
|
+
|
|
45
|
+
if (state.mode === 'grid') {
|
|
46
|
+
lines.push(...buildSpanRules(state.itemSpans));
|
|
47
|
+
}
|
|
48
|
+
return lines.join('\n');
|
|
49
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { LayoutMode } from './logic';
|
|
2
|
+
import { getAllItems, clearPresetActive } from './canvas';
|
|
3
|
+
|
|
4
|
+
type PresetCfg = Partial<{
|
|
5
|
+
mode: LayoutMode;
|
|
6
|
+
gap: number; columns: number; width: number; height: number; itemSize: number;
|
|
7
|
+
justifyContent: string; alignItems: string; flexDirection: string;
|
|
8
|
+
flexWrap: string; alignContent: string;
|
|
9
|
+
}>;
|
|
10
|
+
|
|
11
|
+
export const PRESETS: Record<string, PresetCfg> = {
|
|
12
|
+
navbar: { mode: 'flex', flexDirection: 'row', flexWrap: 'nowrap', gap: 12, justifyContent: 'space-between', alignItems: 'center', alignContent: 'stretch', width: 760, height: 220, itemSize: 80 },
|
|
13
|
+
cards: { mode: 'grid', columns: 3, gap: 24, justifyContent: 'center', alignItems: 'center', width: 760, height: 420, itemSize: 100 },
|
|
14
|
+
hero: { mode: 'flex', flexDirection: 'column', flexWrap: 'nowrap', gap: 20, justifyContent: 'center', alignItems: 'center', alignContent: 'center', width: 600, height: 500, itemSize: 120 },
|
|
15
|
+
sidebar: { mode: 'grid', columns: 2, gap: 16, justifyContent: 'start', alignItems: 'stretch', width: 760, height: 460, itemSize: 100 },
|
|
16
|
+
gallery: { mode: 'grid', columns: 4, gap: 12, justifyContent: 'center', alignItems: 'start', width: 760, height: 400, itemSize: 80 },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
interface PresetCtrls {
|
|
20
|
+
gapInput: HTMLInputElement;
|
|
21
|
+
columnsInput: HTMLInputElement;
|
|
22
|
+
widthInput: HTMLInputElement;
|
|
23
|
+
heightInput: HTMLInputElement;
|
|
24
|
+
itemSizeInput: HTMLInputElement;
|
|
25
|
+
justifySelect: HTMLSelectElement;
|
|
26
|
+
alignSelect: HTMLSelectElement;
|
|
27
|
+
directionSelect: HTMLSelectElement;
|
|
28
|
+
wrapSelect: HTMLSelectElement;
|
|
29
|
+
alignContentSelect: HTMLSelectElement;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const KEY_MAP: Record<string, keyof PresetCtrls> = {
|
|
33
|
+
gap: 'gapInput', columns: 'columnsInput', width: 'widthInput', height: 'heightInput',
|
|
34
|
+
itemSize: 'itemSizeInput', justifyContent: 'justifySelect', alignItems: 'alignSelect',
|
|
35
|
+
flexDirection: 'directionSelect', flexWrap: 'wrapSelect', alignContent: 'alignContentSelect',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
interface PresetContext {
|
|
39
|
+
canvas: HTMLElement;
|
|
40
|
+
ctrls: PresetCtrls;
|
|
41
|
+
setMode: (m: LayoutMode) => void;
|
|
42
|
+
update: () => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function applyPreset(name: string, ctx: PresetContext) {
|
|
46
|
+
const cfg = PRESETS[name];
|
|
47
|
+
if (!cfg) return;
|
|
48
|
+
|
|
49
|
+
clearPresetActive();
|
|
50
|
+
const btn = document.querySelector(`[data-preset="${name}"]`);
|
|
51
|
+
if (btn) btn.classList.add('vcg-preset-active');
|
|
52
|
+
|
|
53
|
+
getAllItems(ctx.canvas).forEach((item) => { item.dataset.span = '1'; });
|
|
54
|
+
if (cfg.mode) ctx.setMode(cfg.mode);
|
|
55
|
+
|
|
56
|
+
for (const [key, value] of Object.entries(cfg)) {
|
|
57
|
+
if (key === 'mode') continue;
|
|
58
|
+
const ck = KEY_MAP[key] as keyof PresetCtrls | undefined;
|
|
59
|
+
if (ck && ctx.ctrls[ck]) ctx.ctrls[ck].value = String(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ctx.update();
|
|
63
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { visualCssGridFlexboxGenerator } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const content = await visualCssGridFlexboxGenerator.i18n[locale]?.();
|
|
12
|
+
if (!content) return null;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface VisualCssGridFlexboxGeneratorUI extends Record<string, string> {
|
|
2
|
+
modeLabel: string;
|
|
3
|
+
flexMode: string;
|
|
4
|
+
gridMode: string;
|
|
5
|
+
canvasLabel: string;
|
|
6
|
+
addItem: string;
|
|
7
|
+
removeItem: string;
|
|
8
|
+
resetLayout: string;
|
|
9
|
+
gapLabel: string;
|
|
10
|
+
columnsLabel: string;
|
|
11
|
+
widthLabel: string;
|
|
12
|
+
heightLabel: string;
|
|
13
|
+
justifyLabel: string;
|
|
14
|
+
alignLabel: string;
|
|
15
|
+
itemSizeLabel: string;
|
|
16
|
+
codeTitle: string;
|
|
17
|
+
copyCode: string;
|
|
18
|
+
copied: string;
|
|
19
|
+
dragHint: string;
|
|
20
|
+
outputLabel: string;
|
|
21
|
+
justifyStart: string;
|
|
22
|
+
justifyCenter: string;
|
|
23
|
+
justifyEnd: string;
|
|
24
|
+
justifyBetween: string;
|
|
25
|
+
justifyAround: string;
|
|
26
|
+
justifyEvenly: string;
|
|
27
|
+
alignStart: string;
|
|
28
|
+
alignCenter: string;
|
|
29
|
+
alignEnd: string;
|
|
30
|
+
alignStretch: string;
|
|
31
|
+
alignBaseline: string;
|
|
32
|
+
itemPrefix: string;
|
|
33
|
+
directionLabel: string;
|
|
34
|
+
directionRow: string;
|
|
35
|
+
directionRowReverse: string;
|
|
36
|
+
directionColumn: string;
|
|
37
|
+
directionColumnReverse: string;
|
|
38
|
+
wrapLabel: string;
|
|
39
|
+
wrapNoWrap: string;
|
|
40
|
+
wrapWrap: string;
|
|
41
|
+
wrapWrapReverse: string;
|
|
42
|
+
alignContentLabel: string;
|
|
43
|
+
alignContentStart: string;
|
|
44
|
+
alignContentCenter: string;
|
|
45
|
+
alignContentEnd: string;
|
|
46
|
+
alignContentBetween: string;
|
|
47
|
+
alignContentAround: string;
|
|
48
|
+
alignContentEvenly: string;
|
|
49
|
+
alignContentStretch: string;
|
|
50
|
+
presetsLabel: string;
|
|
51
|
+
presetNavbar: string;
|
|
52
|
+
presetCards: string;
|
|
53
|
+
presetHero: string;
|
|
54
|
+
presetSidebar: string;
|
|
55
|
+
presetGallery: string;
|
|
56
|
+
shakeLimit: string;
|
|
57
|
+
spanHint: string;
|
|
58
|
+
}
|