From ae63080d8aa5b779b264c2043df82955bfe38acf Mon Sep 17 00:00:00 2001 From: BattleCh1cken Date: Mon, 20 May 2024 22:34:44 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20Start=20work=20on=20the=20reorg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flake.lock | 6 +- themes/radial/components/toc.typ | 67 +++++++---- utils.typ | 176 +--------------------------- utils/components.typ | 81 +++++++++++++ utils/misc.typ | 189 +++++++++++++++++++++++++++++++ utils/theme.typ | 0 6 files changed, 320 insertions(+), 199 deletions(-) create mode 100644 utils/components.typ create mode 100644 utils/misc.typ create mode 100644 utils/theme.typ diff --git a/flake.lock b/flake.lock index 34cb3f8..2d0e894 100644 --- a/flake.lock +++ b/flake.lock @@ -78,11 +78,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1715653339, - "narHash": "sha256-7lR9tpVXviSccl07GXI0+ve/natd24HAkuy1sQp0OlI=", + "lastModified": 1716190602, + "narHash": "sha256-xYRimrR0duWvokWQEvB87bSsICeCvvX9DxpUOzCfsDE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "abd6d48f8c77bea7dc51beb2adfa6ed3950d2585", + "rev": "5a5ac83292c7842072318f57d68a48474f8bd34d", "type": "github" }, "original": { diff --git a/themes/radial/components/toc.typ b/themes/radial/components/toc.typ index fc12f04..0917b96 100644 --- a/themes/radial/components/toc.typ +++ b/themes/radial/components/toc.typ @@ -2,29 +2,43 @@ #import "../metadata.typ": * #import "./label.typ": * -/// Print out the table of contents -/// -/// Example Usage: -/// -/// ```typ -/// #create-frontmatter-entry(title: "Table of Contents")[ -/// #components.toc() -/// ] -/// ``` -#let toc() = utils.print-toc((frontmatter, body, appendix) => { +#let toc = utils.make-toc(( + _, + body, + appendix, +) => { heading(level: 1)[Entries] - stack(spacing: 1em, ..for entry in body { - ([ - #entry.date.display("[year]/[month]/[day]") - #h(5pt) - #label(entry.type) - #h(5pt) - #entry.title - #box(width: 1fr, line(length: 100%, stroke: (dash: "dotted"))) - #entry.page-number - ],) - }) + stack( + spacing: 1em, + ..for entry in body { + ( + [ + #entry.date.display("[year]/[month]/[day]") + #h(5pt) + #label(entry.type) + #h(5pt) + #entry.title + #box( + width: 1fr, + line( + length: 100%, + stroke: ( + dash: "dotted", + ), + ), + ) + #entry.page-number + ], + ) + }, + ) + + if ( + appendix.len() <= 0 + ) { + return + } linebreak() @@ -32,8 +46,15 @@ for entry in appendix [ #entry.title - #box(width: 1fr, line(length: 100%, stroke: (dash: "dotted"))) + #box( + width: 1fr, + line( + length: 100%, + stroke: ( + dash: "dotted", + ), + ), + ) #entry.page-number - ] }) diff --git a/utils.typ b/utils.typ index 72d1516..a89f33d 100644 --- a/utils.typ +++ b/utils.typ @@ -1,173 +1,3 @@ -#import "/globals.typ" - -// TODO: document what ctx provides to the callback -/// Utility function to help themes implement a table of contents. -/// -/// Example Usage: -/// ```typ -/// #let toc() = utils.print-toc((frontmatter, body, appendix) => { -/// for entry in body [ -/// #entry.title -/// #box(width: 1fr, line(length: 100%, stroke: (dash: "dotted"))) -/// #entry.page-number -/// ] -/// }) -/// ``` -/// - callback (function): A function which takes the #link()[ctx] of all entries as input, and returns the content of the entire table of contents. -/// -> content -#let print-toc(callback) = locate( - loc => { - // Each of the types of entries have their own state variable and label, so we need to decide which ones to use - let helper(type) = { - let (state, markers) = if type == "frontmatter" { - ( - globals.frontmatter-entries, - query(selector(), loc), - ) - } else if type == "body" { - (globals.entries, query(selector(), loc)) - } else if type == "appendix" { - (globals.appendix-entries, query(selector(), loc)) - } else { - panic("No valid entry type selected.") - } - - let result = () - - for (index, entry) in state.final(loc).enumerate() { - let page-number = counter(page).at(markers.at(index).location()).at(0) - let ctx = entry.ctx - ctx.page-number = page-number - result.push(ctx) - } - return result - } - - let frontmatter-entries = helper("frontmatter") - let body-entries = helper("body") - let appendix-entries = helper("appendix") - - callback(frontmatter-entries, body-entries, appendix-entries) - }, -) - -/// A utility function meant to help themes implement a glossary -/// - callback (function): A function that returns the content of the glossary -/// -> content -#let print-glossary(callback) = locate( - loc => { - let sorted-glossary = globals.glossary-entries.final(loc).sorted(key: ((word: word, definition: _)) => word) - callback(sorted-glossary) - }, -) - -/// A utility function that does the calculation for decision matrices for you -/// -/// Example Usage: -/// -/// ```typ -/// #calc-decision-matrix( -/// properties: ( -/// (name: "Versatility", weight: 2), -/// (name: "Flavor", weight: 6), -/// (name: "Crunchiness"), // Defaults to a weight of 1 -/// ), -/// ("Sweet potato", 2, 5, 1), -/// ("Red potato", 2, 1, 3), -/// ("Yellow potato", 2, 2, 3), -/// ) -/// ``` -/// -/// The function returns an array of dictionaries, one for each choice. -/// Here's an example of what one of these dictionaries might look like: -/// -/// ```typ -/// #(name: "Sweet potato", values: ( -/// Versatility: (value: 3, highest: true), -/// Flavor: (value: 1, highest: false), -/// Crunchiness: (value: 1, highest: false), -/// total: (value: 5, highest: false), -/// )), -/// ``` -/// - properties (array string): A list of the properties that each choice will be rated by -/// - ..choices (array): All of the choices that are being rated. The first element of the array should be the name of the -/// -> array -#let calc-decision-matrix(properties: (), ..choices) = { - for choice in choices.pos() { - assert( - choice.len() - 1 == properties.len(), - message: "The number of supplied values did not match the number of properties.", - ) - } - - // This function follows the follow steps to calculate the outcome of a decision matrix: - // 1. Applies all of the weights to each of the values - // 2. Calculates the total for each choice - // 3. Adds all of the values to a dictionary, labeling them with their respective property names - // 4. Iterates over each of the newly organized choices to determine which is the highest for each category (including the total) - let choices = choices.pos().enumerate().map( - ((index, choice)) => { - let name = choice.at(0) - let values = choice.slice(1) - - // 1. Weight the values - values = values.enumerate().map( - ((index, value)) => value * properties.at(index).at("weight", default: 1), - ) - - // 2. Calc total - let total = values.sum() - - // 3. Assign the value names - let result = (:) - for (index, value) in values.enumerate() { - result.insert(properties.at(index).name, (value: value, highest: false)) - } - result.insert("total", (value: total, highest: false)) - - (name: name, values: result) - }, - ) - - // 4. Check if highest - properties.push((name: "total")) // Treat total as a property as well - for property in properties { - let highest = (index: 0, value: 0) // Records the index of the choice which had the highest total - - for (index, choice) in choices.enumerate() { - let property-value = choice.values.at(property.name).value - - if property-value > highest.value { - highest.index = index - highest.value = property-value - } - } - choices.at(highest.index).values.at(property.name).highest = true; - } - - return choices -} - -/// Returns the raw image data, not image content -/// You'll still need to run image.decode on the result -/// -/// - raw-icon (string): The raw data for the image. Must be svg data. -/// - fill (color): The new icon color -/// -> string -#let change-icon-color(raw-icon: "", fill: red) = { - return raw-icon.replace(" content -#let colored-icon(path, fill: red, width: 100%, height: 100%, fit: "contain") = { - let raw-icon = read(path) - let raw-colored-icon = raw-icon.replace(" { +/// for entry in body [ +/// #entry.title +/// #box(width: 1fr, line(length: 100%, stroke: (dash: "dotted"))) +/// #entry.page-number +/// ] +/// }) +/// ``` +/// - callback (function): A function which takes the #link()[ctx] of all entries as input, and returns the content of the entire table of contents. +/// -> content +#let make-toc( + callback, +) = { + let helper( + type, + ) = { + let ( + state, + markers, + ) = if type == "frontmatter" { + ( + globals.frontmatter-entries, + query( + selector(), + ), + ) + } else if type == "body" { + ( + globals.entries, + query( + selector(), + ), + ) + } else if type == "appendix" { + ( + globals.appendix-entries, + query( + selector(), + ), + ) + } else { + panic("No valid entry type selected.") + } + + let result = () + + for ( + index, + entry, + ) in state.final().enumerate() { + let page-number = counter(page).at( + markers.at(index).location(), + ).at(0) + let ctx = entry.ctx + ctx.page-number = page-number + result.push(ctx) + } + return result + } + + + return () => context { + let frontmatter-entries = helper("frontmatter") + let body-entries = helper("body") + let appendix-entries = helper("appendix") + + callback( + frontmatter-entries, + body-entries, + appendix-entries, + ) + } +} + +// Glossary +// Pro / Con +// Decision Matrix diff --git a/utils/misc.typ b/utils/misc.typ new file mode 100644 index 0000000..3dfed93 --- /dev/null +++ b/utils/misc.typ @@ -0,0 +1,189 @@ +#import "/globals.typ" + + + +/// A utility function meant to help themes implement a glossary +/// - callback (function): A function that returns the content of the glossary +/// -> content +#let print-glossary( + callback, +) = locate(loc => { + let sorted-glossary = globals.glossary-entries.final(loc).sorted(key: ( + ( + word: word, + definition: definition, + ), + ) => word) + callback(sorted-glossary) +}) + +/// A utility function that does the calculation for decision matrices for you +/// +/// Example Usage: +/// +/// ```typ +/// #calc-decision-matrix( +/// properties: ( +/// (name: "Versatility", weight: 2), +/// (name: "Flavor", weight: 6), +/// (name: "Crunchiness"), // Defaults to a weight of 1 +/// ), +/// ("Sweet potato", 2, 5, 1), +/// ("Red potato", 2, 1, 3), +/// ("Yellow potato", 2, 2, 3), +/// ) +/// ``` +/// +/// The function returns an array of dictionaries, one for each choice. +/// Here's an example of what one of these dictionaries might look like: +/// +/// ```typ +/// #(name: "Sweet potato", values: ( +/// Versatility: (value: 3, highest: true), +/// Flavor: (value: 1, highest: false), +/// Crunchiness: (value: 1, highest: false), +/// total: (value: 5, highest: false), +/// )), +/// ``` +/// - properties (array string): A list of the properties that each choice will be rated by +/// - ..choices (array): All of the choices that are being rated. The first element of the array should be the name of the +/// -> array +#let calc-decision-matrix( + properties: (), + ..choices, +) = { + for choice in choices.pos() { + assert( + choice.len() - 1 == properties.len(), + message: "The number of supplied values did not match the number of properties.", + ) + } + + // This function follows the follow steps to calculate the outcome of a decision matrix: + // 1. Applies all of the weights to each of the values + // 2. Calculates the total for each choice + // 3. Adds all of the values to a dictionary, labeling them with their respective property names + // 4. Iterates over each of the newly organized choices to determine which is the highest for each category (including the total) + let choices = choices.pos().enumerate().map(( + ( + index, + choice, + ), + ) => { + let name = choice.at(0) + let values = choice.slice(1) + + // 1. Weight the values + values = values.enumerate().map(( + ( + index, + value, + ), + ) => value * properties.at(index).at( + "weight", + default: 1, + )) + + // 2. Calc total + let total = values.sum() + + // 3. Assign the value names + let result = (:) + for ( + index, + value, + ) in values.enumerate() { + result.insert( + properties.at(index).name, + ( + value: value, + highest: false, + ), + ) + } + result.insert( + "total", + ( + value: total, + highest: false, + ), + ) + + ( + name: name, + values: result, + ) + }) + + // 4. Check if highest + properties.push(( + name: "total", + )) + // Treat total as a property as well + for property in properties { + let highest = ( + index: 0, + value: 0, + ) + // Records the index of the choice which had the highest total + + for ( + index, + choice, + ) in choices.enumerate() { + let property-value = choice.values.at(property.name).value + + if property-value > highest.value { + highest.index = index + highest.value = property-value + } + } + choices.at(highest.index).values.at(property.name).highest = true + } + + return choices +} + +/// Returns the raw image data, not image content +/// You'll still need to run image.decode on the result +/// +/// - raw-icon (string): The raw data for the image. Must be svg data. +/// - fill (color): The new icon color +/// -> string +#let change-icon-color( + raw-icon: "", + fill: red, +) = { + return raw-icon.replace( + " content +#let colored-icon( + path, + fill: red, + width: 100%, + height: 100%, + fit: "contain", +) = { + let raw-icon = read(path) + let raw-colored-icon = raw-icon.replace( + "