Skip to content

Commit

Permalink
docs: write docs for most modules
Browse files Browse the repository at this point in the history
Signed-off-by: Guillaume Hivert <[email protected]>
  • Loading branch information
ghivert committed Apr 5, 2024
1 parent 0bcf847 commit 2d1356f
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 21 deletions.
3 changes: 1 addition & 2 deletions e2e/stylesheet_render/src/stylesheet_render.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ pub type Msg {

pub fn main() {
let assert Ok(render) =
craft_options.default()
|> craft_options.node()
craft_options.node()
|> craft.setup()

let assert Ok(_) =
Expand Down
22 changes: 20 additions & 2 deletions src/cache.ffi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ import { StyleSheet } from './stylesheet.ffi.mjs'

export let cache

/**
* The cache maintains a structure similar to a VDOM, but for CSS. It tries to
* works on the more optimized way, by using two different cache, to store the
* current state, and the old state, and apply a diff correctly.
* The way it works is to first call the `prepare` function, and then to add
* add style by using the `store` function. To avoid doing the hard work twice,
* the function `persist` will try to get the style in the old cache, and
* transfer it to the new cache, to make sure it will not be destroyed in the
* diff.
* It uses a virtual StyleSheet, in which it's possible to insert or delete rules
* and allow to build the real stylesheet once the diff is made.
*/
export class Cache {
#memoCache
#activeCache
Expand All @@ -21,6 +33,9 @@ export class Cache {
this.#activeCache = new Map()
}

// Compute the predefined classes C = (Keys(Old) ∩ Keys(New))
// Remove the keys defined by Keys(Old) - C.
// Insert the keys defined by Keys(New) - C.
diff() {
const keys = new Set()
for (const key of this.#activeCache.keys()) keys.add(key)
Expand Down Expand Up @@ -84,7 +99,9 @@ export class Cache {
}

// Insert the styles in the stylesheet.
// It inserts medias, selectors and index rules.
// It inserts medias, selectors and index rules. It inserts first the rule,
// then the selectors, and then the media, to respect the usual order in a
// standard CSS sheet, and to respect precedence of the styles.
#insertStyles(klass) {
const indexRules = []
const { definitions } = klass
Expand All @@ -107,7 +124,8 @@ export function createCache(options) {
return cache
}

export function prepareCache(cache) {
export function prepareCache(cache_) {
cache = cache_
cache.prepare()
}

Expand Down
8 changes: 8 additions & 0 deletions src/craft.ffi.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { cache } from './cache.ffi.mjs'
import * as helpers from './helpers.ffi.mjs'

// The Style data structure being a recursive data, computeProperties traverse
// the data structure and collect the properties with their context.
function computeProperties(rawProperties, indent = 2) {
const properties = rawProperties.toArray()
const init = { properties: [], medias: [], classes: [], pseudoSelectors: [], indent }
Expand Down Expand Up @@ -39,6 +41,8 @@ function computeProperties(rawProperties, indent = 2) {
}, init)
}

// Compute classes by using the class definitions, and by wrapping them in the
// correct class declarations, to be CSS compliant.
function computeClasses(id, computedProperties) {
const { properties, medias, classes, pseudoSelectors } = computedProperties
const classDef = helpers.wrapClass(id, properties, 0)
Expand Down Expand Up @@ -68,11 +72,15 @@ export function compileClass(styles, classId) {
return { name, className }
}

// Memoize the class definitions in the cache.
// Once memoized, it's impossible to un-memoize a class.
export function memo(klass) {
cache.memoize(klass)
return klass
}

// Extract the name of the Class type, which is an opaque type for
// the type { name: string, className: string }
export function toString({ name }) {
return name
}
13 changes: 11 additions & 2 deletions src/craft/media.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
//// Defines media queries directly with functions.
//// Define media queries directly with functions.
//// Refer to the craft module to get more details on the usage.
////
//// ## Advanced usage
////
//// Media queries can be rather complex, and the module tries to give all
//// features in a usable way. A media query takes form (property: value) and
//// can be combined, like (orientation: landscape or min-width: 1000px).
//// Those media queries can be created by using the corresponding `and`,
//// `or` or `not` functions.

import craft/size.{type Size, to_string as to_str}
import gleam/string
Expand Down Expand Up @@ -79,7 +88,7 @@ fn q_to_str(query: Query) {
}
}

/// Mainly internal function, can be used if you need to go from a media query to a String
/// Internal function, can be used if you need to go from a media query to a String
/// in case you're building on top of craft.
pub fn to_string(query: Query) {
let content = q_to_str(query)
Expand Down
19 changes: 11 additions & 8 deletions src/craft/options.gleam
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
//// Defines Options to setup the runtime of craft.
//// The Options could right now be a `style` node injected in the DOM, or on
//// a CSSStyleSheet, adopted directly on the document.

/// Internal use.
pub opaque type StyleSheet {
Node
Browser
Expand All @@ -7,18 +12,16 @@ pub opaque type Options {
Options(stylesheet: StyleSheet)
}

pub fn default() {
Options(Node)
}

pub fn node(options: Options) -> Options {
Options(..options, stylesheet: Node)
pub fn node() -> Options {
Options(stylesheet: Node)
}

pub fn browser(options: Options) -> Options {
Options(..options, stylesheet: Browser)
pub fn browser() -> Options {
Options(stylesheet: Browser)
}

/// Internal function, can be used if you need to go from a StyleSheet to a String
/// in case you're building on top of craft. Used in FFI at the moment.
pub fn stylesheet_to_string(stylesheet: StyleSheet) -> String {
case stylesheet {
Node -> "node"
Expand Down
6 changes: 5 additions & 1 deletion src/craft/size.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import gleam/int
import gleam/float
import gleam/string

/// Size defines a CSS Unit. It can be either `px`, `pt`, `vh`, `vw`, `em`,
/// `rem`, `lh`, `rlh`, `ch`, `%`. To instanciate a Size, use the corresponding
/// functions. Every unit exposes two functions: the Int function (like `px(0)`)
/// and the Float version suffixed by an underscore (like `px_(0.0)`).
pub opaque type Size {
Px(Float)
Pt(Float)
Expand Down Expand Up @@ -98,7 +102,7 @@ pub fn ch_(value: Float) {
Ch(value)
}

/// Mainly internal function, can be used if you need to go from a Size to a String
/// Internal function, can be used if you need to go from a Size to a String
/// in case you're building on top of craft.
pub fn to_string(size) {
case size {
Expand Down
24 changes: 18 additions & 6 deletions src/helpers.ffi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,22 @@ export function getFunctionName() {
return stack.split('\n').slice(1, 5).join('\n')
}

export function deepEqual(args, previousArgs) {
const constants = ['string', 'number', 'boolean']
if (constants.includes(typeof args) || constants.includes(typeof previousArgs)) return args === previousArgs
for (const value in args) {
if (!(value in previousArgs)) return false
const isSame = deepEqual(args[value], previousArgs[value])
// Compare two data structures to check if they're the same.
export function deepEqual(a, b) {
const consts = ['string', 'number', 'boolean']
if (consts.includes(typeof a) || consts.includes(typeof b)) return a === b
for (const value in a) {
if (!(value in b)) return false
const isSame = deepEqual(a[value], b[value])
if (!isSame) return false
}
return true
}

// A Style property can be of four types: a class composition, a property definition
// a pseudo-selector definitions or a media query definition.
// They're defined in Gleam, and the class is opaque, so the only way is to
// read in the content of the object to check the interesting fields.
export function determineStyleType(style) {
if ('class_name' in style && typeof style.class_name === 'string') {
return 'compose'
Expand All @@ -43,11 +48,18 @@ export function determineStyleType(style) {
}
}

// Take a class definition, and turns it to something like
// .className {
// property1: value;
// property2: value;
// }
export function wrapClass(id, properties, indent_, pseudo = '') {
const baseIndent = indent(indent_)
return [`${baseIndent}.${id}${pseudo} {`, ...properties, `${baseIndent}}`].join('\n')
}

// Turn the different components of a property into the correct CSS property.
// I.e. property: value [!important];
export function computeProperty(indent_, property) {
const baseIndent = indent(indent_)
const key = `${baseIndent}${property.key}`
Expand Down

0 comments on commit 2d1356f

Please sign in to comment.