%% ********************************************************************* %% Copyright 2024 by M.Y. XIA * %% * %% This work may be distributed and/or modified under the * %% conditions of the LaTeX Project Public License * %% * %% http://www.latex-project.org/lppl.txt * %% * %% either version 1.3c of this license or any later version. * %% * %% This work has the LPPL maintenance status `maintained'. * %% * %% The Current Maintainers of this work are M.Y. XIA & L.J. Guo * %% * %% This work consists of the files litetable.cls, * %% and README.md. * %% available at https://github.com/xiamyphys/litetable * %% ********************************************************************* \RequirePackage{xparse}% For TeX Live 2019 - 2020 Compatibility \ProvidesExplClass {litetable} {2024/10/13} {3.1D} {Course Schedule} \DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}} \ProcessOptions\relax \LoadClass{article} \pagestyle{empty} \RequirePackage{tikz} % Module for texlive 2021 and later (by @ljguo) \cs_if_exist:NF \clist_put_right:Ne { \cs_generate_variant:Nn \clist_put_right:Nn { Ne } } \cs_if_exist:NF \clist_set:Ne { \cs_generate_variant:Nn \clist_set:Nn { Ne } } \cs_if_exist:NT \clist_count:e { \cs_generate_variant:Nn \clist_count:n { e } } \cs_if_exist:NF \exp_args:NNNe { \cs_new:Npn \exp_args:NNNe #1#2#3#4 { \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN #3 \tex_expanded:D { {#4} } } } % Module for left -> right data structure (by @ljguo) \cs_new_protected_nopar:Npn \__litetable_get_left:nN #1#2 { \group_begin: \seq_set_split:Nnn \l__litetable_tmpa_seq { -> } {#1} \exp_args:NNNe \group_end: \tl_set:Nn #2 { \seq_item:Nn \l__litetable_tmpa_seq { 1 } } } \cs_new_protected_nopar:Npn \__litetable_get_right:nN #1#2 { \group_begin: \seq_set_split:Nnn \l__litetable_tmpa_seq { -> } {#1} \exp_args:NNNe \group_end: \tl_set:Nn #2 { \seq_item:Nn \l__litetable_tmpa_seq { 2 } } } \cs_generate_variant:Nn \__litetable_get_left:nN { e } \cs_generate_variant:Nn \__litetable_get_right:nN { e } \int_new:N \l__time_num_int \dim_new:N \l__time_vunit_dim \cs_new_protected:Npn \litetable_msg_new:nn #1#2 { \msg_new:nnn { litetable } {#1} {#2} } \cs_new_protected:Npn \litetable_msg_warning:n #1 { \msg_warning:nn { litetable } {#1} } \litetable_msg_new:nn { timelist } { \exp_not:N \timelist~extra~time~group(s)~were~ignored } \cs_new_protected_nopar:Npn \litetable_timelist:nn #1#2 { \clist_set:Nn \l__litetable_timelist_clist {#2} \tl_if_empty:nTF {#1} { \int_set:Nn \l__time_num_int { \clist_count:N \l__litetable_timelist_clist } } { \int_set:Nn \l__time_num_int {#1} \int_compare:nNnTF {#1} < { \clist_count:N \l__litetable_timelist_clist } { \int_set:Nn \l__time_num_int { \clist_count:N \l__litetable_timelist_clist } \litetable_msg_warning:n { timelist } } { \int_set:Nn \l__time_num_int {#1} } } \dim_set:Nn \l__time_vunit_dim { \fp_eval:n { 1/( 2\l__time_num_int + 3.5 ) } \paperheight } } \NewDocumentCommand \timelist { O{} m O{} } { \litetable_timelist:nn { #1 #3 } {#2} } \clist_new:N \l__week_ratio_clist \clist_new:N \l__week_accum_clist \int_new:N \l__week_num_int \dim_new:N \l__week_hunit_dim \cs_new_protected_nopar:Npn \litetable_weeklist:nn #1#2 { \tl_set:Nn \l__default_weeks_tl {#1} \clist_set:Nn \l__litetable_weeklist_clist {#2} \int_step_inline:nn { \clist_count:N \l__litetable_weeklist_clist } { \__litetable_get_right:eN { \clist_item:Nn \l__litetable_weeklist_clist {##1} } \l__litetable_tmpb_tl \clist_put_right:Ne \l__week_ratio_clist { \l__litetable_tmpb_tl } } \int_step_inline:nn { \clist_count:N \l__litetable_weeklist_clist } { \clist_clear:N \l__week_accumtmp_clist \int_step_inline:nn {##1} { \clist_put_right:Ne \l__week_accumtmp_clist { \clist_item:Nn \l__week_ratio_clist {####1} } } \clist_put_right:Ne \l__week_accum_clist { \int_eval:n { \clist_use:Nn \l__week_accumtmp_clist { + } } } } \int_set:Nn \l__week_num_int { \clist_item:Nn \l__week_accum_clist { \clist_count:N \l__litetable_weeklist_clist } } \dim_set:Nn \l__week_hunit_dim { \fp_eval:n { 1/\l__week_num_int * 14/15 } \paperwidth } } \NewDocumentCommand \weeklist { O{} m O{} } { \litetable_weeklist:nn { #1 #3 } {#2} } \NewDocumentCommand \more { m } { \tl_set:Nn \l__litetable_comment_tl {#1} } \int_new:N \l__litetable_week_day_int \int_set:Nn \l__litetable_week_day_int { 1 } \NewDocumentCommand \newday { O{1} } { \int_add:Nn \l__litetable_week_day_int {#1} } \keys_define:nn { litetable / frame } { sem.tl_set:N = \l__bg_sem_tl, color.tl_set:N = \l__bg_color_tl, color.initial:n = gray } \cs_new_protected_nopar:Npn \litetable_maketable:nn #1 { \fill [ \l__bg_color_tl!5 ] (current~page.north~west) rectangle + (\paperwidth, -1.5\l__time_vunit_dim) node [ midway, black, font = \huge\bfseries ] {#1}; \tl_if_empty:NF { \l__bg_sem_tl } { \node [ shift = {(-.02\paperwidth, -.75\l__time_vunit_dim)}, left, rectangle, fill = DarkBlue!10, text = DarkBlue!60, inner~sep = 2ex, rounded~corners = 8pt, font = \large ] at (current~page.north~east) { \ensuremath\rightleftharpoons\ \l__bg_sem_tl }; } % Darker Blocks \int_step_inline:nnnn { 0 } { 2 } { \l__time_num_int } { \filldraw [ fill = \l__bg_color_tl!5, draw = gray, thick, densely~dashed, line~cap = round ] ([shift = {(-.4pt, \fp_eval:n { - 2 * ##1 - 2.5 } \l__time_vunit_dim)}] current~page.north~west ) rectangle + (\paperwidth + .8pt, -2\l__time_vunit_dim); } % Classes numbering \clist_if_empty:NTF { \l__litetable_timelist_clist } { \int_step_inline:nn { \l__time_num_int } { \node [ shift = {( \paperwidth/30, \fp_eval:n { - 2 * ##1 - 1.5 } \l__time_vunit_dim )}, darkgray!80, font = \large\ttfamily\bfseries ] at (current~page.north~west) {##1}; } } { \int_step_inline:nn { \l__time_num_int } { \node [ shift = {( \paperwidth/30, \fp_eval:n { - 2 * ##1 - 1 } \l__time_vunit_dim )}, darkgray!80, font = \large\ttfamily\bfseries ] at (current~page.north~west) {##1}; } % Classes time \int_step_inline:nn { \clist_count:N \l__litetable_timelist_clist } { \__litetable_get_left:eN { \clist_item:Nn \l__litetable_timelist_clist {##1} } \l__litetable_tmpa_tl \__litetable_get_right:eN { \clist_item:Nn \l__litetable_timelist_clist {##1} } \l__litetable_tmpb_tl \node [ shift = {( \paperwidth/30, \fp_eval:n { - 1.9 - 2 * ##1 } \l__time_vunit_dim )}, gray, font = \ttfamily, align = center ] at (current~page.north~west) { \l__litetable_tmpa_tl\\\l__litetable_tmpb_tl }; } } % Weekdays \int_step_inline:nn { \clist_count:N \l__litetable_weeklist_clist } { \__litetable_get_left:eN { \clist_item:Nn \l__litetable_weeklist_clist {##1} } \l__litetable_tmpa_tl \node [ shift = {(\fp_eval:n {( \clist_item:Nn \l__week_accum_clist {##1} - \clist_item:Nn \l__week_ratio_clist {##1}/2 )/\l__week_num_int * 14/15 + 1/15 } \paperwidth, -2\l__time_vunit_dim )}, font = \large\bfseries ] at (current~page.north~west) { \l__litetable_tmpa_tl }; } % Comment \tl_if_empty:NF \l__litetable_comment_tl { \node [ yshift = .5\l__time_vunit_dim, left = 1ex, darkgray, font = \small\bfseries ] at (current~page.south~east) { \l__litetable_comment_tl }; } \tl_gclear:N \l__litetable_comment_tl } \NewDocumentCommand \maketable { O{} m O{} } { \group_begin: \keys_set:nn { litetable / frame } { #1 #3 } \litetable_maketable:nn {#2} \group_end: } \keys_define:nn { litetable / course } { color.tl_set:N = \l__course_color_tl, color.initial:n = teal, subject.tl_set:N = \l__course_subject_tl, teacher.tl_set:N = \l__course_teacher_tl, location.tl_set:N = \l__course_location_tl, weeks.tl_set:N = \l__default_weeks_tl, weeks.initial:n = \l__default_weeks_tl, } \dim_new:N \l__course_infoshift_dim \cs_new_protected_nopar:Npn \__litetable_course_box_aux:nn #1#2 { % Course box fg \begin{scope} \clip [ preaction = { draw, ultra~thick, \l__course_color_tl!60 }, preaction = { fill, \l__course_color_tl!10 }, rounded~corners = 8pt ] ([shift = {( \fp_eval:n { \clist_item:Nn \l__week_accum_clist { \l__litetable_week_day_int } - \clist_item:Nn \l__week_ratio_clist { \l__litetable_week_day_int } } \l__week_hunit_dim + \paperwidth/15 + 1.2pt, \fp_eval:n { - .5 - 2 * #1 } \l__time_vunit_dim - 1.2pt )} ]current~page.north~west) rectangle + ( \clist_item:Nn \l__week_ratio_clist { \l__litetable_week_day_int } \l__week_hunit_dim - 2.4pt, \fp_eval:n { 2 * ( #1 - #2 - 1 ) } \l__time_vunit_dim + 2.4pt ); % Course box bg \fill [ \l__course_color_tl!60 ] ([shift = {( \fp_eval:n { \clist_item:Nn \l__week_accum_clist { \l__litetable_week_day_int } - \clist_item:Nn \l__week_ratio_clist { \l__litetable_week_day_int } } \l__week_hunit_dim + \paperwidth/15, \fp_eval:n { - .5 - 2 * #1 } \l__time_vunit_dim )} ]current~page.north~west) rectangle + ( \clist_item:Nn \l__week_ratio_clist { \l__litetable_week_day_int } \l__week_hunit_dim, -\l__time_vunit_dim/2 ); \end{scope} % Course info \tl_if_eq:NNTF {#1} {#2}% Single-unit height course box { \bool_if:nTF { \tl_if_empty_p:N \l__course_location_tl && \tl_if_empty_p:N \l__course_teacher_tl } { \tl_set:Nn \l_shortcourse_anchor_tl { } } { \tl_set:Nn \l_shortcourse_anchor_tl { above } } \cs_if_exist:NT { \l__course_subject_tl } { \node [ shift = {( \fp_eval:n { \clist_item:Nn \l__week_accum_clist { \l__litetable_week_day_int } - \clist_item:Nn \l__week_ratio_clist { \l__litetable_week_day_int }/2 } \l__week_hunit_dim + \paperwidth/15, \fp_eval:n { - 1.75 - #1 - #2 } \l__time_vunit_dim )}, align = center, font = \large\bfseries, \l_shortcourse_anchor_tl, \l__course_color_tl!60 ] at (current~page.north~west) { \l__course_subject_tl }; } \bool_if:nTF { !\tl_if_empty_p:N \l__course_location_tl && !\tl_if_empty_p:N \l__course_teacher_tl } { \tl_set:Nn \l_shortcourse_sep_tl { ,~ } } { \tl_set:Nn \l_shortcourse_sep_tl { } } \node [ shift = {( \fp_eval:n { \clist_item:Nn \l__week_accum_clist { \l__litetable_week_day_int } - \clist_item:Nn \l__week_ratio_clist { \l__litetable_week_day_int }/2 } \l__week_hunit_dim + \paperwidth/15, \fp_eval:n { - 1.75 - #1 - #2 } \l__time_vunit_dim )}, below, \l__course_color_tl!60, align = center ] at (current~page.north~west) { \tl_if_exist:NT { \l__course_location_tl } { \l__course_location_tl } \l_shortcourse_sep_tl \tl_if_exist:NT { \l__course_teacher_tl } { \l__course_teacher_tl } }; } { \bool_if:nTF% Multiply-unit height course box { \tl_if_empty_p:N \l__course_location_tl && \tl_if_empty_p:N \l__course_teacher_tl } { \tl_set:Nn \l_course_anchor_tl { } \dim_set:Nn \l__course_infoshift_dim { 0pt } } { \tl_set:Nn \l_course_anchor_tl { above } \dim_set:Nn \l__course_infoshift_dim { \l__time_vunit_dim/8 } } \cs_if_exist:NT { \l__course_subject_tl } { \node [ shift = {( \fp_eval:n { \clist_item:Nn \l__week_accum_clist { \l__litetable_week_day_int } - \clist_item:Nn \l__week_ratio_clist { \l__litetable_week_day_int }/2 } \l__week_hunit_dim + \paperwidth/15, \fp_eval:n { - 1.5 - #1 - #2 } \l__time_vunit_dim + \l__course_infoshift_dim )}, font = \large\bfseries, \l_course_anchor_tl, \l__course_color_tl!60, align = center ] at (current~page.north~west) { \l__course_subject_tl }; } \bool_if:nTF { !\tl_if_empty_p:N \l__course_location_tl && !\tl_if_empty_p:N \l__course_teacher_tl } { \tl_set:Nn \l_courseinfo_sep_tl { \\ } } { \tl_set:Nn \l_courseinfo_sep_tl { } } \node [ shift = {( \fp_eval:n { \clist_item:Nn \l__week_accum_clist { \l__litetable_week_day_int } - \clist_item:Nn \l__week_ratio_clist { \l__litetable_week_day_int }/2 } \l__week_hunit_dim + \paperwidth/15, \fp_eval:n { - 1.625 - #1 - #2 } \l__time_vunit_dim )}, below, \l__course_color_tl!60, align = center ] at (current~page.north~west) { \tl_if_exist:NT { \l__course_location_tl } { \l__course_location_tl } \l_courseinfo_sep_tl \tl_if_exist:NT { \l__course_teacher_tl } { \l__course_teacher_tl } }; \node [ shift = {(\clist_item:Nn \l__week_accum_clist { \l__litetable_week_day_int } \l__week_hunit_dim + \paperwidth/15, \fp_eval:n { - 2.5 - 2 * #2 } \l__time_vunit_dim )}, above~left, \l__course_color_tl!60, outer~sep = .5ex ] at (current~page.north~west) { \l__default_weeks_tl }; } } \litetable_msg_new:nn { course } { \exp_not:N \course~box(s)~exceed~workdays~were~ignored } \NewDocumentCommand \course { O{} m O{} m O{} } { \group_begin: \int_compare:nNnTF { \l__litetable_week_day_int - 1 } < { \clist_count:N \l__litetable_weeklist_clist } { \keys_set:nn { litetable / course } { #1 #3 #5 } \__litetable_course_box_aux:nn {#2} {#4} } { \litetable_msg_warning:n { course } } \group_end: } \endinput % End of file litetable.cls