style(web): полотно доски — карточки, дорожки, даты в стиле системы (#65)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
cec8beecfb
commit
3a711ff2ea
@ -1,5 +1,5 @@
|
||||
/* 3. Закреплённая шапка: заголовок и действия (PageHeader), панель управления (Panel), предупреждения, легенда */
|
||||
.sticky{position:sticky;top:env(safe-area-inset-top,0px);z-index:10;background:var(--bg);padding-block:var(--space-4) var(--space-2);margin:0 0 var(--space-2);box-shadow:0 1px 0 var(--line)}
|
||||
.sticky{position:sticky;top:env(safe-area-inset-top,0px);z-index:10;background:var(--color-bg-canvas);padding-block:var(--space-4) var(--space-2);margin:0 0 var(--space-2);box-shadow:0 1px 0 var(--color-border-default)}
|
||||
.sticky .ui-page-header{margin-bottom:var(--space-3);gap:var(--space-2)}
|
||||
.stats{font-variant-numeric:tabular-nums}
|
||||
.stats b{color:var(--color-text-primary);font-weight:600}
|
||||
@ -27,140 +27,147 @@
|
||||
.legend-head{display:flex;align-items:center;gap:var(--space-2)}
|
||||
.legend{display:flex;flex-wrap:wrap;align-items:center;gap:2px 12px;margin:var(--space-1) 0 0;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--color-text-secondary)}
|
||||
.legend span{display:inline-flex;align-items:center;gap:6px}
|
||||
.legend .sep{width:1px;height:14px;background:var(--line)}
|
||||
.legend .sep{width:1px;height:14px;background:var(--color-border-default)}
|
||||
.legend .hint{margin-left:auto}
|
||||
.lg{font:inherit;font-size:13px;color:var(--muted);background:none;border:0;padding:2px 5px;border-radius:4px;cursor:pointer;display:inline-flex;align-items:center;gap:6px}
|
||||
.lg:hover,.lg:focus-visible{background:var(--soft);color:var(--fg);outline:none}
|
||||
.lg{font:inherit;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--color-text-secondary);background:none;border:1px solid transparent;padding:2px 6px;border-radius:var(--radius-xs);cursor:pointer;display:inline-flex;align-items:center;gap:6px}
|
||||
.lg:hover{background:var(--color-bg-hover);color:var(--color-text-primary)}
|
||||
.lg:focus-visible{background:var(--color-bg-hover);color:var(--color-text-primary);border-color:var(--color-border-selected);outline:none}
|
||||
.lg.off{opacity:.45;text-decoration:line-through}
|
||||
.sw{width:10px;height:10px;border-radius:2px;display:inline-block}
|
||||
.ln{width:26px;height:0;border-top:2px solid var(--edge-strong);display:inline-block}
|
||||
.ln.dash{border-top-style:dashed;border-top-color:var(--edge)}
|
||||
.ln.hand{border-top-color:var(--guide)}
|
||||
.ln.crit{border-top-width:4px;border-top-color:var(--guide)}
|
||||
.out-sw{width:16px;height:11px;border:1.5px dashed var(--muted);border-radius:3px;display:inline-block}
|
||||
.out-sw{width:16px;height:11px;border:1.5px dashed var(--color-text-secondary);border-radius:3px;display:inline-block}
|
||||
|
||||
/* 6. Доска: прокрутка только по горизонтали (вертикальная — у страницы), дорожки по эпикам, карточки абсолютные */
|
||||
.stage{display:flex;gap:10px;align-items:stretch}
|
||||
.wrap{flex:1 1 auto;min-width:0;overflow-x:auto;overflow-y:hidden;border:1px solid var(--line);border-radius:8px;background:var(--card);user-select:none;-webkit-user-select:none}
|
||||
.board{position:relative;min-width:max(100%,max-content)}
|
||||
.stage{display:flex;gap:var(--space-3);align-items:stretch}
|
||||
.wrap{flex:1 1 auto;min-width:0;overflow-x:auto;overflow-y:hidden;border:1px solid var(--color-border-default);border-radius:var(--radius-xl);background:var(--color-bg-surface);user-select:none;-webkit-user-select:none}
|
||||
.board{position:relative;min-width:100%}
|
||||
svg.edges,svg.guides{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
|
||||
svg.guides{z-index:3}
|
||||
svg.guides line{stroke:var(--guide);stroke-width:1;stroke-dasharray:4 3}
|
||||
.empty{padding:40px 16px;color:var(--muted);text-align:center}
|
||||
.empty{padding:40px 16px;color:var(--color-text-secondary);text-align:center}
|
||||
#hubState{white-space:normal;min-width:0}
|
||||
.login{max-width:560px;margin:48px auto;padding:20px 24px;background:var(--panel);border:1px solid var(--line);border-radius:8px}
|
||||
.login h2{margin:0 0 8px;font-size:17px;font-weight:600}
|
||||
.login p{margin:0 0 12px;font-size:14px;color:var(--muted)}
|
||||
.login p.err{color:var(--p-crit)}
|
||||
.login code{font-family:var(--font-mono);font-size:13px;color:var(--fg)}
|
||||
.login{max-width:560px;margin:48px auto;padding:20px 24px;background:var(--color-bg-surface);border:1px solid var(--color-border-default);border-radius:var(--radius-xl)}
|
||||
.login h2{margin:0 0 8px;font-size:var(--fs-lg);line-height:var(--lh-lg);font-weight:700}
|
||||
.login p{margin:0 0 12px;font-size:var(--fs-body);color:var(--color-text-secondary)}
|
||||
.login p.err{color:var(--color-status-error)}
|
||||
.login code{font-family:var(--font-mono);font-size:13px;color:var(--color-text-primary)}
|
||||
.login form{display:flex;gap:8px;margin:0 0 12px}
|
||||
.login input{flex:1;min-width:0;font:inherit;font-size:14px;height:34px;box-sizing:border-box;padding:0 10px;color:var(--fg);background:var(--bg);border:1px solid var(--line);border-radius:6px}
|
||||
.login input:focus-visible{border-color:var(--accent);outline:none}
|
||||
.lane{position:absolute;left:0;right:0;border-top:1px solid var(--line)}
|
||||
.lane:nth-child(even of .lane){background:color-mix(in srgb,var(--soft) 45%,transparent)}
|
||||
.lane.folded{background:color-mix(in srgb,var(--soft) 80%,transparent)}
|
||||
.lane.target{box-shadow:inset 0 3px 0 var(--accent)}
|
||||
.lane.target-after{box-shadow:inset 0 -3px 0 var(--accent)}
|
||||
.login input{flex:1;min-width:0;font:inherit;font-size:var(--fs-body);height:var(--control-h);box-sizing:border-box;padding:0 10px;color:var(--color-text-primary);background:var(--color-bg-canvas);border:1px solid var(--color-border-default);border-radius:var(--radius-sm)}
|
||||
.login input:focus-visible{border-color:var(--color-border-selected);outline:none}
|
||||
.lane{position:absolute;left:0;right:0;border-top:1px solid var(--color-border-default)}
|
||||
.lane:nth-child(even of .lane){background:var(--lane-alt)}
|
||||
.lane.folded{background:var(--color-bg-hover)}
|
||||
.lane.target{box-shadow:inset 0 3px 0 var(--color-action-primary)}
|
||||
.lane.target-after{box-shadow:inset 0 -3px 0 var(--color-action-primary)}
|
||||
.lane.dragging{opacity:.6}
|
||||
.lane-head{position:absolute;left:8px;top:8px;display:flex;align-items:center;gap:4px;z-index:1}
|
||||
.fold{width:18px;height:18px;border:0;background:none;color:var(--muted);cursor:pointer;padding:0;border-radius:4px;display:inline-flex;align-items:center;justify-content:center;font-size:0}
|
||||
.fold::before{content:"";border:5px solid transparent;border-top:6px solid currentColor;border-bottom:0;transition:transform .15s}
|
||||
.fold:hover{background:var(--soft);color:var(--fg)}
|
||||
.lane.folded .fold::before{transform:rotate(-90deg)}
|
||||
.lane-name{font-size:12.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);font-weight:500;cursor:grab;padding:2px 4px;border-radius:4px;user-select:none;touch-action:none}
|
||||
.lane-name:hover{background:var(--soft);color:var(--fg)}
|
||||
.lane-count{display:none;font-size:12.5px;color:var(--muted);margin-left:4px}
|
||||
.fold{width:20px;height:20px;border:0;background:none;color:var(--color-text-secondary);cursor:pointer;padding:0;border-radius:var(--radius-xs);display:inline-flex;align-items:center;justify-content:center}
|
||||
.fold svg{transition:transform .15s}
|
||||
.fold:hover{background:var(--color-bg-hover);color:var(--color-text-primary)}
|
||||
.fold:focus-visible,.lane-tools button:focus-visible,.card .open-card:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:1px}
|
||||
.lane.folded .fold svg{transform:rotate(-90deg)}
|
||||
.lane-name{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--color-text-secondary);font-weight:600;cursor:grab;padding:2px 4px;border-radius:var(--radius-xs);user-select:none;touch-action:none}
|
||||
.lane-name:hover{background:var(--color-bg-hover);color:var(--color-text-primary)}
|
||||
.lane-count{display:none;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--color-text-secondary);margin-left:4px}
|
||||
.lane.folded .lane-count{display:inline}
|
||||
.lane-tools{display:none;gap:2px}
|
||||
.lane:hover .lane-tools{display:inline-flex}
|
||||
.lane-tools button{font:inherit;font-size:12px;line-height:1;color:var(--muted);background:var(--card);border:1px solid var(--line);border-radius:4px;width:22px;height:22px;cursor:pointer;padding:0}
|
||||
.lane-tools button:hover{border-color:var(--accent);color:var(--accent)}
|
||||
.lane-tools button{display:inline-flex;align-items:center;justify-content:center;color:var(--color-text-secondary);background:var(--color-bg-surface);border:1px solid var(--color-border-strong);border-radius:var(--radius-xs);width:22px;height:22px;cursor:pointer;padding:0}
|
||||
.lane-tools button:hover{border-color:var(--color-action-primary);color:var(--color-action-primary)}
|
||||
.lane-tools button:disabled{opacity:.35;cursor:default}
|
||||
.lane-tools .up svg{transform:rotate(180deg)}
|
||||
/* режим «по датам»: колонки по плановым датам окончания, шапка колонки над дорожками; карточки двигаются внутри своей колонки */
|
||||
.dcol{position:absolute;top:0;border-left:1px solid var(--line);box-sizing:border-box;pointer-events:none}
|
||||
.dcol{position:absolute;top:0;border-left:1px solid var(--color-border-default);box-sizing:border-box;pointer-events:none}
|
||||
.dcol:first-of-type{border-left:0}
|
||||
.dcol.past{background:color-mix(in srgb,var(--p-crit) 5%,transparent)}
|
||||
.dcol.today{background:color-mix(in srgb,var(--accent) 7%,transparent)}
|
||||
.dcol.today{background:color-mix(in srgb,var(--color-action-primary) 7%,transparent)}
|
||||
/* полоса дат над доской: липнет под шапкой страницы, по горизонтали двигается вместе с доской (transform по scrollLeft) */
|
||||
.dates-bar{position:sticky;top:calc(var(--head-h,0px) + env(safe-area-inset-top,0px));z-index:9;background:var(--bg)}
|
||||
.dates-in{overflow:hidden;border:1px solid var(--line);border-bottom:0;border-radius:8px 8px 0 0;background:var(--card);height:36px;box-sizing:border-box}
|
||||
.dates-bar{position:sticky;top:calc(var(--head-h,0px) + env(safe-area-inset-top,0px));z-index:9;background:var(--color-bg-canvas)}
|
||||
.dates-in{overflow:hidden;border:1px solid var(--color-border-default);border-bottom:0;border-radius:var(--radius-xl) var(--radius-xl) 0 0;background:var(--color-bg-surface);height:36px;box-sizing:border-box}
|
||||
.dates-track{position:relative;height:100%}
|
||||
.dlabel{position:absolute;top:9px;font-size:12.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--fg);font-weight:600;white-space:nowrap}
|
||||
.dlabel .n{font-weight:400;color:var(--muted);margin-left:6px;font-variant-numeric:tabular-nums}
|
||||
.dlabel{position:absolute;top:10px;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--color-text-primary);font-weight:600;white-space:nowrap}
|
||||
.dlabel .n{font-weight:400;color:var(--color-text-secondary);margin-left:6px;font-variant-numeric:tabular-nums}
|
||||
.dlabel.past{color:var(--p-crit)}
|
||||
.dlabel.today{color:var(--accent)}
|
||||
.dlabel.none{color:var(--muted);font-weight:500}
|
||||
.dlabel.today{color:var(--color-action-primary)}
|
||||
.dlabel.none{color:var(--color-text-secondary);font-weight:400}
|
||||
.stage.bydate .wrap{border-top-left-radius:0;border-top-right-radius:0}
|
||||
.dsplit{position:absolute;top:0;bottom:0;width:11px;margin-left:-5px;cursor:col-resize;z-index:1}
|
||||
.dsplit::after{content:"";position:absolute;left:5px;top:6px;bottom:6px;width:1px;background:var(--line)}
|
||||
.dsplit:hover::after,.dsplit.on::after{background:var(--accent);width:3px;left:4px;border-radius:2px}
|
||||
.dsplit::after{content:"";position:absolute;left:5px;top:6px;bottom:6px;width:1px;background:var(--color-border-default)}
|
||||
.dsplit:hover::after,.dsplit.on::after{background:var(--color-action-primary);width:3px;left:4px;border-radius:2px}
|
||||
.dates-in.resizing{cursor:col-resize}
|
||||
/* горизонтальная прокрутка доски, прилипшая к низу окна: полоса всегда видна, скроллбар нарисован явно (на macOS системный прячется) */
|
||||
.hscroll{position:sticky;bottom:0;z-index:8;overflow-x:scroll;overflow-y:hidden;height:14px;background:var(--bg);padding-top:2px;box-sizing:border-box;scrollbar-width:thin;scrollbar-color:var(--edge) var(--soft)}
|
||||
.hscroll{position:sticky;bottom:0;z-index:8;overflow-x:scroll;overflow-y:hidden;height:14px;background:var(--color-bg-canvas);padding-top:2px;box-sizing:border-box;scrollbar-width:thin;scrollbar-color:var(--color-border-strong) var(--color-bg-track)}
|
||||
.hscroll>div{height:1px}
|
||||
.hscroll::-webkit-scrollbar{height:12px}
|
||||
.hscroll::-webkit-scrollbar-track{background:var(--soft);border-radius:6px}
|
||||
.hscroll::-webkit-scrollbar-thumb{background:var(--edge);border-radius:6px;border:2px solid var(--soft)}
|
||||
.hscroll::-webkit-scrollbar-thumb:hover{background:var(--accent)}
|
||||
.hscroll::-webkit-scrollbar-track{background:var(--color-bg-track);border-radius:6px}
|
||||
.hscroll::-webkit-scrollbar-thumb{background:var(--color-border-strong);border-radius:6px;border:2px solid var(--color-bg-track)}
|
||||
.hscroll::-webkit-scrollbar-thumb:hover{background:var(--color-action-primary)}
|
||||
/* карточка: текст выделяется, тянуть можно только за рамку (появляется при наведении) и за уголок */
|
||||
.card{position:absolute;width:240px;height:120px;box-sizing:border-box;background:var(--card);border:1px solid var(--line);border-radius:6px;padding:12px 14px;display:flex;flex-direction:column;gap:4px;overflow:hidden;cursor:default;touch-action:none}
|
||||
.card:hover{border-color:var(--accent)}
|
||||
.card{position:absolute;width:240px;height:120px;box-sizing:border-box;background:var(--color-bg-surface);border:1px solid var(--color-border-default);border-radius:var(--radius-lg);padding:12px;display:flex;flex-direction:column;gap:4px;overflow:hidden;cursor:default;touch-action:none}
|
||||
.card:hover{border-color:var(--color-border-strong)}
|
||||
.card.out{border-style:dashed;opacity:.7}
|
||||
.card.closed{opacity:.4}
|
||||
.card.closed:hover,.card.closed:focus-within{opacity:1}
|
||||
.edges path.closed{opacity:.4}
|
||||
.card.crit{box-shadow:0 0 0 2px var(--guide)}
|
||||
.card.focus{box-shadow:0 0 0 2px var(--accent)}
|
||||
.card.drag{cursor:grabbing;border-color:var(--accent);box-shadow:var(--shadow-popover);z-index:2}
|
||||
.card.focus{box-shadow:0 0 0 2px var(--color-action-primary)}
|
||||
.card.drag{cursor:grabbing;border-color:var(--color-action-primary);box-shadow:var(--shadow-popover);z-index:2}
|
||||
.card .title,.card .state,.card .who,.card .meta,.card .key,.card .sprint-tag{user-select:text;-webkit-user-select:text;cursor:text;touch-action:auto}
|
||||
.card .key{cursor:pointer}
|
||||
.card .meta{cursor:default}
|
||||
.card .grip{position:absolute;background:var(--accent);opacity:0;cursor:grab;z-index:1;transition:opacity .12s}
|
||||
.card .grip.t{left:0;right:0;top:0;height:8px;border-radius:5px 5px 0 0}
|
||||
.card .grip.b{left:0;right:14px;bottom:0;height:8px;border-radius:0 0 0 5px}
|
||||
.card .grip.l{left:0;top:0;bottom:0;width:8px;border-radius:5px 0 0 5px}
|
||||
.card .grip.r{right:0;top:0;bottom:14px;width:8px;border-radius:0 5px 0 0}
|
||||
.card:hover .grip,.card.drag .grip{opacity:.85}
|
||||
.card .grip{position:absolute;background:var(--color-action-primary);opacity:0;cursor:grab;z-index:1;transition:opacity .12s}
|
||||
.card .grip.t{left:0;right:0;top:0;height:8px;border-radius:11px 11px 0 0}
|
||||
.card .grip.b{left:0;right:14px;bottom:0;height:8px;border-radius:0 0 0 11px}
|
||||
.card .grip.l{left:0;top:0;bottom:0;width:8px;border-radius:11px 0 0 11px}
|
||||
.card .grip.r{right:0;top:0;bottom:14px;width:8px;border-radius:0 11px 0 0}
|
||||
.card:hover .grip,.card.drag .grip{opacity:.6}
|
||||
.card.drag .grip{cursor:grabbing}
|
||||
.card .rsz{position:absolute;right:0;bottom:0;width:14px;height:14px;cursor:nwse-resize;background:linear-gradient(135deg,transparent 50%,var(--line) 50%);border-bottom-right-radius:5px;z-index:2}
|
||||
.card:hover .rsz{background:linear-gradient(135deg,transparent 50%,var(--accent) 50%)}
|
||||
.card .rsz{position:absolute;right:0;bottom:0;width:14px;height:14px;cursor:nwse-resize;background:linear-gradient(135deg,transparent 50%,var(--color-border-strong) 50%);border-bottom-right-radius:11px;z-index:2}
|
||||
.card:hover .rsz{background:linear-gradient(135deg,transparent 50%,var(--color-action-primary) 50%)}
|
||||
.head{display:flex;align-items:center;justify-content:space-between;gap:6px;min-width:0}
|
||||
.key{font-family:var(--font-mono);font-size:13.5px;font-weight:500;color:var(--accent);text-decoration:none;flex:0 0 auto}
|
||||
.sprint-tag{font-size:11px;line-height:1;padding:4px 7px;border-radius:10px;background:var(--soft);color:var(--muted);max-width:55%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:0 1 auto}
|
||||
.sprint-tag.sel{background:var(--accent);color:var(--accent-fg)}
|
||||
.sprint-tag.none{background:transparent;border:1px dashed var(--line)}
|
||||
.title{font-size:14.5px;line-height:19px;min-width:0;flex:1 1 auto;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}
|
||||
.meta{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--muted);white-space:nowrap;flex:0 0 auto}
|
||||
.key{font-family:var(--font-mono);font-size:13px;font-weight:600;color:var(--color-action-primary);text-decoration:none;flex:0 0 auto}
|
||||
.sprint-tag{font-size:11px;line-height:1;padding:4px 7px;border-radius:var(--radius-round);background:var(--color-bg-track);color:var(--color-text-secondary);max-width:55%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:0 1 auto}
|
||||
.sprint-tag.sel{background:var(--color-action-primary);color:var(--color-text-on-primary)}
|
||||
.sprint-tag.none{background:transparent;border:1px dashed var(--color-border-strong)}
|
||||
.title{font-size:14.5px;line-height:19px;font-weight:600;color:var(--color-text-primary);min-width:0;flex:1 1 auto;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}
|
||||
.meta{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--color-text-secondary);white-space:nowrap;flex:0 0 auto}
|
||||
.kind{display:inline-flex;align-items:center;gap:4px}
|
||||
.kind i{width:8px;height:8px;border-radius:2px;display:inline-block}
|
||||
.prio{font-weight:600}
|
||||
/* состояние задачи (статус, срок, исполнитель) — отдельный блок под чертой, основным цветом: это главное после названия */
|
||||
.state,.who{font-size:13px;flex:0 0 auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--fg)}
|
||||
.state{display:flex;align-items:center;gap:6px;margin-top:4px;padding-top:7px;border-top:1px solid var(--line)}
|
||||
.status{font-size:12.5px;line-height:1;padding:4px 7px;border-radius:10px;background:var(--soft);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.state,.who{font-size:13px;flex:0 0 auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--color-text-primary)}
|
||||
.state{display:flex;align-items:center;gap:6px;margin-top:4px;padding-top:7px;border-top:1px solid var(--color-border-default)}
|
||||
/* статус — вид Badge (neutral) */
|
||||
.status{font-size:var(--fs-caption);line-height:var(--lh-caption);font-weight:600;padding:1px 8px;border:1px solid var(--color-border-default);border-radius:var(--radius-round);background:var(--color-bg-track);color:var(--color-text-secondary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.due{font-variant-numeric:tabular-nums;font-weight:600;flex:0 0 auto}
|
||||
.who::before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--accent);margin-right:5px;vertical-align:1px}
|
||||
.who.none{color:var(--muted);font-style:italic}
|
||||
.who.none::before{background:transparent;border:1px solid var(--muted)}
|
||||
.who::before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--color-action-primary);margin-right:5px;vertical-align:1px}
|
||||
.who.none{color:var(--color-text-secondary);font-style:italic}
|
||||
.who.none::before{background:transparent;border:1px solid var(--color-text-secondary)}
|
||||
|
||||
/* 7. Панель проблем справа от доски, липнет под шапкой и прокручивается внутри себя */
|
||||
.drawer{flex:0 0 340px;max-width:40%;overflow:auto;box-sizing:border-box;align-self:flex-start;position:sticky;top:calc(var(--head-h,0px) + 8px);max-height:calc(100vh - var(--head-h,0px) - 24px);font-size:var(--fs-body)}
|
||||
.drawer-head{display:flex;justify-content:space-between;align-items:center;gap:var(--space-2)}
|
||||
.board-h2{margin:0;font-size:var(--fs-lg);line-height:var(--lh-lg);font-weight:600;color:var(--color-text-primary)}
|
||||
/* 7. Панель проблем справа от доски, липнет под шапкой; прокручивается содержимое внутри рамки Panel */
|
||||
.drawer{flex:0 0 340px;max-width:40%;box-sizing:border-box;align-self:flex-start;position:sticky;top:calc(var(--head-h,0px) + 8px);max-height:calc(100vh - var(--head-h,0px) - 24px);display:flex;font-size:var(--fs-body)}
|
||||
.drawer>.ui-panel{display:flex;flex-direction:column;gap:var(--space-3);min-height:0;min-width:0;flex:1 1 auto;padding:var(--space-4) 0 var(--space-4) var(--space-4)}
|
||||
.drawer-head{display:flex;justify-content:space-between;align-items:center;gap:var(--space-2);padding-right:var(--space-4)}
|
||||
.drawer-body{overflow-y:auto;min-height:0;padding-right:var(--space-4)}
|
||||
.board-h2{margin:0;font-size:var(--fs-lg);line-height:var(--lh-lg);font-weight:700;color:var(--color-text-primary)}
|
||||
.board-h3{margin:var(--space-3) 0 var(--space-1);font-size:var(--fs-caption);line-height:var(--lh-caption);font-weight:600;color:var(--color-text-secondary)}
|
||||
.board-h3:first-child{margin-top:0}
|
||||
.drawer ul{margin:0;padding-left:18px}
|
||||
.drawer li{margin:3px 0}
|
||||
.drawer .k{font-family:var(--font-mono);font-size:13px;color:var(--color-action-primary);text-decoration:none}
|
||||
.drawer .ok{color:var(--color-status-success);margin:var(--space-2) 0 0}
|
||||
.drawer .why{color:var(--muted)}
|
||||
.drawer .k{font-family:var(--font-mono);font-size:13px;font-weight:600;color:var(--color-action-primary);text-decoration:none}
|
||||
.drawer .ok{color:var(--color-status-success);margin:0}
|
||||
.drawer .why{color:var(--color-text-secondary)}
|
||||
|
||||
/* 8. Экспорт JSON и таблица связей (включаются из меню «Ещё») */
|
||||
.export{display:flex;flex-direction:column;gap:var(--space-2);margin:0 0 var(--space-2)}
|
||||
.export textarea{font-family:var(--font-mono);font-size:11.5px;width:100%;box-sizing:border-box;height:180px;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--card);color:var(--fg);padding:8px;resize:vertical}
|
||||
.export textarea{font-family:var(--font-mono);font-size:11.5px;width:100%;box-sizing:border-box;height:180px;border:1px solid var(--color-border-default);border-radius:var(--radius-sm);background:var(--color-bg-surface);color:var(--color-text-primary);padding:8px;resize:vertical}
|
||||
.link-table{margin-top:var(--space-3)}
|
||||
.tbl{max-width:100%;overflow-x:auto;margin-top:var(--space-2)}
|
||||
table{border-collapse:collapse;font-size:var(--fs-body);min-width:520px}
|
||||
th,td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--line);vertical-align:top}
|
||||
th,td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--color-border-default);vertical-align:top}
|
||||
th{font-size:var(--fs-caption);font-weight:600;color:var(--color-text-secondary)}
|
||||
td.k{font-family:var(--font-mono);font-size:12px;white-space:nowrap;color:var(--color-action-primary)}
|
||||
|
||||
@ -171,8 +178,8 @@ td.k{font-family:var(--font-mono);font-size:12px;white-space:nowrap;color:var(--
|
||||
@media (max-width:640px){.sticky{position:static}.dates-bar{position:static}}
|
||||
|
||||
/* «Открыть карточку» в заголовке карточки (R-1) */
|
||||
.card .open-card{margin-left:6px;color:var(--muted);text-decoration:none;font-size:13px}
|
||||
.card .open-card:hover,.card .open-card:focus-visible{color:var(--accent);outline:none}
|
||||
.card .open-card{margin-left:6px;color:var(--color-text-secondary);text-decoration:none;display:inline-flex;align-items:center;border-radius:var(--radius-xs)}
|
||||
.card .open-card:hover{color:var(--color-action-primary)}
|
||||
/* слой измерения: карточки шириной CW с высотой по содержимому, невидим */
|
||||
.measure{position:absolute;left:-100000px;top:0;visibility:hidden;pointer-events:none}
|
||||
.measure .card{position:static;height:auto}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { memo, type PointerEvent } from "react";
|
||||
import { Link } from "react-router";
|
||||
import { IconOpen } from "../../ui/icons.tsx";
|
||||
import { BOARD_CARD_STATE } from "../../issues/modal.ts";
|
||||
import { CW, JIRA_URL, KIND, PRIO } from "../constants.ts";
|
||||
import { lineClamp, type Rect, type RelBox } from "../layout/layout.ts";
|
||||
@ -34,7 +35,7 @@ export const Card = memo(function Card({ node: n, box, rel, multiSprint, sprintS
|
||||
<div className="head">
|
||||
<a className="key" href={`${JIRA_URL}/browse/${n.key}`} target="_blank" rel="noopener">{n.key}</a>
|
||||
{!n.stub && <Link className="open-card" to={`/issues/${n.key}`} state={BOARD_CARD_STATE} title="Открыть карточку задачи"
|
||||
aria-label={`Открыть карточку ${n.key}`}>ⓘ</Link>}
|
||||
aria-label={`Открыть карточку ${n.key}`}><IconOpen size={16} /></Link>}
|
||||
{showTag && (n.sprint
|
||||
? <span className={"sprint-tag" + (sprintSel.has(n.sprint) ? " sel" : "")} title={n.sprint}>{n.sprint.replace(/^AWPCA_/, "")}</span>
|
||||
: <span className="sprint-tag none">без спринта</span>)}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useRef, useState, type PointerEvent } from "react";
|
||||
import { IconChevronDown } from "../../ui/icons.tsx";
|
||||
import type { LaneGeom } from "../layout/layout.ts";
|
||||
import { plural } from "../model/util.ts";
|
||||
|
||||
@ -39,12 +40,12 @@ export function Lane({ g, index, last, count, lanes, boardTop, targetClass, onTo
|
||||
return (
|
||||
<div className={cls} style={{ top: g.y, height: g.h }}>
|
||||
<div className="lane-head">
|
||||
<button type="button" className="fold" title="Свернуть или развернуть дорожку" onClick={() => onToggle(g.name)} />
|
||||
<button type="button" className="fold" title="Свернуть или развернуть дорожку" onClick={() => onToggle(g.name)}><IconChevronDown size={16} /></button>
|
||||
<span ref={nameRef} className="lane-name" title="Перетащить, чтобы поменять дорожки местами" onPointerDown={down}>{g.name}</span>
|
||||
<span className="lane-count">{plural(count, "задача", "задачи", "задач")}</span>
|
||||
<span className="lane-tools">
|
||||
<button type="button" title="Выше" disabled={index === 0} onClick={() => onMove(g.name, index - 1)}>▲</button>
|
||||
<button type="button" title="Ниже" disabled={index === last} onClick={() => onMove(g.name, index + 1)}>▼</button>
|
||||
<button type="button" className="up" title="Выше" disabled={index === 0} onClick={() => onMove(g.name, index - 1)}><IconChevronDown size={16} /></button>
|
||||
<button type="button" title="Ниже" disabled={index === last} onClick={() => onMove(g.name, index + 1)}><IconChevronDown size={16} /></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -20,7 +20,7 @@ export function ProblemsDrawer({ problems, onClose }: { problems: Problems; onCl
|
||||
<aside className="drawer">
|
||||
<Panel>
|
||||
<div className="drawer-head"><h2 className="board-h2">Проблемы на доске</h2><Button variant="ghost" onClick={onClose}>Закрыть</Button></div>
|
||||
<div>
|
||||
<div className="drawer-body">
|
||||
{!problemCount(problems) && <div className="ok">По датам, исполнителям и статусам блокеров на видимой части доски проблем нет.</div>}
|
||||
<Section title="Срок блокера не раньше срока зависимой задачи" list={problems.dates} />
|
||||
<Section title="Блокер без исполнителя" list={problems.unassigned} />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user