Skip to content

Commit 79f35b5

Browse files
committed
feat: magaziner line and box
1 parent 640bc70 commit 79f35b5

File tree

1 file changed

+142
-2
lines changed

1 file changed

+142
-2
lines changed

src/pages/apps/MagazinerPage.jsx

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
ArrowsOutIcon,
1414
PushPinIcon,
1515
FloppyDiskBackIcon,
16+
PlusIcon,
17+
SquareIcon,
1618
} from '@phosphor-icons/react';
1719
import useSeo from '../../hooks/useSeo';
1820
import { useToast } from '../../hooks/useToast';
@@ -94,11 +96,36 @@ const MagazinerPage = () => {
9496
const [shapesCount, setShapesCount] = useState(15);
9597
const [borderWidth, setBorderWidth] = useState(10);
9698
const [inputs, setInputs] = useState(initialInputs);
99+
const [assets, setAssets] = useState([]);
97100
const [isSaveDialogOpen, setIsSaveDialogOpen] = useState(false);
98101
const [isLoadDialogOpen, setIsLoadDialogOpen] = useState(false);
99102
const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);
100103
const [stickyPreview, setStickyPreview] = useState(true);
101104

105+
const addAsset = (type) => {
106+
const newAsset = {
107+
id: Date.now(),
108+
type,
109+
x: 50,
110+
y: 50,
111+
width: type === 'line' ? 20 : 10,
112+
height: type === 'line' ? 0.5 : 10,
113+
rotation: 0,
114+
opacity: 1,
115+
};
116+
setAssets([...assets, newAsset]);
117+
addToast({ title: 'ASSET_ADDED', message: `New ${type.toUpperCase()} entity initialized.` });
118+
};
119+
120+
const updateAsset = (id, field, value) => {
121+
setAssets(assets.map(a => a.id === id ? { ...a, [field]: value } : a));
122+
};
123+
124+
const removeAsset = (id) => {
125+
setAssets(assets.filter(a => a.id !== id));
126+
addToast({ title: 'ASSET_REMOVED', message: 'Entity purged from current sequence.', type: 'info' });
127+
};
128+
102129
const handleSavePreset = () => {
103130
const preset = {
104131
style,
@@ -111,6 +138,7 @@ const MagazinerPage = () => {
111138
shapesCount,
112139
borderWidth,
113140
inputs,
141+
assets,
114142
};
115143
localStorage.setItem('magaziner_preset', JSON.stringify(preset));
116144
addToast({ title: 'PRESET_SAVED', message: 'Current configuration stored in local memory.' });
@@ -131,6 +159,7 @@ const MagazinerPage = () => {
131159
setShapesCount(preset.shapesCount);
132160
setBorderWidth(preset.borderWidth);
133161
setInputs(preset.inputs);
162+
if (preset.assets) setAssets(preset.assets);
134163
addToast({ title: 'PRESET_LOADED', message: 'Configuration successfully restored.' });
135164
} catch (e) {
136165
addToast({ title: 'LOAD_ERROR', message: 'Stored preset is corrupted or incompatible.', type: 'error' });
@@ -444,7 +473,32 @@ const MagazinerPage = () => {
444473
}
445474
ctx.restore();
446475

447-
// 4. Typography
476+
// 4. Manual Assets (Structural Entities)
477+
ctx.save();
478+
assets.forEach(asset => {
479+
ctx.save();
480+
const ax = (asset.x / 100) * width;
481+
const ay = (asset.y / 100) * height;
482+
const aw = (asset.width / 100) * width;
483+
const ah = (asset.height / 100) * height;
484+
485+
ctx.translate(ax, ay);
486+
ctx.rotate(asset.rotation * (Math.PI / 180));
487+
ctx.globalAlpha = asset.opacity;
488+
ctx.fillStyle = accentColor.hex;
489+
ctx.strokeStyle = accentColor.hex;
490+
ctx.lineWidth = 1 * scale;
491+
492+
if (asset.type === 'line') {
493+
ctx.fillRect(-aw / 2, -ah / 2, aw, ah);
494+
} else if (asset.type === 'box') {
495+
ctx.strokeRect(-aw / 2, -ah / 2, aw, ah);
496+
}
497+
ctx.restore();
498+
});
499+
ctx.restore();
500+
501+
// 5. Typography
448502
if (options.includeText) {
449503
ctx.fillStyle = accentColor.hex;
450504
ctx.textBaseline = 'middle';
@@ -518,7 +572,7 @@ const MagazinerPage = () => {
518572
ctx.fillRect(0, 0, width, height);
519573
ctx.restore();
520574
}
521-
}, [style, pattern, primaryColor, accentColor, bgImage, seed, shapesCount, shapesOpacity, noiseOpacity, gridOpacity, borderWidth, inputs]);
575+
}, [style, pattern, primaryColor, accentColor, bgImage, seed, shapesCount, shapesOpacity, noiseOpacity, gridOpacity, borderWidth, inputs, assets]);
522576

523577
useEffect(() => {
524578
const canvas = canvasRef.current;
@@ -689,6 +743,92 @@ const MagazinerPage = () => {
689743
</div>
690744
</div>
691745

746+
<div className="border border-white/10 bg-white/[0.02] p-8 rounded-sm space-y-10">
747+
<h3 className="font-mono text-[10px] font-bold text-emerald-500 uppercase tracking-widest flex items-center gap-2 border-b border-white/5 pb-6">
748+
<SquareIcon weight="fill" />
749+
Visual_Assets_Manager
750+
</h3>
751+
752+
<div className="space-y-8">
753+
<div className="grid grid-cols-2 gap-4">
754+
<button
755+
onClick={() => addAsset('line')}
756+
className="py-3 border border-white/10 text-[9px] font-mono uppercase tracking-widest hover:bg-emerald-500 hover:text-black transition-all flex items-center justify-center gap-2"
757+
>
758+
<PlusIcon weight="bold" /> Add_Line
759+
</button>
760+
<button
761+
onClick={() => addAsset('box')}
762+
className="py-3 border border-white/10 text-[9px] font-mono uppercase tracking-widest hover:bg-emerald-500 hover:text-black transition-all flex items-center justify-center gap-2"
763+
>
764+
<PlusIcon weight="bold" /> Add_Box
765+
</button>
766+
</div>
767+
768+
<div className="space-y-12 overflow-y-auto max-h-[400px] pr-2 custom-scrollbar">
769+
{assets.map((asset) => (
770+
<div key={asset.id} className="p-4 border border-white/5 bg-white/[0.01] space-y-6">
771+
<div className="flex justify-between items-center border-b border-white/5 pb-2">
772+
<span className="text-[9px] font-mono text-gray-500 uppercase">{asset.type} {'//'} {asset.id.toString().slice(-4)}</span>
773+
<button
774+
onClick={() => removeAsset(asset.id)}
775+
className="text-red-500 hover:text-white transition-colors"
776+
>
777+
<TrashIcon weight="bold" size={14} />
778+
</button>
779+
</div>
780+
781+
<div className="space-y-4">
782+
<CustomSlider
783+
label="X Position"
784+
min={0}
785+
max={100}
786+
value={asset.x}
787+
onChange={(val) => updateAsset(asset.id, 'x', val)}
788+
/>
789+
<CustomSlider
790+
label="Y Position"
791+
min={0}
792+
max={100}
793+
value={asset.y}
794+
onChange={(val) => updateAsset(asset.id, 'y', val)}
795+
/>
796+
<CustomSlider
797+
label="Width"
798+
min={0.1}
799+
max={100}
800+
value={asset.width}
801+
onChange={(val) => updateAsset(asset.id, 'width', val)}
802+
/>
803+
<CustomSlider
804+
label="Height / Thickness"
805+
min={0.1}
806+
max={100}
807+
value={asset.height}
808+
onChange={(val) => updateAsset(asset.id, 'height', val)}
809+
/>
810+
<CustomSlider
811+
label="Rotation"
812+
min={0}
813+
max={360}
814+
value={asset.rotation}
815+
onChange={(val) => updateAsset(asset.id, 'rotation', val)}
816+
/>
817+
<CustomSlider
818+
label="Opacity"
819+
min={0}
820+
max={1}
821+
step={0.01}
822+
value={asset.opacity}
823+
onChange={(val) => updateAsset(asset.id, 'opacity', val)}
824+
/>
825+
</div>
826+
</div>
827+
))}
828+
</div>
829+
</div>
830+
</div>
831+
692832
{Object.entries(inputs).map(([key, config]) => (
693833
<div key={key} className="border border-white/10 bg-white/[0.02] p-8 rounded-sm space-y-10">
694834
<h3 className="font-mono text-[10px] font-bold text-emerald-500 uppercase tracking-widest flex items-center gap-2 border-b border-white/5 pb-6">

0 commit comments

Comments
 (0)