Skip to content

Commit 8651b60

Browse files
feat: side-by-side Default/Pressed icon grid in properties panel
1 parent 924611e commit 8651b60

2 files changed

Lines changed: 138 additions & 61 deletions

File tree

src/App.css

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,82 @@
12111211
border-color: #703030;
12121212
}
12131213

1214+
/* ── Icon side-by-side grid (Default / Pressed) ──────── */
1215+
.icons-grid {
1216+
display: grid;
1217+
grid-template-columns: 1fr 1fr;
1218+
gap: 10px;
1219+
margin-top: 6px;
1220+
}
1221+
1222+
.icon-slot {
1223+
display: flex;
1224+
flex-direction: column;
1225+
align-items: center;
1226+
gap: 5px;
1227+
}
1228+
1229+
.icon-slot-preview {
1230+
width: 100%;
1231+
aspect-ratio: 1;
1232+
border: 1px dashed #2e2e2e;
1233+
border-radius: 8px;
1234+
overflow: hidden;
1235+
cursor: pointer;
1236+
transition: border-color 0.15s;
1237+
display: flex;
1238+
align-items: center;
1239+
justify-content: center;
1240+
background: #1e1e1e;
1241+
}
1242+
1243+
.icon-slot-preview:hover {
1244+
border-color: #0082ff;
1245+
}
1246+
1247+
.icon-slot-preview:hover .icon-picker-placeholder {
1248+
color: #666;
1249+
}
1250+
1251+
.icon-slot-label {
1252+
font-size: 10px;
1253+
font-weight: 600;
1254+
color: #484848;
1255+
letter-spacing: 0.06em;
1256+
text-transform: uppercase;
1257+
}
1258+
1259+
.icon-slot-actions {
1260+
display: flex;
1261+
gap: 4px;
1262+
justify-content: center;
1263+
}
1264+
1265+
.icon-slot-btn {
1266+
display: flex;
1267+
align-items: center;
1268+
justify-content: center;
1269+
width: 26px;
1270+
height: 26px;
1271+
border-radius: 5px;
1272+
background: #222;
1273+
border: 1px solid #2e2e2e;
1274+
color: #666;
1275+
cursor: pointer;
1276+
transition: background 0.12s, color 0.12s;
1277+
}
1278+
1279+
.icon-slot-btn:hover {
1280+
background: #2e2e2e;
1281+
color: #bbb;
1282+
}
1283+
1284+
.icon-slot-btn.icon-slot-remove:hover {
1285+
color: #e05555;
1286+
border-color: #703030;
1287+
background: rgba(224, 85, 85, 0.07);
1288+
}
1289+
12141290
/* ── Context menu ────────────────────────────────────── */
12151291
.context-menu {
12161292
position: fixed;

src/App.jsx

Lines changed: 62 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,71 +1572,72 @@ function PropertiesPanel({ keyIndex, onClose, config, onChange, iconSize, profil
15721572
<div className="prop-section">
15731573
<span className="prop-label">Icon</span>
15741574

1575-
<span className="prop-label-sm">Default</span>
1576-
<div className="icon-picker-area" onClick={() => fileInputRef.current?.click()}>
1577-
{config?.iconDataUrl ? (
1578-
<img src={config.iconDataUrl} className="icon-preview" alt="Button icon" />
1579-
) : (
1580-
<div className="icon-picker-placeholder">
1581-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" width="24" height="24">
1582-
<rect x="3" y="3" width="18" height="18" rx="3" />
1583-
<path d="M12 8v8M8 12h8" />
1584-
</svg>
1585-
<span>Add image</span>
1575+
<div className="icons-grid">
1576+
{/* Default icon slot */}
1577+
<div className="icon-slot">
1578+
<div className="icon-slot-preview" onClick={() => fileInputRef.current?.click()}>
1579+
{config?.iconDataUrl ? (
1580+
<img src={config.iconDataUrl} className="icon-preview" alt="Button icon" />
1581+
) : (
1582+
<div className="icon-picker-placeholder">
1583+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" width="22" height="22">
1584+
<rect x="3" y="3" width="18" height="18" rx="3" />
1585+
<path d="M12 8v8M8 12h8" />
1586+
</svg>
1587+
</div>
1588+
)}
15861589
</div>
1587-
)}
1588-
</div>
1589-
<div className="icon-action-row">
1590-
<button className="icon-lib-open-btn" onClick={() => setLibraryTarget('default')}>
1591-
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" width="13" height="13">
1592-
<path d="M2 4.5A1.5 1.5 0 0 1 3.5 3h3l1.5 2H13a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H3.5A1.5 1.5 0 0 1 2 11.5v-7z" />
1593-
</svg>
1594-
Icon Library
1595-
</button>
1596-
{config?.iconDataUrl && (
1597-
<button className="icon-remove-btn" onClick={removeIcon}>Remove</button>
1598-
)}
1599-
</div>
1600-
<input
1601-
ref={fileInputRef}
1602-
type="file"
1603-
accept="image/*"
1604-
style={{ display: 'none' }}
1605-
onChange={handleIconSelect}
1606-
/>
1590+
<span className="icon-slot-label">Default</span>
1591+
<div className="icon-slot-actions">
1592+
<button className="icon-slot-btn" title="Icon Library" onClick={() => setLibraryTarget('default')}>
1593+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" width="13" height="13">
1594+
<path d="M2 4.5A1.5 1.5 0 0 1 3.5 3h3l1.5 2H13a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H3.5A1.5 1.5 0 0 1 2 11.5v-7z" />
1595+
</svg>
1596+
</button>
1597+
{config?.iconDataUrl && (
1598+
<button className="icon-slot-btn icon-slot-remove" title="Remove icon" onClick={removeIcon}>
1599+
<svg viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.6" width="10" height="10" strokeLinecap="round">
1600+
<path d="M1 1l8 8M9 1L1 9" />
1601+
</svg>
1602+
</button>
1603+
)}
1604+
</div>
1605+
</div>
16071606

1608-
<span className="prop-label-sm" style={{ marginTop: 10 }}>Pressed</span>
1609-
<div className="icon-picker-area" onClick={() => pressedFileInputRef.current?.click()}>
1610-
{config?.pressedIconDataUrl ? (
1611-
<img src={config.pressedIconDataUrl} className="icon-preview" alt="Pressed icon" />
1612-
) : (
1613-
<div className="icon-picker-placeholder">
1614-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" width="24" height="24">
1615-
<rect x="3" y="3" width="18" height="18" rx="3" />
1616-
<path d="M12 8v8M8 12h8" />
1617-
</svg>
1618-
<span>Add image</span>
1607+
{/* Pressed icon slot */}
1608+
<div className="icon-slot">
1609+
<div className="icon-slot-preview" onClick={() => pressedFileInputRef.current?.click()}>
1610+
{config?.pressedIconDataUrl ? (
1611+
<img src={config.pressedIconDataUrl} className="icon-preview" alt="Pressed icon" />
1612+
) : (
1613+
<div className="icon-picker-placeholder">
1614+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" width="22" height="22">
1615+
<rect x="3" y="3" width="18" height="18" rx="3" />
1616+
<path d="M12 8v8M8 12h8" />
1617+
</svg>
1618+
</div>
1619+
)}
16191620
</div>
1620-
)}
1621-
</div>
1622-
<div className="icon-action-row">
1623-
<button className="icon-lib-open-btn" onClick={() => setLibraryTarget('pressed')}>
1624-
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" width="13" height="13">
1625-
<path d="M2 4.5A1.5 1.5 0 0 1 3.5 3h3l1.5 2H13a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H3.5A1.5 1.5 0 0 1 2 11.5v-7z" />
1626-
</svg>
1627-
Icon Library
1628-
</button>
1629-
{config?.pressedIconDataUrl && (
1630-
<button className="icon-remove-btn" onClick={removePressedIcon}>Remove</button>
1631-
)}
1621+
<span className="icon-slot-label">Pressed</span>
1622+
<div className="icon-slot-actions">
1623+
<button className="icon-slot-btn" title="Icon Library" onClick={() => setLibraryTarget('pressed')}>
1624+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" width="13" height="13">
1625+
<path d="M2 4.5A1.5 1.5 0 0 1 3.5 3h3l1.5 2H13a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H3.5A1.5 1.5 0 0 1 2 11.5v-7z" />
1626+
</svg>
1627+
</button>
1628+
{config?.pressedIconDataUrl && (
1629+
<button className="icon-slot-btn icon-slot-remove" title="Remove pressed icon" onClick={removePressedIcon}>
1630+
<svg viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.6" width="10" height="10" strokeLinecap="round">
1631+
<path d="M1 1l8 8M9 1L1 9" />
1632+
</svg>
1633+
</button>
1634+
)}
1635+
</div>
1636+
</div>
16321637
</div>
1633-
<input
1634-
ref={pressedFileInputRef}
1635-
type="file"
1636-
accept="image/*"
1637-
style={{ display: 'none' }}
1638-
onChange={handlePressedIconSelect}
1639-
/>
1638+
1639+
<input ref={fileInputRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={handleIconSelect} />
1640+
<input ref={pressedFileInputRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={handlePressedIconSelect} />
16401641

16411642
{libraryTarget && (
16421643
<IconLibraryModal

0 commit comments

Comments
 (0)