% \iffalse meta-comment % Copyright (C) 2026 Andreas Willibald Weber % % This work may be distributed and/or modified under the conditions of the % LaTeX Project Public License, either version 1.3c of this license or (at your % option) any later version. The latest version of the license is available at % https://www.latex-project.org/lppl.txt. % % This work has the LPPL maintenance status `maintained'. The Current % Maintainer of this work is Andreas Willibald Weber. % % This file is part of the `kanban-board' package. The complete list of files % constituting the work appears in the main source file kanban-board.dtx. % \fi % % \begin{macrocode} %<*package> % \end{macrocode} % % \subsection{Card components and renderers} % % Metadata elements are small inline boxes. Their semantic colors are stable % across modes. Only the card surface, card text, and card border respond to % the light or dark board mode. Public namespaced styles are appended after % the structural defaults. Fixed and breakable layouts reuse the same card % renderer but provide different column containers. Rendering is delayed. % Task declarations store calls to these components, and the environment % executes those calls only after it knows all column counts and dimensions. % % \paragraph{Structural and extension styles.} % The style registry distinguishes fixed package structure from supported % user extension points. The empty public styles may be extended with % |\tcbset|. The private styles establish the common geometry of inline pills % and the stable author and deadline colors. Because the public extension key % is applied last in each component definition, a document may override % presentation without replacing structural collection logic. % \begin{macrocode} \tcbset{ kanban/header/.style={}, kanban/card/.style={}, kanban/column-body/.style={}, kanban/priority-chip/.style={}, kanban/author-chip/.style={}, kanban/deadline-chip/.style={}, kanban/completed-chip/.style={}, kanban@pill/.style={on line,boxsep=0pt,left=1.2mm,right=1.2mm, top=.4mm,bottom=.4mm,arc=.7mm,boxrule=0pt,nobeforeafter, box align=base}, kanban@chip/.style={kanban@pill,colback=\kanban@authorback, coltext=\kanban@authortext}, kanban@due/.style={kanban@pill,colback=\kanban@deadlineback, coltext=\kanban@deadlinetext}} % \end{macrocode} % % \paragraph{Priority and author chips.} % A priority chip maps a normalized semantic value to the board-local label, % the semantic background, and the matching foreground. An empty or unknown % value produces no chip. The author component has no semantic % classification and renders any non-blank author text with the stable % author style. % \begin{macrocode} \newcommand\kanban@prioritypill[1]{\ifblank{#1}{}{% \def\kb@pl{}\def\kb@pc{}\def\kb@pt{}% \ifstrequal{#1}{high}{\def\kb@pl{\kanban@boardhighlabel}% \def\kb@pc{kanbanHigh}\def\kb@pt{\kanban@hightext}}{}% \ifstrequal{#1}{medium}{\def\kb@pl{\kanban@boardmediumlabel}% \def\kb@pc{kanbanMed}\def\kb@pt{\kanban@mediumtext}}{}% \ifstrequal{#1}{med}{\def\kb@pl{\kanban@boardmediumlabel}% \def\kb@pc{kanbanMed}\def\kb@pt{\kanban@mediumtext}}{}% \ifstrequal{#1}{low}{\def\kb@pl{\kanban@boardlowlabel}% \def\kb@pc{kanbanLow}\def\kb@pt{\kanban@lowtext}}{}% \ifdefempty\kb@pl{}{\tcbox[kanban@pill, colback=\kb@pc,colframe=\kb@pc,coltext=\kb@pt, kanban/priority-chip]{\bfseries\scriptsize\kb@pl}}}} \newcommand\kanban@authorchip[1]{\ifblank{#1}{}{% \tcbox[kanban@chip,kanban/author-chip]{\scriptsize #1}}} % \end{macrocode} % % \paragraph{The deadline clock symbol.} % Deadline chips include a compact clock drawn with TikZ primitives, so the % package does not depend on an icon font. Its dimensions are fixed in points % and small enough to align with script-sized metadata text. The picture is % self-contained and does not alter later TikZ material. % \begin{macrocode} \newcommand\kb@clock{\tikz[baseline=-.55ex]{% \draw[line width=.3pt] (0,0) circle (1.7pt); \draw[line width=.3pt] (0,0)--(0,1.1pt); \draw[line width=.3pt] (0,0)--(1pt,.2pt);}} % \end{macrocode} % % \paragraph{Deadline and completion chips.} % Deadline rendering first classifies the value and then prepares its % display notation. A task marked |late| or an automatically overdue date % receives the high semantic color. A date equal to the reference date % receives the medium color. Future dates, project labels, and descriptive % text keep the neutral deadline style. Completion is a separate flag % rendered with the low semantic color. These colors are identical in light % and dark board modes. % \begin{macrocode} \newcommand\kanban@deadlinechip[2]{\ifblank{#2}{}{% \kb@evaloverdue{#2}% \kanban@preparedeadline{#2}% \ifboolexpr{test {\ifstrequal{#1}{1}} or bool {kb@overdue}} {\tcbox[kanban@due,colback=kanbanHigh,coltext=\kanban@hightext, kanban/deadline-chip] {\kb@clock\,\scriptsize\bfseries \kb@displaydeadline}} {\ifbool{kb@duenow} {\tcbox[kanban@due,colback=kanbanMed,coltext=\kanban@mediumtext, kanban/deadline-chip] {\kb@clock\,\scriptsize\bfseries \kb@displaydeadline}} {\tcbox[kanban@due,kanban/deadline-chip] {\kb@clock\,\scriptsize \kb@displaydeadline}}}}} \newcommand\kanban@completedchip[1]{\ifstrequal{#1}{1} {\tcbox[kanban@pill,colback=kanbanLow,colframe=kanbanLow, coltext=\kanban@lowtext,kanban/completed-chip] {\bfseries\scriptsize\kanban@boarddonelabel}}{}} % \end{macrocode} % % \paragraph{Priority-value validation.} % Task options must store only priority values understood by the renderer. % The setter normalizes case and surrounding spaces, accepts the four % documented spellings, treats a blank choice as omission, and warns when % non-blank input is unknown. The normalized value is stored with the task. % All definitions occur while a task is collected inside the environment % group, so the temporary value cannot leak to later tasks. The warning is % a named message, so the definition sits in an expl3 syntax region. % % The background setter validates the optional card tint before TeX reaches % \textsf{xcolor}, because an unknown color would otherwise stop the run % while the board is rendered, far from the declaration that caused it. The % accepted grammar is a color name, optionally negated, followed by any % number of |!|\meta{percentage}|!|\meta{name} mixing steps. Every name in % the expression must be known to \textsf{xcolor}. The value is expanded % with |\text_expand:n| first, so an ordinary macro that holds a color name % works and a robust command is rejected as unknown. % \begin{macrocode} \ExplSyntaxOn \cs_new_protected:Npn \kanban@setbackground #1 { \tl_set:Nx \l__kanban_background_tl {\text_expand:n {#1}} \tl_trim_spaces:N \l__kanban_background_tl \cs_set:Npn \kbtask@bg {} \tl_if_blank:VF \l__kanban_background_tl { \bool_set_false:N \l__kanban_background_ok_bool \exp_args:NnV \regex_match:nnT { \A-?[A-Za-z][A-Za-z0-9]* (?:![0-9]{1,3}(?:![A-Za-z][A-Za-z0-9]*)?)*\Z } \l__kanban_background_tl { \bool_set_true:N \l__kanban_background_ok_bool \exp_args:NnV \regex_extract_all:nnN {[A-Za-z][A-Za-z0-9]*} \l__kanban_background_tl \l__kanban_background_seq \seq_map_inline:Nn \l__kanban_background_seq { \@ifundefinedcolor{##1} {\bool_set_false:N \l__kanban_background_ok_bool} {} } } \bool_if:NTF \l__kanban_background_ok_bool {\cs_set:Npx \kbtask@bg {\tl_use:N \l__kanban_background_tl}} { \use:x { \msg_warning:nnn {kanban-board}{unknown-color} {\tl_to_str:N \l__kanban_background_tl} } } } } \newcommand\kanban@setpriority[1]{% \kanban@normalizechoice{#1}% \def\kbtask@prio{}% \ifdefstring{\kb@choice}{high}{\def\kbtask@prio{high}}{% \ifdefstring{\kb@choice}{medium}{\def\kbtask@prio{medium}}{% \ifdefstring{\kb@choice}{med}{\def\kbtask@prio{med}}{% \ifdefstring{\kb@choice}{low}{\def\kbtask@prio{low}}{% \ifdefempty\kb@choice{} {\msg_warning:nnn {kanban-board}{unknown-priority}{#1}}}}}}} \ExplSyntaxOff % \end{macrocode} % % \paragraph{Break-layout headers and shared task cards.} % The page-breakable renderer needs a header box whose height is % synchronized across its independent columns through a board-specific % equal-height group. All renderers need one full-width card whose surface % and text follow the active mode and whose colored west rule identifies the % target column. The card surface is either the plain surface of the mode or % that surface mixed with a tint. The tint is a stored \textsf{xcolor} % expression, so it is first bound to a named color and then mixed with the % mode percentage, which keeps arbitrary expressions valid as a mixing % operand. The shared card composer resolves the surface, places the task % text first, then an optional row with priority, completion, and author, % and finally an optional deadline row. Storing the same composer for both % layouts guarantees identical card semantics. % \begin{macrocode} \newtcolorbox{kanban@header}[2]{enhanced,sharp corners,colback=#1, boxrule=0pt,coltext=#2, fontupper=\bfseries\raggedright, left=2mm,right=2mm, top=1.2mm,bottom=1.2mm,before skip=0pt,after skip=1.8mm, equal height group={KBH@\kanban@boardgroup},valign=center, kanban/header} \newtcolorbox{kanban@card}[2]{enhanced,arc=.6mm,outer arc=.6mm, colback=#2,colframe=\kanban@bordercolor, coltext=\kanban@textcolor,boxrule=.4pt,width=\linewidth, borderline west={2.2pt}{0pt}{#1},left=2.4mm,right=2mm,top=1.6mm, bottom=1.6mm,before skip=0pt,after skip=1.8mm, fontupper=\scriptsize\raggedright,kanban/card} \newcommand\kanban@resolvecardback[1]{% \ifblank{#1} {\let\kb@cardback\kanban@cardcolor} {\colorlet{kb@tintbase}{#1}% \edef\kb@cardback{kb@tintbase!\kanban@cardtint!\kanban@cardcolor}}} \newcommand\kanban@rendercard[8]{% \kanban@resolvecardback{#8}% \begin{kanban@card}{#1}{\kb@cardback}#2% \notblank{#3#4#7}{\par\vspace{1mm}\noindent \kanban@prioritypill{#3}% \notblank{#3}{\notblank{#7}{\hspace{1mm}}{}}{}% \kanban@completedchip{#7}\hfill\kanban@authorchip{#4}}{}% \notblank{#5}{\par\vspace{1mm}\noindent \kanban@deadlinechip{#6}{#5}}{}% \end{kanban@card}} % \end{macrocode} % % \paragraph{Fixed-board containers and renderer adapters.} % The fixed renderer uses a two-row raster. The first row contains % synchronized headings and the second contains equal-height column bodies. % The body hides both frame and interior, so the page shows through without % a transparency group in the PDF, and draws only a bottom rule in a tint of % the column color. The breakable renderer instead places each header and % task list in a separate |paracol| stream. Three adapters insert the stored % column name, count, color, foreground, and deferred card list into the % appropriate container. % \begin{macrocode} \newtcolorbox{kanban@fixedheader}[2]{enhanced,sharp corners, colback=#1,boxrule=0pt,coltext=#2, fontupper=\bfseries\raggedright,boxsep=0pt,left=2mm,right=2mm, top=1mm,bottom=1mm,before skip=0pt,after skip=0pt,valign=center, kanban/header} \newtcolorbox{kanban@fixedbody}[1]{enhanced,sharp corners,frame hidden, interior hidden,boxrule=0pt, borderline south={.6pt}{0pt}{#1!55},boxsep=0pt, left=0pt,right=0pt,top=2mm,bottom=2mm, before skip=0pt,after skip=0pt,valign=top,kanban/column-body} \newcommand\kanban@onehead[1]{% \begin{kanban@fixedheader}{\csuse{kanban@color@#1}}% {\csuse{kanban@headingtext@#1}}% \csuse{kanban@name@#1}\hfill \csuse{kanban@count@\kanban@boardgroup @#1}% \end{kanban@fixedheader}} \newcommand\kanban@onecol[1]{% \begin{kanban@fixedbody}{\csuse{kanban@color@#1}}% \csuse{kanban@list@\kanban@boardgroup @#1}% \end{kanban@fixedbody}} \newcommand\kanban@onecol@b[1]{% \begin{kanban@header}{\csuse{kanban@color@#1}}% {\csuse{kanban@headingtext@#1}}% \csuse{kanban@name@#1}\hfill \csuse{kanban@count@\kanban@boardgroup @#1}% \end{kanban@header}% \csuse{kanban@list@\kanban@boardgroup @#1}\par} % \end{macrocode} % % \begin{macrocode} % % \end{macrocode}