feat(ui): one action bar for both tabs, and no sideways scroll in the datasheet
- Rotate/Delete (and the board actions) moved OUT of the left column into a bar across the bottom of the dialog, so they are in the same place on both tabs. Buy joins them, right-aligned: it used to be the last thing in the datasheet body, which meant scrolling that pane to the end to reach it, and it did not exist at all on the Properties tab. - The datasheet scrolled sideways because the doc tables have a nowrap first column (the pin name) and wide pinouts pushed past the pane. The dialog is wider (720px, still clamped to the viewport so it cannot run off the right edge) and the doc now wraps: fixed table layout, wrapping first column, pre-wrap on code blocks, overflow-x hidden.
This commit is contained in:
parent
269503b386
commit
c6256cc49a
|
|
@ -9,7 +9,9 @@
|
|||
background-color: #2d2d2d;
|
||||
border: 1px solid #555;
|
||||
border-radius: 10px;
|
||||
width: 560px;
|
||||
/* Wide enough that a pinout table in a datasheet fits without sideways
|
||||
scrolling, and clamped so the dialog can never run off the right edge. */
|
||||
width: 720px;
|
||||
max-width: calc(100vw - 24px);
|
||||
max-height: calc(100vh - 24px);
|
||||
display: flex;
|
||||
|
|
@ -298,13 +300,24 @@
|
|||
|
||||
/* ── Actions ─────────────────────────────────────────────────────────── */
|
||||
|
||||
/* One action bar across the bottom of the dialog, OUTSIDE the tabs, so the
|
||||
same buttons are there whichever tab is open. Rotate/Delete (and the board
|
||||
actions) sit left; Buy sits right. */
|
||||
.pid-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
margin-top: auto;
|
||||
padding: 10px 12px;
|
||||
border-top: 1px solid #444;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.pid-actions-right {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* Solid fills, per the brand kit: interactive surfaces are the accent blue
|
||||
|
|
@ -343,8 +356,36 @@
|
|||
.pid-confirm-label {
|
||||
font-size: 12px;
|
||||
color: #ffb4ab;
|
||||
flex-basis: 100%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.pid-confirm-hint {
|
||||
color: #b58b86;
|
||||
font-style: normal;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Buy in the action bar: same solid treatment as the other buttons, in the
|
||||
brand's success green so it reads as the outward link, not an app action. */
|
||||
.pid-buy-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: #2c2c2e;
|
||||
border: 1px solid #4a4a4c;
|
||||
border-radius: 6px;
|
||||
color: #f5f5f7;
|
||||
padding: 6px 14px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pid-buy-button:hover {
|
||||
background: #3a3a3c;
|
||||
}
|
||||
|
||||
/* ── Tabs ────────────────────────────────────────────────────────────── */
|
||||
|
|
@ -382,9 +423,37 @@
|
|||
max-height: 46vh;
|
||||
}
|
||||
|
||||
/* The datasheet reuses the picker card's .cip-doc styles as-is. */
|
||||
/* The datasheet reuses the picker card's .cip-doc styles, with one override:
|
||||
NO sideways scrolling. The doc tables have a nowrap first column (a pin
|
||||
name), which pushed wide pinouts past the edge; here they wrap instead and
|
||||
long tokens break rather than widening the pane. */
|
||||
.pid-tab-body--doc {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.pid-tab-body--doc .cip-doc {
|
||||
max-height: none;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.pid-tab-body--doc .cip-doc table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pid-tab-body--doc .cip-doc td:first-child {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.pid-tab-body--doc .cip-doc pre {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* The body's own Buy footer is suppressed here — the dialog puts Buy in the
|
||||
action bar, where it stays visible on both tabs. */
|
||||
.pid-tab-body--doc .cip-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ── Property rows ───────────────────────────────────────────────────── */
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
* Merges the two surfaces that used to exist separately: the old
|
||||
* ComponentPropertyDialog (a 250px column of pin names, editors and
|
||||
* Rotate/Delete) and the component-picker info card (datasheet, brand, Buy).
|
||||
* Layout: actions and a live preview of the part on the left; Properties /
|
||||
* Datasheet tabs on the right.
|
||||
* Layout: a live preview of the part on the left, Properties / Datasheet
|
||||
* tabs on the right, and one action bar across the bottom (Rotate/Delete on
|
||||
* the left, Buy on the right) that stays put whichever tab is open.
|
||||
*
|
||||
* The preview is the REAL web component, mounted and scaled — not a drawing.
|
||||
* Pin markers are laid over it at their true positions and their labels flank
|
||||
|
|
@ -37,6 +38,7 @@ import {
|
|||
useComponentDoc,
|
||||
HIDDEN_PROPS,
|
||||
formatValue,
|
||||
safeHref,
|
||||
type PanelData,
|
||||
} from '../ComponentInfoPanel';
|
||||
import {
|
||||
|
|
@ -356,6 +358,10 @@ export const PartInspectorDialog: React.FC<PartInspectorDialogProps> = ({
|
|||
? componentMetadata.thumbnail
|
||||
: null;
|
||||
|
||||
// Same http(s)-only guard the picker card applies — docs are a surface the
|
||||
// pro overlay can write to, so the validation is load-bearing.
|
||||
const buyHref = safeHref(doc?.buy);
|
||||
|
||||
const editableProps = componentMetadata.properties.filter((p: any) => isEditable(p));
|
||||
const readOnlyProps = componentMetadata.properties.filter(
|
||||
(p: any) => !isEditable(p) && !HIDDEN_PROPS.has(p.name),
|
||||
|
|
@ -489,65 +495,6 @@ export const PartInspectorDialog: React.FC<PartInspectorDialogProps> = ({
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="pid-actions">
|
||||
{confirmingDelete ? (
|
||||
<>
|
||||
<span className="pid-confirm-label">
|
||||
{t('editor.componentProps.confirmDelete', { name: componentMetadata.name })}
|
||||
{deleteHint && <em className="pid-confirm-hint">{deleteHint}</em>}
|
||||
</span>
|
||||
<button
|
||||
className="pid-action-button"
|
||||
onClick={() => setConfirmingDelete(false)}
|
||||
title={t('editor.componentProps.cancel')}
|
||||
>
|
||||
{t('editor.componentProps.cancel')}
|
||||
</button>
|
||||
<button
|
||||
className="pid-action-button pid-action-delete"
|
||||
onClick={() => {
|
||||
setConfirmingDelete(false);
|
||||
onDelete(componentId);
|
||||
}}
|
||||
title={t('editor.componentProps.confirmDeleteTitle')}
|
||||
>
|
||||
{deleteLabel ?? t('editor.componentProps.delete')}
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* Boards pass no onRotate (they do not rotate); they pass
|
||||
Board Options / Flash as extraActions instead. */}
|
||||
{onRotate && (
|
||||
<button
|
||||
className="pid-action-button"
|
||||
onClick={() => onRotate(componentId)}
|
||||
title={t('editor.componentProps.rotate90')}
|
||||
>
|
||||
{t('editor.componentProps.rotate')}
|
||||
</button>
|
||||
)}
|
||||
{extraActions?.map((a) => (
|
||||
<button
|
||||
key={a.id}
|
||||
className="pid-action-button"
|
||||
onClick={a.disabled ? undefined : a.onSelect}
|
||||
disabled={a.disabled}
|
||||
title={a.title}
|
||||
>
|
||||
{a.label}
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
className="pid-action-button pid-action-delete"
|
||||
onClick={() => setConfirmingDelete(true)}
|
||||
title={t('editor.componentProps.deleteTitle')}
|
||||
>
|
||||
{deleteLabel ?? t('editor.componentProps.delete')}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right column: tabs. */}
|
||||
|
|
@ -684,6 +631,98 @@ export const PartInspectorDialog: React.FC<PartInspectorDialogProps> = ({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pid-actions">
|
||||
{confirmingDelete ? (
|
||||
<>
|
||||
<span className="pid-confirm-label">
|
||||
{t('editor.componentProps.confirmDelete', { name: componentMetadata.name })}
|
||||
{deleteHint && <em className="pid-confirm-hint">{deleteHint}</em>}
|
||||
</span>
|
||||
<button
|
||||
className="pid-action-button"
|
||||
onClick={() => setConfirmingDelete(false)}
|
||||
title={t('editor.componentProps.cancel')}
|
||||
>
|
||||
{t('editor.componentProps.cancel')}
|
||||
</button>
|
||||
<button
|
||||
className="pid-action-button pid-action-delete"
|
||||
onClick={() => {
|
||||
setConfirmingDelete(false);
|
||||
onDelete(componentId);
|
||||
}}
|
||||
title={t('editor.componentProps.confirmDeleteTitle')}
|
||||
>
|
||||
{deleteLabel ?? t('editor.componentProps.delete')}
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* Boards pass no onRotate (they do not rotate); they pass
|
||||
Board Options / Flash as extraActions instead. */}
|
||||
{onRotate && (
|
||||
<button
|
||||
className="pid-action-button"
|
||||
onClick={() => onRotate(componentId)}
|
||||
title={t('editor.componentProps.rotate90')}
|
||||
>
|
||||
{t('editor.componentProps.rotate')}
|
||||
</button>
|
||||
)}
|
||||
{extraActions?.map((a) => (
|
||||
<button
|
||||
key={a.id}
|
||||
className="pid-action-button"
|
||||
onClick={a.disabled ? undefined : a.onSelect}
|
||||
disabled={a.disabled}
|
||||
title={a.title}
|
||||
>
|
||||
{a.label}
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
className="pid-action-button pid-action-delete"
|
||||
onClick={() => setConfirmingDelete(true)}
|
||||
title={t('editor.componentProps.deleteTitle')}
|
||||
>
|
||||
{deleteLabel ?? t('editor.componentProps.delete')}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Buy lives here, right-aligned, so it is reachable from BOTH
|
||||
tabs — it used to sit at the end of the datasheet body, which
|
||||
meant scrolling to the bottom of the right tab to find it. */}
|
||||
{buyHref && (
|
||||
<div className="pid-actions-right">
|
||||
<a
|
||||
className="pid-buy-button"
|
||||
href={buyHref}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title={buyHref}
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle cx="9" cy="21" r="1" />
|
||||
<circle cx="20" cy="21" r="1" />
|
||||
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" />
|
||||
</svg>
|
||||
Buy
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue