feat: standardize card library as treegrid widget
Check card layouts / check (push) Has been cancelled
Check card layouts / check (push) Has been cancelled
This commit is contained in:
+239
-130
@@ -4594,6 +4594,87 @@ function SidePanelDock({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CardLibraryTreeKind = "subject" | "level" | "series" | "card" | "task";
|
||||||
|
|
||||||
|
function CardLibraryTreeRow({
|
||||||
|
id,
|
||||||
|
depth,
|
||||||
|
kind,
|
||||||
|
title,
|
||||||
|
label,
|
||||||
|
refValue,
|
||||||
|
stateValue,
|
||||||
|
stateKind,
|
||||||
|
hasChildren = false,
|
||||||
|
expanded = false,
|
||||||
|
onToggle,
|
||||||
|
href,
|
||||||
|
current = false,
|
||||||
|
unavailable = false,
|
||||||
|
onNavigate,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
depth: number;
|
||||||
|
kind: CardLibraryTreeKind;
|
||||||
|
title: string;
|
||||||
|
label: React.ReactNode;
|
||||||
|
refValue: React.ReactNode;
|
||||||
|
stateValue: React.ReactNode;
|
||||||
|
stateKind?: string;
|
||||||
|
hasChildren?: boolean;
|
||||||
|
expanded?: boolean;
|
||||||
|
onToggle?: () => void;
|
||||||
|
href?: string;
|
||||||
|
current?: boolean;
|
||||||
|
unavailable?: boolean;
|
||||||
|
onNavigate?: () => void;
|
||||||
|
}) {
|
||||||
|
const nodeLabel = href ? (
|
||||||
|
<a
|
||||||
|
className="card-library-tree-link"
|
||||||
|
href={href}
|
||||||
|
onClick={onNavigate}
|
||||||
|
aria-current={current ? "page" : undefined}
|
||||||
|
title={current ? "Pokaż bieżącą kartę" : `Otwórz: ${title}`}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<span className="card-library-tree-label" title={title}>{label}</span>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`card-library-treegrid-row is-${kind}${current ? " is-current" : ""}${unavailable ? " is-unavailable" : ""}`}
|
||||||
|
role="row"
|
||||||
|
aria-level={depth + 1}
|
||||||
|
aria-expanded={hasChildren ? expanded : undefined}
|
||||||
|
aria-disabled={unavailable || undefined}
|
||||||
|
data-node-id={id}
|
||||||
|
style={{ "--card-library-depth": depth } as React.CSSProperties}
|
||||||
|
>
|
||||||
|
<span className="card-library-tree-title" role="gridcell">
|
||||||
|
{hasChildren ? (
|
||||||
|
<button
|
||||||
|
className="card-library-tree-expander"
|
||||||
|
type="button"
|
||||||
|
onClick={onToggle}
|
||||||
|
aria-label={`${expanded ? "Zwiń" : "Rozwiń"}: ${title}`}
|
||||||
|
>
|
||||||
|
{expanded ? "▾" : "▸"}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<span className="card-library-tree-expander is-empty" aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
{nodeLabel}
|
||||||
|
</span>
|
||||||
|
<span role="gridcell">{refValue}</span>
|
||||||
|
<span role="gridcell">
|
||||||
|
<b className="card-library-tree-state" data-state={stateKind}>{stateValue}</b>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function CardLibraryPanel({
|
function CardLibraryPanel({
|
||||||
library,
|
library,
|
||||||
onNavigate,
|
onNavigate,
|
||||||
@@ -4626,141 +4707,169 @@ function CardLibraryPanel({
|
|||||||
),
|
),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
const [expandedNodes, setExpandedNodes] = useState<Set<string>>(() => {
|
||||||
|
const initial = new Set<string>();
|
||||||
|
library.subjects.forEach(subject => {
|
||||||
|
const subjectKey = `subject:${subject.id}`;
|
||||||
|
if (subject.current) initial.add(subjectKey);
|
||||||
|
subject.levels.forEach(level => {
|
||||||
|
const levelKey = `${subjectKey}:level:${level.id}`;
|
||||||
|
if (level.current) initial.add(levelKey);
|
||||||
|
level.series.forEach(series => {
|
||||||
|
const seriesKey = `${levelKey}:series:${series.id}`;
|
||||||
|
if (series.current) initial.add(seriesKey);
|
||||||
|
series.cards.forEach(card => {
|
||||||
|
if (card.current && (card.tasks?.length ?? 0) > 0) {
|
||||||
|
initial.add(`${seriesKey}:card:${card.id}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return initial;
|
||||||
|
});
|
||||||
|
const isExpanded = (id: string) => expandedNodes.has(id);
|
||||||
|
const toggle = (id: string) => {
|
||||||
|
setExpandedNodes(current => {
|
||||||
|
const next = new Set(current);
|
||||||
|
if (next.has(id)) next.delete(id);
|
||||||
|
else next.add(id);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="card-library-panel">
|
<div className="card-library-panel">
|
||||||
<header className="card-library-header">
|
<div
|
||||||
<div>
|
className="card-library-treegrid"
|
||||||
<small>{library.workspace || "STEM"} · KATALOG</small>
|
role="treegrid"
|
||||||
<h2 id="card-library-title">Serie i karty</h2>
|
aria-label="Serie, karty i taski"
|
||||||
<p>{seriesCount} serii · {availableCount}/{cardCount} kart online</p>
|
aria-colcount={3}
|
||||||
|
>
|
||||||
|
<div className="card-library-treegrid-row card-library-treegrid-head" role="row">
|
||||||
|
<span role="columnheader">DRZEWO</span>
|
||||||
|
<span role="columnheader">REF</span>
|
||||||
|
<span role="columnheader">STATUS</span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
{library.subjects.map(subject => {
|
||||||
<nav className="card-library-tree" aria-label="Drzewo serii i kart">
|
const subjectKey = `subject:${subject.id}`;
|
||||||
{library.subjects.map(subject => (
|
return (
|
||||||
<details
|
<React.Fragment key={subjectKey}>
|
||||||
className="card-library-subject"
|
<CardLibraryTreeRow
|
||||||
key={subject.id}
|
id={subjectKey}
|
||||||
open={subject.current || undefined}
|
depth={0}
|
||||||
>
|
kind="subject"
|
||||||
<summary>
|
title={subject.title}
|
||||||
<span className="card-library-branch" aria-hidden="true">▾</span>
|
label={<strong>{subject.title}</strong>}
|
||||||
<span>
|
refValue="PRZEDMIOT"
|
||||||
<strong>{subject.title}</strong>
|
stateValue={`${subject.levels.length} POZ.`}
|
||||||
<small>PRZEDMIOT</small>
|
hasChildren={subject.levels.length > 0}
|
||||||
</span>
|
expanded={isExpanded(subjectKey)}
|
||||||
<output>{subject.levels.length}</output>
|
onToggle={() => toggle(subjectKey)}
|
||||||
</summary>
|
/>
|
||||||
<div className="card-library-level-list">
|
{isExpanded(subjectKey) && subject.levels.map(level => {
|
||||||
{subject.levels.map(level => (
|
const levelKey = `${subjectKey}:level:${level.id}`;
|
||||||
<details
|
return (
|
||||||
className="card-library-level"
|
<React.Fragment key={levelKey}>
|
||||||
key={level.id}
|
<CardLibraryTreeRow
|
||||||
open={level.current || undefined}
|
id={levelKey}
|
||||||
>
|
depth={1}
|
||||||
<summary>
|
kind="level"
|
||||||
<span className="card-library-branch" aria-hidden="true">▾</span>
|
title={level.title}
|
||||||
<strong>{level.title}</strong>
|
label={<strong>{level.title}</strong>}
|
||||||
<output>{level.series.length}</output>
|
refValue="ROK"
|
||||||
</summary>
|
stateValue={`${level.series.length} SERII`}
|
||||||
<div className="card-library-series-list">
|
hasChildren={level.series.length > 0}
|
||||||
{level.series.map(series => (
|
expanded={isExpanded(levelKey)}
|
||||||
<details
|
onToggle={() => toggle(levelKey)}
|
||||||
className="card-library-series"
|
/>
|
||||||
key={`${level.id}:${series.id}`}
|
{isExpanded(levelKey) && level.series.map(series => {
|
||||||
open={series.current || undefined}
|
const seriesKey = `${levelKey}:series:${series.id}`;
|
||||||
>
|
return (
|
||||||
<summary>
|
<React.Fragment key={seriesKey}>
|
||||||
<span className="card-library-branch" aria-hidden="true">▾</span>
|
<CardLibraryTreeRow
|
||||||
<span>
|
id={seriesKey}
|
||||||
<strong>{series.title}</strong>
|
depth={2}
|
||||||
{series.role === "reference" && <small>REFERENCJA</small>}
|
kind="series"
|
||||||
</span>
|
title={series.title}
|
||||||
<output>{series.cards.length}</output>
|
label={<strong>{series.title}</strong>}
|
||||||
</summary>
|
refValue={series.role === "reference" ? "REF." : "SERIA"}
|
||||||
{series.cards.length > 0 && (
|
stateValue={`${series.cards.length} KART`}
|
||||||
<ul className="card-library-cards">
|
hasChildren={series.cards.length > 0}
|
||||||
{series.cards.map((card, cardIndex) => {
|
expanded={isExpanded(seriesKey)}
|
||||||
const tasks = card.tasks ?? [];
|
onToggle={() => toggle(seriesKey)}
|
||||||
const body = (
|
/>
|
||||||
<>
|
{isExpanded(seriesKey) && series.cards.map(card => {
|
||||||
<span className="card-library-node" aria-hidden="true">
|
const cardKey = `${seriesKey}:card:${card.id}`;
|
||||||
{cardIndex === series.cards.length - 1 ? "└─" : "├─"}
|
const tasks = card.tasks ?? [];
|
||||||
</span>
|
const available = card.current || card.available;
|
||||||
<span className="card-library-card-copy">
|
const state = card.current ? "current" : card.available ? "online" : "plan";
|
||||||
<strong>{card.id}</strong>
|
return (
|
||||||
<span>{card.title}</span>
|
<React.Fragment key={cardKey}>
|
||||||
</span>
|
<CardLibraryTreeRow
|
||||||
<span className="card-library-card-meta">
|
id={cardKey}
|
||||||
{card.version && <small>{card.version}</small>}
|
depth={3}
|
||||||
<small>{card.current ? "BIEŻĄCA" : card.available ? "ONLINE" : "PLAN"}</small>
|
kind="card"
|
||||||
</span>
|
title={`${card.id} · ${card.title}`}
|
||||||
</>
|
label={(
|
||||||
);
|
<>
|
||||||
return (
|
<strong>{card.id}</strong>
|
||||||
<li className="card-library-card-entry" key={`${series.id}:${card.id}`}>
|
<span>{card.title}</span>
|
||||||
{card.current || card.available ? (
|
</>
|
||||||
<a
|
|
||||||
className={`card-library-card is-available${card.current ? " is-current" : ""}`}
|
|
||||||
href={card.current ? "#" : card.href}
|
|
||||||
onClick={onNavigate}
|
|
||||||
aria-current={card.current ? "page" : undefined}
|
|
||||||
title={card.current ? "Pokaż bieżącą kartę" : `Otwórz kartę ${card.id}`}
|
|
||||||
>
|
|
||||||
{body}
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
<span
|
|
||||||
className="card-library-card is-unavailable"
|
|
||||||
aria-disabled="true"
|
|
||||||
title="Karta nie ma jeszcze wygenerowanego adresu"
|
|
||||||
>
|
|
||||||
{body}
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
{tasks.length > 0 && (
|
refValue={card.version || "—"}
|
||||||
<ul className="card-library-tasks">
|
stateValue={card.current ? "BIEŻĄCA" : card.available ? "ONLINE" : "PLAN"}
|
||||||
{tasks.map((task, taskIndex) => {
|
stateKind={state}
|
||||||
const taskBody = (
|
hasChildren={tasks.length > 0}
|
||||||
<>
|
expanded={isExpanded(cardKey)}
|
||||||
<span className="card-library-node" aria-hidden="true">
|
onToggle={() => toggle(cardKey)}
|
||||||
{taskIndex === tasks.length - 1 ? "└─" : "├─"}
|
href={available ? (card.current ? "#" : card.href) : undefined}
|
||||||
</span>
|
current={card.current}
|
||||||
<strong>{task.id.toUpperCase()}</strong>
|
unavailable={!available}
|
||||||
<span>{task.title}</span>
|
onNavigate={onNavigate}
|
||||||
</>
|
/>
|
||||||
);
|
{isExpanded(cardKey) && tasks.map(task => {
|
||||||
const taskHref = card.current
|
const taskHref = card.current
|
||||||
? `#${task.id}`
|
? `#${task.id}`
|
||||||
: `${card.href}#${task.id}`;
|
: `${card.href}#${task.id}`;
|
||||||
return (
|
return (
|
||||||
<li key={`${card.id}:${task.id}`}>
|
<CardLibraryTreeRow
|
||||||
{card.current || card.available ? (
|
key={`${cardKey}:task:${task.id}`}
|
||||||
<a href={taskHref} onClick={onNavigate}>{taskBody}</a>
|
id={`${cardKey}:task:${task.id}`}
|
||||||
) : (
|
depth={4}
|
||||||
<span>{taskBody}</span>
|
kind="task"
|
||||||
)}
|
title={`${task.id.toUpperCase()} · ${task.title}`}
|
||||||
</li>
|
label={(
|
||||||
);
|
<>
|
||||||
})}
|
<strong>{task.id.toUpperCase()}</strong>
|
||||||
</ul>
|
<span>{task.title}</span>
|
||||||
)}
|
</>
|
||||||
</li>
|
)}
|
||||||
);
|
refValue="TASK"
|
||||||
})}
|
stateValue={available ? "GOTOWY" : "PLAN"}
|
||||||
</ul>
|
stateKind={available ? "online" : "plan"}
|
||||||
)}
|
href={available ? taskHref : undefined}
|
||||||
</details>
|
unavailable={!available}
|
||||||
))}
|
onNavigate={onNavigate}
|
||||||
</div>
|
/>
|
||||||
</details>
|
);
|
||||||
))}
|
})}
|
||||||
</div>
|
</React.Fragment>
|
||||||
</details>
|
);
|
||||||
))}
|
})}
|
||||||
</nav>
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
<footer className="card-library-footer">
|
<footer className="card-library-footer">
|
||||||
<span><i className="is-online" /> online</span>
|
<span>{library.workspace || "STEM"}</span>
|
||||||
<span><i className="is-draft" /> plan / brak strony</span>
|
<span>{seriesCount} serii</span>
|
||||||
|
<span><i className="is-online" /> {availableCount}/{cardCount} online</span>
|
||||||
<kbd>Esc</kbd>
|
<kbd>Esc</kbd>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -679,6 +679,178 @@
|
|||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
font: 600 16px/1 ui-monospace, monospace;
|
font: 600 16px/1 ui-monospace, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shared tree-widget presentation: the library uses the same tabular grammar
|
||||||
|
as the classroom tree (tree column, fixed metadata columns, sticky head). */
|
||||||
|
.card-library-treegrid {
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
margin: 8px;
|
||||||
|
border: 1px solid rgb(31 35 40 / 18%);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #fff;
|
||||||
|
scrollbar-color: #93a2a8 #edf1f2;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row {
|
||||||
|
display: grid;
|
||||||
|
min-width: 760px;
|
||||||
|
min-height: 38px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
grid-template-columns: minmax(500px, 1fr) 110px 120px;
|
||||||
|
align-items: stretch;
|
||||||
|
border-bottom: 1px solid rgb(31 35 40 / 9%);
|
||||||
|
color: #273941;
|
||||||
|
font: 500 16px/1.25 ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row > [role="gridcell"],
|
||||||
|
.card-library-treegrid-row > [role="columnheader"] {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 7px 9px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row > [role="gridcell"] + [role="gridcell"],
|
||||||
|
.card-library-treegrid-row > [role="columnheader"] + [role="columnheader"] {
|
||||||
|
border-left: 1px solid rgb(31 35 40 / 9%);
|
||||||
|
}
|
||||||
|
.card-library-treegrid-head {
|
||||||
|
position: sticky;
|
||||||
|
z-index: 3;
|
||||||
|
top: 0;
|
||||||
|
min-height: 34px;
|
||||||
|
border-bottom-color: rgb(31 35 40 / 20%);
|
||||||
|
background: #eef1f2;
|
||||||
|
color: #3e525b;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: .045em;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row:not(.card-library-treegrid-head):hover {
|
||||||
|
background: rgb(31 35 40 / 4%);
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row.is-subject {
|
||||||
|
background: #dfe8e5;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row.is-level {
|
||||||
|
background: #f5f8f8;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row.is-series {
|
||||||
|
background: #f9fbfb;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row.is-card {
|
||||||
|
min-height: 42px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row.is-task {
|
||||||
|
background: #fbfcfc;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row.is-current {
|
||||||
|
box-shadow: inset 3px 0 #1680a5;
|
||||||
|
background: #e1f1f6;
|
||||||
|
}
|
||||||
|
.card-library-tree-title {
|
||||||
|
gap: 6px;
|
||||||
|
padding-left: calc(8px + (var(--card-library-depth, 0) * 21px)) !important;
|
||||||
|
font-family: system-ui, sans-serif;
|
||||||
|
}
|
||||||
|
.card-library-tree-expander {
|
||||||
|
display: inline-grid;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
flex: none;
|
||||||
|
place-items: center;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: #405961;
|
||||||
|
padding: 0;
|
||||||
|
font: 18px/1 ui-monospace, monospace;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.card-library-tree-expander:hover,
|
||||||
|
.card-library-tree-expander:focus-visible {
|
||||||
|
background: rgb(22 93 107 / 10%);
|
||||||
|
color: #165d6b;
|
||||||
|
outline: 1px solid rgb(22 93 107 / 28%);
|
||||||
|
}
|
||||||
|
.card-library-tree-expander.is-empty {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.card-library-tree-link,
|
||||||
|
.card-library-tree-label {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 7px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #10181c;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.card-library-tree-link:hover,
|
||||||
|
.card-library-tree-link:focus-visible {
|
||||||
|
color: #075d7c;
|
||||||
|
outline: none;
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
}
|
||||||
|
.card-library-tree-link strong,
|
||||||
|
.card-library-tree-label strong {
|
||||||
|
flex: none;
|
||||||
|
color: inherit;
|
||||||
|
font: 700 16px/1.25 ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
.card-library-tree-link > span,
|
||||||
|
.card-library-tree-label > span {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
color: inherit;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
font: 500 16px/1.25 system-ui, sans-serif;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row.is-subject .card-library-tree-label strong,
|
||||||
|
.card-library-treegrid-row.is-level .card-library-tree-label strong,
|
||||||
|
.card-library-treegrid-row.is-series .card-library-tree-label strong {
|
||||||
|
font-family: system-ui, sans-serif;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
.card-library-tree-state {
|
||||||
|
display: inline-flex;
|
||||||
|
min-width: 76px;
|
||||||
|
min-height: 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #33474f;
|
||||||
|
padding: 2px 7px;
|
||||||
|
font: 650 14px/1 ui-monospace, monospace;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.card-library-tree-state[data-state="current"] {
|
||||||
|
border-color: #3184a0;
|
||||||
|
background: #e9f5f8;
|
||||||
|
color: #075d7c;
|
||||||
|
}
|
||||||
|
.card-library-tree-state[data-state="online"] {
|
||||||
|
border-color: #4b9a7a;
|
||||||
|
background: #edf8f2;
|
||||||
|
color: #176144;
|
||||||
|
}
|
||||||
|
.card-library-tree-state[data-state="plan"] {
|
||||||
|
border-color: #c59a49;
|
||||||
|
background: #fff7e5;
|
||||||
|
color: #775313;
|
||||||
|
}
|
||||||
|
.card-library-treegrid-row.is-unavailable .card-library-tree-label,
|
||||||
|
.card-library-treegrid-row.is-unavailable .card-library-tree-label strong {
|
||||||
|
color: #10181c;
|
||||||
|
}
|
||||||
.side-toc,
|
.side-toc,
|
||||||
.side-progress {
|
.side-progress {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+9
-9
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user