diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 1cd9deb..1ab70c1 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -32,7 +32,7 @@ jobs: version: '0.8.156' - name: Build static site - run: bb github-pages + run: bb publish-gh-pages - name: Deploy uses: peaceiris/actions-gh-pages@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e725f0e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +## [unreleased] + +## 0.1.0 + +First real release! + +- Added full complement of components +- Published fleshed-out documentation notebook at https://jsxgraph.mentat.org diff --git a/DEVELOPING.md b/DEVELOPING.md index cc4533f..f27fc0d 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -1,37 +1,72 @@ +## Note about Fork + +This library is currently using my fork of jsxgraph at +https://github.com/sritchie/jsxgraph and published to +https://www.npmjs.com/package/@mentatcollective/jsxgraph, but these changes +should appear upstream soon and we can back off to the official library. + ## Dev Dependencies `clj`, `shadow-cljs`, `node` and `babashka`. -## Releasing Github Pages +## Github Pages, Docs Notebook + +The project's [Github Pages site](https://jsxgraph.mentat.org) hosts an +interactive [Clerk](https://github.com/nextjournal/clerk) notebook demonstrating +the library's use. -TODO discuss the documentation notebook, how we build it, how we use Clerk etc.' +### Local Notebook Dev -To generate the Github Pages site in the `public` folder, run: +To start a shadow-cljs process watcher for the JS required to run the Clerk +notebook, run ``` -bb github-pages +bb dev-notebook ``` -This is run by the Github Action to actually publish. +Then start a Clojure process however you like, and run `(user/start!)` to run +the Clerk server. This command should open up `localhost:7777`. -To locally generate a copy of the documentation notebook, run +### Github Pages Static Build + +To test the Pages build locally: ``` bb publish-local ``` -After the build is complete, visit http://127.0.0.1:8080/ to see the production -build of the documentation notebook. +This will generate the static site in `public`, start a development http server +and open up a browser window (http://127.0.0.1:8080/) with the production build +of the documentation notebook. + +### Pages Build + +To build and release to Github Pages: + +``` +bb release-gh-pages +``` + +This will ship the site to https://jsxgraph.mentat.org. ## Publishing to Clojars -- Update the version in build.clj -- Make a new Github Release +The template for the project's `pom.xml` lives at +[`template/pom.xml`](https://github.com/mentat-collective/jsxgraph.cljs/blob/main/template/pom.xml). + +To create a new release: + +- Update the version in + [build.clj](https://github.com/mentat-collective/jsxgraph.cljs/blob/main/build.clj) +- Make a new [Github + Release](https://github.com/mentat-collective/jsxgraph.cljs/releases) with tag + `v`. -Launching the release will create a new tag and trigger the following command: +Submitting the release will create the new tag and trigger the following +command: ``` -clojure -T:build publish +bb release ``` The new release will appear on Clojars. diff --git a/README.md b/README.md index 8873305..cec0577 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,98 @@ -# jsxgraph.cljs - -A light ClojureScript wrapper over [JSXGraph][JSXGRAPH]. +# JSXGraph.cljs [![Build Status](https://github.com/mentat-collective/jsxgraph.cljs/actions/workflows/kondo.yml/badge.svg?branch=main)](https://github.com/mentat-collective/jsxgraph.cljs/actions/workflows/kondo.yml) [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/mentat-collective/jsxgraph.cljs/blob/main/LICENSE) [![cljdoc badge](https://cljdoc.org/badge/org.mentat/jsxgraph.cljs)](https://cljdoc.org/d/org.mentat/jsxgraph.cljs/CURRENT) [![Clojars Project](https://img.shields.io/clojars/v/org.mentat/jsxgraph.cljs.svg)](https://clojars.org/org.mentat/jsxgraph.cljs) -In progress! +[JSXGraph][JSXGraph] is a JavaScript library that lets you build 2-dimensional +scenes full of geometric objects, function curves and interactive UI elements, +potentially with many complex constraints defined between these objects. + +[JSXGraph.cljs](https://github.com/mentat-collective/jsxgraph.cljs) extends +JSXGraph with a [React][REACT] / [Reagent][REAGENT] component that makes it easy to define +JSXGraph constructions inside of a user interface built with Clojurescript. + +## Quickstart + +Install `jsxgraph.cljs` into your Clojurescript project using the instructions +at its Clojars page: + +[![Clojars Project](https://img.shields.io/clojars/v/org.mentat/jsxgraph.cljs.svg)](https://clojars.org/org.mentat/jsxgraph.cljs) + +Require `jsxgraph.core` in your namespace: + +```clj +(ns my-app + (:require [jsxgraph.core :as jsx])) +``` + +Create your first `jsx/JSXGraph` board, populated with two points and an arrow +between them: + +```clj +(cljs + [jsx/JSXGraph {:boundingbox [-3 3 3 -3] :axis true} + [jsx/Point {:name "A" :size 1 :parents [-1 1]}] + [jsx/Point {:id "B" :name "BEE!" :size 1 :parents [2 -1]}] + [jsx/Arrow {:size 4 + :parents ["A" "B"]}]]) +``` + +![2022-12-08 14 25 32](https://user-images.githubusercontent.com/69635/206570839-2fb1c4a9-dbb0-4c39-ac63-be5fc1ac4900.gif) + +Here's a more complex example of an interactive vector field, implemented +[here](https://jsxgraph.mentat.org/#Vector%20Field): -- how to use / require the library -- usage example -- notebook link and source -- how to convert from jsxgraph proper... +![2022-12-08 10 31 18](https://user-images.githubusercontent.com/69635/206570865-7b24e561-2c21-4b79-a665-41644c5e6f65.gif) -## JSXGraph Examples +See the project's [interactive documentation +notebook](https://jsxgraph.mentat.org) for more guides and examples. -- TODO get the examples directory linked +## Interactive Documentation via Clerk + +The project's [interactive documentation](https://jsxgraph.mentat.org) was +generated using Nextjournal's [Clerk](https://github.com/nextjournal/clerk). If +you'd like to edit or play with the documentation, you'll need to install + +- [node.js](https://nodejs.org/en/) +- The [clojure command line tool](https://clojure.org/guides/install_clojure) +- [Babashka](https://github.com/babashka/babashka#installation) + +Once this is done, run this command in one terminal window to build and serve the custom JS required by the notebook: + +``` +bb dev-notebook +``` + +In another terminal window, run + +``` +bb start-clerk +``` + +This should open a browser window to `http://localhost:7777` with the contents +of the documentation notebook. Any edits you make to `dev/jsxgraph/notebook.clj` +will be picked up and displayed in the browser on save. + +## Thanks and Support + +To support this work and my other open source projects, consider sponsoring me +via my [GitHub Sponsors page](https://github.com/sponsors/sritchie). Thank you +to my current sponsors! + +I'm grateful to [Clojurists Together](https://www.clojuriststogether.org/) for +financial support during this library's creation. Please consider [becoming a +member](https://www.clojuriststogether.org/developers/) to support this work and +projects like it. ## License -[MIT](LICENSE). +Copyright © 2022 Sam Ritchie. + +Distributed under the [MIT License](LICENSE). See [LICENSE](LICENSE). -[JSXGRAPH]: https://jsxgraph.org +[CLJS]: https://clojurescript.org/ +[JSXGRAPH]: https://jsxgraph.org/ +[REACT]: https://reactjs.org/ +[REAGENT]: https://reagent-project.github.io/ diff --git a/bb.edn b/bb.edn index f88b01b..78672dc 100644 --- a/bb.edn +++ b/bb.edn @@ -2,7 +2,7 @@ {sha (let [sha (-> (shell {:out :string} "git rev-parse HEAD") (:out) - (clojure.string/trim )) + (clojure.string/trim)) file "public/index.html"] (-> (slurp file) (clojure.string/replace "$GIT_SHA" sha) @@ -12,17 +12,22 @@ (do (shell "npm install") (shell "npm run watch-clerk")) - github-pages - (do (shell "npm install") - (shell "npm run release-clerk") - (shell "clojure -X:nextjournal/clerk user/github-pages!") - (run 'sha)) + start-clerk + (shell "clojure -X:dev:nextjournal/clerk user/start!") - publish-local - (do (shell "npm install") + publish-gh-pages + (do (shell "npm ci") (shell "npm run release-clerk") - (shell "clojure -X:nextjournal/clerk user/publish-local!") + (shell "clojure -X:dev:nextjournal/clerk user/github-pages!") (run 'sha) + (spit "./public/CNAME" "jsxgraph.mentat.org")) + + release-gh-pages + (do (run 'publish-gh-pages) + (shell "npm run gh-pages")) + + publish-local + (do (run 'publish-gh-pages) (shell "npm run serve")) release diff --git a/build.clj b/build.clj index ac0706c..efed68e 100644 --- a/build.clj +++ b/build.clj @@ -5,7 +5,7 @@ ;; ## Variables (def lib 'org.mentat/jsxgraph.cljs) -(def version "0.0.1") +(def version "0.1.0") (defn- ->version ([] version) diff --git a/deps.edn b/deps.edn index dcc2c86..b92f3ea 100644 --- a/deps.edn +++ b/deps.edn @@ -10,15 +10,14 @@ :nextjournal/clerk {:extra-paths ["dev"] :extra-deps - {io.github.nextjournal/clerk.viewer - {:git/url "https://github.com/nextjournal/clerk.git" - :git/sha "f18afd7e6165748765ea43c23604cc1965f20e35" - :deps/root "viewer"}} - :exec-fn nextjournal.clerk/build! - :exec-args {:index "dev/jsxgraph/notebook.clj" - :bundle? false - :out-path "public/build" - :extra-namespaces [jsxgraph.clerk-ui]}} + {io.github.nextjournal/clerk {:mvn/version "0.11.603"} + + applied-science/js-interop {:mvn/version "0.3.3"} + org.babashka/sci {:mvn/version "0.4.33"} + reagent/reagent {:mvn/version "1.1.1"} + io.github.babashka/sci.configs {:git/sha "fcd367c6a6115c5c4e41f3a08ee5a8d5b3387a18"} + io.github.nextjournal/viewers {:git/sha "3284aae7379bde3fcf41d17c663bed421fb31d6d"} + metosin/reitit-frontend {:mvn/version "0.5.15"}}} :build {:deps {io.github.clojure/tools.build {:git/tag "v0.8.2" :git/sha "ba1a2bf"} diff --git a/dev/jsxgraph/clerk_ui.cljc b/dev/jsxgraph/clerk_ui.cljc index 70bd0ef..7b96dbb 100644 --- a/dev/jsxgraph/clerk_ui.cljc +++ b/dev/jsxgraph/clerk_ui.cljc @@ -1,7 +1,7 @@ (ns jsxgraph.clerk-ui (:require #?(:cljs [jsxgraph.core]) - #?(:clj [nextjournal.clerk :as clerk]) - #?(:cljs [nextjournal.clerk.sci-env :refer [!sci-ctx]]) + #?(:clj [nextjournal.clerk :as clerk]) + #?(:cljs [nextjournal.clerk.sci-viewer :as sv]) #?(:cljs [sci.core :as sci])) #?(:cljs (:require-macros [jsxgraph.clerk-ui]))) @@ -12,7 +12,7 @@ ;; library's CLJS code in the Clerk notebooks that document the library. #?(:cljs - (swap! !sci-ctx + (swap! sv/!sci-ctx sci/merge-opts {:classes {'Math js/Math} :aliases {'jsx 'jsxgraph.core} diff --git a/dev/jsxgraph/notebook.clj b/dev/jsxgraph/notebook.clj index d02af3b..aa7cb36 100644 --- a/dev/jsxgraph/notebook.clj +++ b/dev/jsxgraph/notebook.clj @@ -1,101 +1,1110 @@ +^#:nextjournal.clerk +{:toc true + :no-cache true + :visibility :hide-ns} +(ns jsxgraph.notebook + (:require [jsxgraph.clerk-ui :refer [cljs]])) + ;; # JSXGraph.cljs ;; -;; _alpha - [feedback welcome](https://github.com/mentat-collective/jsxgraph.cljs)_ +;; A [React](https://reactjs.org/) +;; / [Reagent](https://reagent-project.github.io/) interface to +;; the [JSXGraph](https://jsxgraph.org/) interactive geometry and mathematics +;; library. + +;; [![Build Status](https://github.com/mentat-collective/jsxgraph.cljs/actions/workflows/kondo.yml/badge.svg?branch=main)](https://github.com/mentat-collective/jsxgraph.cljs/actions/workflows/kondo.yml) +;; [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/mentat-collective/jsxgraph.cljs/blob/main/LICENSE) +;; [![cljdoc badge](https://cljdoc.org/badge/org.mentat/jsxgraph.cljs)](https://cljdoc.org/d/org.mentat/jsxgraph.cljs/CURRENT) +;; [![Clojars Project](https://img.shields.io/clojars/v/org.mentat/jsxgraph.cljs.svg)](https://clojars.org/org.mentat/jsxgraph.cljs) +;; +;; > The interactive documentation on this page was generated from [this source +;; > file](https://github.com/mentat-collective/jsxgraph.cljs/blob/$GIT_SHA/dev/jsxgraph/notebook.clj) +;; > using [Clerk](https://github.com/nextjournal/clerk). Follow +;; > the [instructions in the +;; > README](https://github.com/mentat-collective/jsxgraph.cljs/tree/main#interactive-documentation-via-clerk) +;; > to run and modify this notebook on your machine! +;; > +;; > See the [Github +;; > project](https://github.com/mentat-collective/jsxgraph.cljs) for more +;; > details, and the [cljdoc +;; > page](https://cljdoc.org/d/org.mentat/jsxgraph.cljs/CURRENT/doc/readme) for +;; > detailed API documentation. ;; -;; Dynamic geometry! +;; ## What is JSXGraph? ;; -;; ## Features +;; [JSXGraph](https://jsxgraph.org/) is a JavaScript library that lets you build +;; 2-dimensional scenes full of geometric objects, function curves and +;; interactive UI elements, potentially with many complex constraints defined +;; between these objects. ;; -;; 1. First feature! +;; For example, here is a visual JSXGraph proof that the [Euler +;; line](https://en.wikipedia.org//wiki/Euler_line) passes through any +;; triangle's [orthocenter](https://en.wikipedia.org/wiki/Orthocenter), [centroid](https://en.wikipedia.org/wiki/Centroid) +;; and [circumcenter](https://en.wikipedia.org/wiki/Circumcenter). Drag the +;; triangle's corner points around and watch the example respond: +;; +;; > The 'show code' link below will expand the example's source. Use the +;; > checkmarks to toggle on and off the elements that contribute to each point +;; > on the Euler Line. + +^{:nextjournal.clerk/visibility {:code :fold}} +(cljs + (let [!state (atom + {:circumcenter true + :orthocenter true + :centroid true}) + centroid? #(:centroid @!state) + orthocenter? #(:orthocenter @!state) + circumcenter? #(:circumcenter @!state) + cerise {:strokeColor "#901B77" + :fillColor "#CA147A"} + grass {:strokeColor "#009256" + :fillColor "#65B72E" + :visible true + :withLabel true} + perpendicular {:strokeColor "black" + :visible orthocenter? + :dash 1 + :strokeWidth 1} + median {:strokeWidth 1 + :strokeColor "#333333" + :dash 2}] + [jsx/JSXGraph + {:boundingbox [-1.5 2 1.5 -1] + :showCopyright false + :keepaspectratio true} + [:checkbox {:parents [-2 1.5 "Circumcenter"] + :checked (:circumcenter @!state) + :on {:up #(swap! !state assoc :circumcenter (not (.Value %)))}}] + [:checkbox {:parents [-2 1.3 "Orthocenter"] + :checked (:orthocenter @!state) + :on {:up #(swap! !state assoc :orthocenter (not (.Value %)))}}] + [:checkbox {:parents [-2 1.1 "Centroid"] + :checked (:centroid @!state) + :on {:up #(swap! !state assoc :centroid (not (.Value %)))}}] + + ;; Triangle + [:point (assoc cerise :parents [1 0] :id "A")] + [:point (assoc cerise :parents [-1 0] :id "B")] + [:point (assoc cerise :parents [0.65 1.45] :id "C")] + [:polygon + {:parents ["A" "B" "C"] + :borders {:ids ["pol_0" "pol_1" "pol_2"]} + :fillColor "#FFFF00" + :lines {:strokeWidth 2 + :strokeColor "#009256"}}] + + ;; ## Circumcircle + [:circumcircle + {:parents ["A" "B" "C"] + :strokeColor "#000000" + :visible circumcenter? + :dash 3 + :strokeWidth 1 + :center (assoc grass + :name "U" + :visible circumcenter?)}] + + ;; ## Orthocenter + ;; + ;; Altitudes + [:perpendicular + (assoc perpendicular :parents ["pol_0" "C"] :id "pABC")] + [:intersection + (assoc cerise + :parents ["pol_0" "pABC"] + :visible orthocenter? + :name "H_c")] + + [:perpendicular + (assoc perpendicular :parents ["pol_1" "A"] :id "pBCA")] + [:intersection + (assoc cerise + :parents ["pol_1" "pBCA"] + :visible orthocenter? + :name "H_a")] + + [:perpendicular + (assoc perpendicular :parents ["pol_2" "B"] :id "pCAB")] + [:intersection + (assoc cerise + :parents ["pol_2" "pCAB"] + :visible orthocenter? + :name "H_b")] + + ;; Intersection of Altitudes + [:intersection + (assoc grass + :visible orthocenter? + :id "i1" + :name "H" + :parents ["pABC" "pCAB" 0])] + + ;; ## Centroid + ;; + ;; Medians + [:midpoint + (assoc cerise :name "M_a" + :visible centroid? + :parents ["B" "C"])] + [:segment + (assoc median :id "ma" + :visible centroid? + :parents ["M_a" "A"])] + + [:midpoint + (assoc cerise :name "M_b" + :visible centroid? + :parents ["C" "A"])] + [:segment + (assoc median :id "mb" + :visible centroid? + :parents ["M_b" "B"])] + + [:midpoint + (assoc cerise :name "M_c" + :visible centroid? + :parents ["A" "B"])] + [:segment + (assoc median :id "mc" + :visible centroid? + :parents ["M_c" "C"])] + + ;; Intersection of Medians + [:intersection + (assoc grass :id "i2" + :visible centroid? + :name "S" :parents ["ma" "mc" 0])] + + ;; Euler's Line (intersection of orthocenter and median, but the + ;; circumcenter lies on this line as well). + [:line + {:parents ["i1" "i2"] + :strokeWidth 2 + :strokeColor "#901B77"}]])) + +;; [JSXGraph.cljs](https://github.com/mentat-collective/jsxgraph.cljs) extends +;; JSXGraph with a [Reagent](https://reagent-project.github.io/) component that +;; makes it easy to define JSXGraph constructions inside of a user interface +;; built with Clojurescript. + +;; ## Quickstart ;; -;; 2. Second feature. +;; Install `JSXGraph.cljs` into your Clojurescript project using the +;; instructions at its Clojars page: + +;; [![Clojars +;; Project](https://img.shields.io/clojars/v/org.mentat/jsxgraph.cljs.svg)](https://clojars.org/org.mentat/jsxgraph.cljs) ;; -;; ## Usage +;; Or grab the most recent code using a Git dependency: ;; ;; ```clj ;; ;; deps -;; {org.mentat/jsxgraph.cljs {:git/sha "$GIT_SHA"}} -;; -;; ;; namespace +;; {org.mentat/jsxgraph.cljs +;; {:git/sha "$GIT_SHA"}} +;; ``` + +;; Require `jsxgraph.core` in your namespace: + +;; ```clj ;; (ns my-app ;; (:require [jsxgraph.core :as jsx] ;; [reagent.core :as reagent])) -;;``` +;; ``` ;; -;; Hi! +;; The main entrypoint to the library is the `jsxgraph.core/JSXGraph` component. +;; Each class in the [JSXGraph API](https://jsxgraph.org/docs/index.html) has a +;; corresponding component bound as `jsx/ClassName`. Use these to build a simple +;; arrow and drag it around by its point: -^#:nextjournal.clerk -{:toc true - :no-cache true - :visibility :hide-ns} -(ns jsxgraph.notebook - (:require [jsxgraph.clerk-ui :refer [cljs]])) +(cljs + [jsx/JSXGraph {:boundingbox [-2 2 2 -2] :axis true} + [jsx/Arrow {:name "A" :size 4 + :parents [[0 0] [1 1]]}]]) + +;; This guide assumes that you're coming at `JSXGraph` for the first time +;; through `JSXGraph.cljs`. If you're coming from JavaScript, OR if you're +;; attempting to port [one of the many JSXGraph +;; examples](http://jsxgraph.org/wp/about/index.html), you'll absolutely need to +;; read [JSXGraph vs JSXGraph.cljs](#JSXGraph%20vs%20JSXGraph.cljs) below. + +;; > If you're not familiar with React or Reagent, or what a "component" is, +;; > please give the [Reagent homepage](https://reagent-project.github.io/) a +;; > read. If this is your first Clojurescript experience, come say hi to +;; > me (@sritchie) in the [Clojurians Slack](http://clojurians.net/) and I'll +;; > get you started. -;; ## Lines +;; ## Guides -;; A line needs two points. Lets construct two points "A" and "B". Then we -;; construct a line through "A" and "B". The setting of a new color and changing -;; the stroke-width is not necessary. +;; A `JSXGraph` construction consists of a "board" populated by some number of +;; "elements", where an element is an instance of one of the classes described +;; in the [JSXGraph API docs](https://jsxgraph.org/docs/index.html). +;; +;; The [React](https://reactjs.org/) +;; / [Reagent](https://reagent-project.github.io/) data model is "declarative". +;; This means that you create and populate a board by listing, in order, the +;; elements that you want to appear on the board. +;; +;; ### Creating Your First Board +;; +;; Declare a board with the `jsx/JSXGraph` component: (cljs - [jsx/JSXGraph {:boundingbox [-5 5 5 -2] :showCopyright false} - [jsx/Point {:name "A" :size 4} [-1 1]] - [jsx/Point {:name "B" :size 4} [2 -1]] - [jsx/Line {:strokeColor "#00ff00" :strokeWidth 2} - ["A" "B"]]]) + [jsx/JSXGraph {:axis true}]) -;; ## Circle +;; The component takes +;; +;; - a `keyword => value` map of attributes (see the `attributes` section under +;; `Parameters` at [this +;; page](https://jsxgraph.org/docs/symbols/JXG.JSXGraph.html)) for allowed +;; values +;; - Any number of child components. +;; +;; Child components are added to the board in the order that they're listed. A +;; full re-render is triggered any time any of the properties of the board or +;; any child component changes. +;; +;; For example, here's a board with two `jsx/Point`s and a `jsx/Arrow` between +;; them: -;; One possibility to construct a circle is to give its center and a point -;; defining its radius. Lets construct two points "A" and "B". Then we'll -;; construct a circle through "A" and "B". +(cljs + [jsx/JSXGraph {:boundingbox [-3 3 3 -3] :axis true} + [jsx/Point {:name "A" :size 1 :parents [-1 1]}] + [jsx/Point {:id "B" :name "BEE!" :size 1 :parents [2 -1]}] + [jsx/Arrow {:size 4 + :parents ["A" "B"]}]]) + +;; Note these details: +;; +;; - The `Point` instances declare their initial coordinates with a vector +;; supplied via the `:parents` key. (All elements require parents.) This leaves +;; them free to move! Drag each `Point` around and see. +;; - The `Arrow` sets its `:parents` to points `"A"` and `"B"` using their IDs. +;; This creates a constraint, where the `Arrow` can only be moved by moving +;; the points. +;; - The second `Point` has a `:name` different from its `:id`. This allowed us +;; to use the `:name` for the second `Point`'s label, and the `:id` for +;; reference by `Arrow`. +;; +;; You can also refer to elements by their classname, written as either a string or a keyword: (cljs - [jsx/JSXGraph {:boundingbox [-5 5 5 -2] :showCopyright false} - [jsx/Point {:name "A" :size 4 :face "o"} [0 1]] - [jsx/Point {:name "B" :size 4 :face "o"} [2 0]] - [jsx/Circle {:strokeColor "#00ff00" :strokeWidth 2} - ["A" "B"]]]) + [jsx/JSXGraph {:boundingbox [-3 3 3 -3] :axis true} + [:point {:name "A" :size 1 :parents [-1 1]}] + ["point" {:id "B" :name "BEE!" :size 1 :parents [2 -1]}] + [:arrow {:size 4 + :parents ["A" "B"]}]]) + +;; > Note that this only works if you insert the element as a direct child of a +;; > `jsx/JSXGraph` component. When writing [Custom +;; > Components](#Custom%20Components) you'll need to write out the full +;; > `jsx/` form. +;; +;; ### Event Listeners +;; +;; `JSXGraph` elements fire events during various updates and user interactions. +;; To get data out of the element, pass a map of `{ }` +;; to the element via the `:on` key. +;; +;; a `Point` exposes its coordinates +;; via [`X()`](https://jsxgraph.org/docs/symbols/JXG.CoordsElement.html#X) +;; and [`Y()`](https://jsxgraph.org/docs/symbols/JXG.CoordsElement.html#X) +;; methods. + +;; This example registers a function that updates a [reactive +;; atom](https://github.com/reagent-project/reagent/blob/master/doc/ManagingState.md#intro-to-atoms) +;; `!state` every time the point fires a `"drag"` event, and renders the current +;; value of `!state` each time it changes. +;; +;; Drag the point around and the watch the state update: + +(cljs + (reagent/with-let + [!state (reagent/atom {:x 0 :y 0}) + update! (fn [p] + (swap! !state assoc + :x (.X p) + :y (.Y p)))] + [:<> + [:pre (str @!state)] + [jsx/JSXGraph {:axis true} + [:point + {:parents [0 0] + :on {:drag update!}}]]])) + +;; The lovely thing about this data model is that other parts of your +;; application outside of `JSXGraph` can read from this state as well. +;; +;; ### Functions as Parents +;; +;; Many `Element` types can take functions as parents. This feature allows you +;; to bind an `Element`'s coordinate to some external state, like the state +;; persisted above in `!state`. +;; +;; This example adds a first free `Point` named `"A"`, and a second `Point` with +;; position constrained to always equal `[cos(A_x), sin(A_y)]`. + +(cljs + (reagent/with-let + [!state (reagent/atom {:x 0 :y 0}) + update! (fn [p] + (swap! !state assoc + :x (.X p) + :y (.Y p))) + cos-x (fn [] (Math/cos (:x @!state))) + sin-y (fn [] (Math/sin (:y @!state)))] + [:<> + [:pre (str @!state)] + [jsx/JSXGraph {:axis true} + [jsx/Point + {:parents [0 0] + :name "A" + :on {:drag update!}}] + [jsx/Point + {:parents [cos-x sin-y] + :name "[cos A_x, sin A_y]"}]]])) + + +;; > There is a "gotcha" here! If you use a [reactive +;; > atom](https://github.com/reagent-project/reagent/blob/master/doc/ManagingState.md#intro-to-atoms) +;; > and access its value somewhere in your component, any change to the atom +;; > will force the board to re-render. +;; > +;; > If you do this you need to be careful to move any functions you want to use +;; > _outside_ of the component. Otherwise the board will erase and re-mount +;; > every component every time the atom's value changes. + +;; ### GEONExT Syntax +;; +;; Another way to declare dependencies between `Element`s is to use "GEONExT +;; syntax". I can't find good documentation for this anywhere, but its +;; referenced [here on the `JSXGraph` +;; wiki](https://jsxgraph.org/wiki/index.php?title=Point#GEONExT_syntax). +;; +;; This example adds a first free `Point` named `"A"`, and a second `Point` with +;; its `Y` coordinate free and its `X` coordinate constrained to always equal +;; `A_x + A_y`: + +(cljs + [jsx/JSXGraph {:axis true} + [jsx/Point {:name "A" :parents [0 0]}] + [jsx/Point {:name "B" :parents ["X(A) + Y(A)" 2]}]]) + +;; > There is currently [a bug](https://github.com/jsxgraph/jsxgraph/issues/489) +;; > with this feature, where re-rendering a board will trigger an error. You +;; > may need to refresh your page if this occurs. + +;; ### Component Refs +;; +;; If you need access to the actual instance of a `JSXGraph` element, pass a +;; callback function using the `:ref` keyword. The `:ref` function receives an +;; instance of the element when it is mounted, and `nil` when the board is +;; destroyed or the element is remounted. + +(cljs + [jsx/JSXGraph {:axis true} + [:point + {:parents [1 1] + :ref (fn [p] + (when p + (.setName p "NAME!")))}]]) + +;; You might use this feature to store a reference to some element that doesn't +;; fire its own events, so that you can query its value when some other element +;; fires. +;; +;; The `RiemannSum` element has this property. The example below uses `:ref` to +;; store a reference to the `RiemannSum`, so that the `Text` element in the +;; bottom left of the scene can query it inside its parent function. See the +;; inline comments for more detail. + +(cljs + (let [!state (atom {:slider 4 :riemann nil}) + f (fn [x] + ;; 1/2 x^2 - 2x + (- (* 0.5 x x) + (* 2 x)))] + [jsx/JSXGraph {:boundingbox [-3 7 5 -3] + :axis true} + [jsx/Slider + {:parents [[0 4] [3 4] [0 (:slider @!state) 10]] + :on {:drag #(swap! !state assoc :slider (.Value %))}}] + [jsx/RiemannSum + {:parents [f #(:slider @!state) "middle" -2 5] + :fillOpacity 0.4 + ;; When this element mounts, a reference to the `RiemannSum` instance is + ;; stored under the `:riemann` key in the `!state` atom. + :ref (fn [elem] + (when elem + (swap! !state assoc :riemann elem)))}] + [jsx/FunctionGraph + {:parents [f -2 5]}] + [jsx/Text + {:parents [-2 -2 (fn [] + ;; The `:riemann` key has been populated by the time the + ;; `Text` component is mounted, so we don't need to + ;; guard against a `nil` value here. + (let [v (.Value (:riemann @!state))] + (str "Sum: " (.toFixed v 4))))]}]])) + +;; ### Custom Components +;; +;; If you want to reuse some pattern in your board declaration, you can create a +;; new Reagent component that bundles together a sequence of primitives, +;; possibly closing over some state that these elements need. +;; +;; > See the [Reagent Component +;; > Guide](https://github.com/reagent-project/reagent/blob/master/doc/CreatingReagentComponents.md#the-three-ways) +;; > for more detail. +;; +;; Create a component by writing a function that takes some number of arguments +;; and returns a vector of the form `[:<> ]`. Here is a +;; component that accepts 3 vertex point definitions and creates a triangle: + +(cljs + (defn Triangle [a b c] + [:<> + [jsx/Point {:name "A" :size 4 :parents a}] + [jsx/Point {:name "B" :size 4 :parents b}] + [jsx/Point {:name "C" :size 4 :parents c}] + [jsx/Polygon {:parents ["A" "B" "C"]}]])) + +;; Use your component by including a vector of the form `[ComponentName +;; ]` as a child if your `JSXGraph` component; + +(cljs + [jsx/JSXGraph {:axis true} + [Triangle + [-1 -1] [1 1] [-1 1]]]) + +;; You can abstract this example further by writing your own version of a +;; `Polygon` element that takes a map of ID => a point's `:parents` entry: + +(cljs + (defn MyPolygon [id->parents] + (let [ids (into [] (keys id->parents)) + points (for [[id parents] id->parents] + [jsx/Point {:name id :parents parents}])] + ;; Build [:<> ...points...], then add `[jsx/Polygon ...]` to the end of the + ;; list. + (-> (into [:<>] points) + (conj [jsx/Polygon {:parents ids}]))))) + +;; Re-define a version of `Triangle` that uses `MyPolygon`: + +(cljs + (defn Triangle* [a b c] + [MyPolygon {:A a :B b :C c}])) + +;; These elements should look exactly the same as the previous `Triangle` +;; definition: + +(cljs + [jsx/JSXGraph {:axis true} + [Triangle* + [-1 -1] [1 1] [-1 1]]]) + +;; ### State across Multiple Boards +;; +;; [Reagent's state +;; model](https://github.com/reagent-project/reagent/blob/master/doc/ManagingState.md) +;; makes it easy to get data out of various elements on the board. Storing state +;; in an external atom allows you to use state updates from one board to trigger +;; updates on another board. +;; +;; The following example places a free point one board (the parent), and +;; designates a second board as a child with `addChild`. The second board will +;; hold a point with location fixed by the first point. Any interaction with the +;; parent board will trigger updates to every element on the child board. + +;; First, our state: + +(cljs + (defonce !state (atom {:x 1 :y 1}))) + +;; Then the parent board with its free point. We'll use a `:ref` +;; function (see [Component Refs](#Component%20Refs)) to perform the [`addChild` +;; mutation](https://jsxgraph.org/docs/symbols/JXG.Board.html#addChild): + +(cljs + (let [update! (fn [p] + (swap! !state assoc + :x (.X p) + :y (.Y p)))] + [jsx/JSXGraph + {:axis true + :style {:height "300px"} + :ref (fn [b] + (when b (swap! !state assoc :board b)))} + [jsx/Point {:on {:drag update!} + :parents [(:x @!state) (:y @!state)]}]])) + +;; The child board will call `addChild` in its `:ref` function. Note that the +;; parents of the second point are no-argument functions that access the `:x` +;; and `:y` coordinates stored in `!state`. (The next example is folded so you +;; can see both boards at once. Click "show code" to unfold.) + +^{:nextjournal.clerk/visibility {:code :fold}} +(cljs + [jsx/JSXGraph + {:axis true + :style {:height "300px"} + :ref (fn [b] + (when b + (.addChild (:board @!state) b)))} + [jsx/Point {:parents + [#(:x @!state) #(:y @!state)]}]]) + +;; ## Advanced Examples +;; +;; This section explores `JSXGraph` by porting a few of the more complex +;; examples from the [Example +;; directory](http://jsxgraph.org/wp/about/index.html) and inventing a few of +;; our own constructions. + +;; ### Archimedean Spiral + +;; This example allows for interactive exploration of the [Archimedean +;; Spiral](https://en.wikipedia.org/wiki/Archimedean_spiral). This is a curve of +;; the form +;; +;; $$r=a + b \cdot \theta.$$ +;; +;; The board below includes interactive sliders for the $a$ and $b$ parameters, +;; and plots a polar curve for every pair of $(r, \theta)$. See the comments in +;; the source code for details on our choices. +;; +;; > The original example lives at [this +;; > page](http://jsxgraph.org/wiki/index.php/Archimedean_spiral). + +(cljs + ;; The `!state` atom is populated with the initial slider positions. Note that + ;; we are NOT using a `reagent/atom`, because we don't need Reagent to perform + ;; any re-renders when the state changes. Instead, state changes are picked up + ;; by the function we provide to the curve below. + (let [!state (atom {:a 1 :b 0.25})] + [jsx/JSXGraph + {:boundingbox [-10 10 10 -10] + :showCopyright false + ;; This option prevents the spiral from bulging out on the left and right + ;; side on wider windows by adjusting the provided `:boundingBox`. + :keepAspectRatio true} + + ;; Just for fun we are using the keyword form here to define components. + [:slider + {:name "a" + ;; The `:parents` are of the form + ;; + ;; [ [ ]] + :parents [[1 8] [5 8] [0 (:a @!state) 4]] + + ;; Each slider updates a value stored in `!state` above. + :on {:drag #(swap! !state assoc :a (.Value %))}}] + [:slider + {:name "b" + :parents [[1 9] [5 9] [0 (:b @!state) 4]] + :on {:drag #(swap! !state assoc :b (.Value %))}}] + [:curve + {:id "c" + ;; [, , , ] + :parents [(fn [phi] + (let [{:keys [a b]} @!state] + (+ a (* b phi)))) + [0 0] 0 (* 8 Math/PI)] + :curveType "polar" + :strokewidth 4}] + ;; Note here that the parents of these elements reference the string-based + ;; IDs of the element they want to target, instead of the actual + ;; instance (like in the original example) + [:glider {:parents ["c"] :name "g"}] + [:tangent {:parents ["g"] :dash 2 :strokeColor "#a612a9"}] + [:normal {:parents ["g"] :dash 2 :strokeColor "#a612a9"}]])) + +;; ### Lissajous Curve +;; +;; From the [Wikipedia page](https://en.wikipedia.org//wiki/Lissajous_curve), + +;; > A Lissajous curve (also known as a Lissajous figure or Bowditch curve) is +;; > the graph of a system of parametric equations +;; > +;; > $$x=A\sin(at+\delta),\quad y=B\sin(bt).$$ +;; > +;; > which describe [complex harmonic +;; > motion](https://en.wikipedia.org/wiki/Complex_harmonic_motion). +;; +;; The board below includes interactive sliders for the $a$, $b$, $A$ and $B$ +;; parameters, and plots a parametric curve for every pair of $(x y)$. + +;; The original example lives at [this +;; page](http://jsxgraph.org/wiki/index.php/Lissajous_curves). + +(cljs + (let [!state (atom {:a 3 :b 2 :A 3 :B 3 :delta 0})] + [jsx/JSXGraph + {:boundingbox [-8 8 8 -8] + :showCopyright false + :keepAspectRatio true + :axis true} + ;; Interactive Sliders + [:slider + {:name "a" :parents [[2,8],[6,8],[0,3,6]] + :on {:drag #(swap! !state assoc :a (.Value %))}}] + [:slider + {:name "b" :parents [[2,7],[6,7],[0,2,6]] + :on {:drag #(swap! !state assoc :b (.Value %))}}] + [:slider + {:name "A" :parents [[2,6],[6,6],[0,3,6]] + :on {:drag #(swap! !state assoc :A (.Value %))}}] + [:slider + {:name "B" :parents [[2,5],[6,5],[0,3,6]] + :on {:drag #(swap! !state assoc :B (.Value %))}}] + [:slider + {:name "δ" :parents [[2,4],[6,4],[0,0,Math.PI]] + :on {:drag #(swap! !state assoc :delta (.Value %))}}] + + ;; The Curve! + [:curve + {:parents + ;; [, , , ] + [(fn [t] + (let [{:keys [a A delta]} @!state] + (* A (Math/sin (+ (* a t) delta))))) + (fn [t] + (let [{:keys [b B]} @!state] + (* B (Math/sin (* b t))))) + 0 + (* 2 Math/PI)] + :strokeColor "#aa2233" + :strokewidth 3}]])) + +;; ### Unit Circle +;; +;; This example uses a custom Reagent component `UnitCircle` to inscribe a +;; polygon of `n` points into the unit circle. `UnitCircle` is similar to the +;; `MyPolygon` implementation above in [Custom +;; Components](#Custom%20Components), but generates its own point coordinates +;; internally. +;; +;; The component below also shows off a case where we _do_ need to use a +;; `reagent/atom`. + +(cljs + (defn UnitCircle [n] + (let [ids (range n) + i->pt (fn [i] + (let [angle (* (/ i n) 2 Math/PI) + x (Math/cos angle) + y (Math/sin angle)] + [jsx/Point + {:name i :size 5 :parents [x y]}]))] + [:<> + (into [:<>] (map i->pt) ids) + [jsx/Polygon + {:parents (mapv str ids) + :borders {:strokeColor "black"}}]]))) + +;; The board below instantiates the `UnitCircle` component using an `n` that +;; comes from an interactive slider. +;; +;; To bind the state, we +;; +;; - use `reagent/with-let` instead of `let` +;; - `reagent/atom` instead of `atom` +;; +;; Doing this will force any component that dereferences `!state` to re-render. +;; This is what we want! We want a change in the `n` slider to trigger the +;; drawing of a new `UnitCircle`, vs previous examples where we wanted to send +;; new information to elements that had rendered a single time on the board. +;; +;; Drag the slider around and note the slight flicker as the board redraws. + +(cljs + (reagent/with-let + [!n (reagent/atom 6) + ->!n #(reset! !n (.Value %))] + [jsx/JSXGraph + {:boundingbox [-1.5 2 1.5 -2] + :showCopyright false + :keepAspectRatio true} + ;; Any time `!n` is updated by the `->!n` function provided to the slider + ;; below, this component AND the `jsx/Slider` will re-render. + [UnitCircle @!n] + [jsx/Slider + {:name "n" + :snapWidth 1 + :on {:drag ->!n} + :parents + [[-1 1.5] [1 1.5] [1 @!n 50]]}]])) -;; ## Data Sharing +;; ### Riemann Sum ;; -;; This demo shows how to get the input talking to some state. There are issues -;; here that I'll document soon. +;; A [Riemann Sum](https://en.wikipedia.org/wiki/Riemann_sum) is an +;; approximation of the integral of a function (the area under its curve). You +;; generate rectangles whose heights are determined by the values of the +;; function, and add up the rectangles to obtain your approximation. Chopping +;; the area into more rectangles gives a better estimate. +;; +;; This example plots $\sin(x)$ along with a visual representation of a Riemann +;; sum. The board contains sliders to change the left and right bounds of the +;; function, as well as the number of rectangles in the (middle) Riemann sum. +;; +;; > The original example lives at [this +;; > page](https://jsxgraph.org/wiki/index.php/Riemann_sums). + +;; First we'll create our `!state` outside of the `reagent/with-let` block. This +;; will let other components access the state. (cljs - (defonce !state + (defonce !r-state (reagent/atom {:start -3 :end (* 2 Math/PI) :n 10}))) -;; And the example: +;; The board uses `reagent/with-let`; this will prevent the dereference calls +;; inside the bindings from triggering a component re-render. (cljs (reagent/with-let - [init @!state - start-update #(swap! !state assoc :start (.Value %)) - end-update #(swap! !state assoc :end (.Value %)) - n-update #(swap! !state assoc :n (.Value %)) - nf #(:n @!state) - startf #(:start @!state) - endf #(:end @!state) + [init @!r-state + start-update #(swap! !r-state assoc :start (.Value %)) + end-update #(swap! !r-state assoc :end (.Value %)) + n-update #(swap! !r-state assoc :n (.Value %)) + nf #(:n @!r-state) + startf #(:start @!r-state) + endf #(:end @!r-state) sin #(Math/sin %)] - [:<> - [:pre (str @!state)] - [jsx/JSXGraph {:boundingbox [-8 4 8 -5] - :showCopyright false - :axis true} - [jsx/Slider {:name "start" - :on-drag start-update} - [[1 3.5] [5 3.5] [-10 (:start init) 0]]] - - [jsx/Slider {:name "end" - :on-drag end-update} - [[1 2.5] [5 2.5] [0 (:end init) 10]]] - - [jsx/Slider {:name "n" - :snapWidth 1 - :on-drag n-update} - [[1 1.5] [5 1.5] [1 (:n init) 50]]] - - [jsx/FunctionGraph [sin startf endf]] - [jsx/RiemannSum [sin nf "left" startf endf]]]])) + [jsx/JSXGraph + {:boundingbox [-8 4 8 -5] + :showCopyright false + :axis true} + [jsx/Slider {:name "start" + :on {:drag start-update} + :parents + [[1 3.5] [5 3.5] [-10 (:start init) 0]]}] + [jsx/Slider {:name "end" + :on {:drag end-update} + :parents + [[1 2.5] [5 2.5] [0 (:end init) 10]]}] + [jsx/Slider {:name "n" + :snapWidth 1 + :on {:drag n-update} + :parents + [[1 1.5] [5 1.5] [1 (:n init) 100]]}] + + [jsx/RiemannSum + {:parents [sin nf "middle" startf endf] + ;; Store a reference to the `RiemannSum` instance so that we can query it + ;; from our text below. + :ref (fn [elem] + (when elem + (swap! !r-state assoc :riemann elem)))}] + + ;; Graph the actual function. + [jsx/FunctionGraph {:parents [sin startf endf]}] + [jsx/Text + {:parents + [-3.5 3 (fn [] + (let [v (.Value (:riemann @!r-state))] + (str "Riemann Sum: " + (.toFixed v 4))))]}] + [jsx/Text + {:parents + [-3.5 2.5 (fn [] + (let [{:keys [start end]} @!r-state] + (str "Actual Integral: " + (-> (- (- (Math/cos end)) + (- (Math/cos start))) + (.toFixed 4)))))]}]])) + +;; Because we stored our state outside of a `let` binding, we can query it from +;; other components, and they'll re-render on any update to `!r-state`. Change the +;; sliders above and watch these values change: + +(cljs + [:pre (str (dissoc @!r-state :riemann))]) + +;; ### Vector Field +;; +;; I found this lovely animation of an interactive vector field over +;; at [mafs.dev](https://mafs.dev/guides/display/vector-fields/), and had to +;; implement the example here in `JSXGraph.cljs`. +;; +;; This [vector field](https://en.wikipedia.org/wiki/Vector_field) example +;; assigns a vector, in this case a small arrow with a constant magnitude equal +;; to the value provided via `:scale`, to each point on a 2-dimensional grid. +;; The `:dimensions` options takes a 2-vector of the form `[, +;; ]` +;; +;; The `:coords-fn` option takes a pair of `[x y]` coordinates and returns the +;; tip `[x', y']` of a new vector. This will generate a vector on the grid +;; starting at `[x y]` and pointing at the same angle $\theta$ as the new +;; vector. +;; +;; `:opacity-fn` takes the coordinates of the base of the vector and returns its +;; opacity. + +(cljs + (defn VectorField + [{:keys [dimensions coords-fn opacity-fn scale] + :or {scale 1 + dimensions [5 5]}}] + (let [f (fn [x y] + (let [[x' y'] (coords-fn x y) + theta (Math/atan2 y' x')] + ;; Calculate the angle, then scale and translate the new vector's + ;; end to the appropriate point. + (js/Array. + (+ x (* scale (Math/cos theta))) + (+ y (* scale (Math/sin theta)))))) + [nx ny] dimensions] + (into [:<>] + (for [x (range nx) + y (range ny) + :let [x (- x (/ nx 2)) + y (- y (/ ny 2))]] + ;; We generate a `jsx/Line` vs a `jsx/Arrow` to have some more + ;; control over the arrowhead via the `:lastArrow` key below. + [jsx/Line + {:parents [#(js/Array. x y) #(f x y)] + :straightFirst false + :lastArrow {:type 1 :size 4} + :straightLast false + :strokeOpacity + (opacity-fn x y)}]))))) + +;; Now that we have the `VectorField`, we can use our tricks from above to +;; customize the `VectorField`'s vector values based on the position of a +;; moveable `Point` in the scene. Drag the point around! + +(cljs + (let [!coords (atom {:x 0.6 :y 0.6}) + update! (fn [p] + (swap! !coords assoc + :x (.X p) + :y (.Y p)))] + [jsx/JSXGraph + {:boundingbox [-6 6 6 -6] + :axis true + :grid true + :showNavigation false + :keepAspectRatio true} + [jsx/Point {:parents [(:x @!coords) + (:y @!coords)] + :on {:drag update!}}] + [VectorField + {:dimensions [20 14] + :scale 0.5 + :coords-fn + (fn [x y] + (let [{px :x py :y} @!coords] + [(- y py (- x px)) + (- (- px x) (- y py))])) + :opacity-fn + (fn [x y] + (/ (+ (Math/abs x) (Math/abs y)) 10))}]])) + +;; ### 3D Views +;; +;; As of [version +;; 1.4.4](https://jsxgraph.uni-bayreuth.de/wp/2022-05-27-release-of-version-1.4.4/), +;; JSXGraph has support for 3d views. +;; +;; Unlike all other elements, 3d elements must be added as children of a +;; `view3d` element. + +;; > `JSXGraph.cljs` doesn't currently support adding children to `jsx/View3D`, +;; > but we will! Follow [this +;; > ticket](https://github.com/mentat-collective/jsxgraph.cljs/issues/23) for +;; > updates. +;; +;; The allowed 3D elements are: +;; +;; - [Curve3D](https://jsxgraph.org/docs/symbols/Curve3D.html) +;; - [FunctionGraph3D](https://jsxgraph.org/docs/symbols/Functiongraph3D.html) +;; - [Line3D](https://jsxgraph.org/docs/symbols/Line3D.html) +;; - [ParametricSurface3D](https://jsxgraph.org/docs/symbols/ParametricSurface3D.html) +;; - [Point3D](https://jsxgraph.org/docs/symbols/Point3D.html) +;; +;; To instantiate a 3D scene with `JSXGraph.cljs`, you'll need to use +;; the [Imperative Style](#Imperative%20Style) described below to create your +;; view and 3D elements directly on the `board` instance. +;; +;; The following example is a port of the demo listed under +;; the [`FunctionGraph3D` API +;; entry](https://jsxgraph.org/docs/symbols/Functiongraph3D.html). The plot +;; shows the surface defined by the equation +;; +;; $$F(x, y) = \sin\left(\frac{xy}{4}\right).$$ + +(cljs + (let [box [-5 5] + F (fn [x y] + (Math/sin (* x (/ y 4))))] + [jsx/JSXGraph + {:boundingbox [-8 8 8 -8] + :showCopyright false + :axis false + :showNavigation false + :ref (fn [b] + ;; First, create a `view3d` object on the board `b`. + (let [view + (jsx/create b "view3d" + [[-6 -3] [8 8] + [box box box]] + {:xPlaneRear {:visible false} + :yPlaneRear {:visible false}})] + ;; Note that we create any 3d elements on `view`, not on the board + ;; `b`. + (jsx/create view "functiongraph3d" + [F box box] + {:strokeWidth 0.5 + :stepsU 70 + :stepsV 70})))}])) + +;; ## JSXGraph vs JSXGraph.cljs +;; +;; This section discusses differences between the [React](https://reactjs.org/) +;; / [Reagent](https://reagent-project.github.io/)-based interface provided by +;; `JSXGraph.cljs` and the imperative JavaScript API presented by the +;; original [`JSXGraph`](http://jsxgraph.org/) library. +;; +;; Let's compare two implementations of the [Circle +;; example](http://jsxgraph.org/wiki/index.php/Circle) from the [JSXGraph +;; examples +;; directory](http://jsxgraph.org/wiki/index.php?title=Category:Examples). +;; +;; Given some `div` with `id="jxgbox"`, the following snippet of JavaScript will +;; inject a `JSXGraph` construction into the `div`: + +;; ```js +;; var b = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-5, 2, 5, -2]}); +;; var p1 = b.create('point',[0,0], {name:'A',size: 4, face: 'o'}); +;; var p2 = b.create('point',[2,-1], {name:'B',size: 4, face: 'o'}) +;; var ci = b.createElement('circle',["A","B"], {strokeColor:'#00ff00',strokeWidth:2}); +;; ``` + +;; In `JSXGraph.cljs` we can write the example like this: + +(cljs + [jsx/JSXGraph {:boundingbox [-5 5 5 -2] + :keepAspectRatio true} + [:point {:name "A" :size 4 :face "o" :parents [0 0]}] + [:point {:name "B" :size 4 :face "o" :parents [2 -1]}] + [:circle {:strokeColor "#00ff00" :strokeWidth 2 + :parents ["A" "B"]}]]) + +;; Note the following differences: +;; +;; **In JSXGraph:** +;; +;; You create a board by calling `initBoard` with the id of some existing `div` +;; and a map of options. +;; +;; New elements are added to a board by calling `board.create( +;; )`, where +;; +;; - ``: the lowercase version of one of the classes in the [API +;; Reference](https://jsxgraph.org/docs/index.html) +;; +;; - ``: an array of the elements described under each class's "Element +;; Detail" +;; section. (A [Circle](https://jsxgraph.org/docs/symbols/Circle.html), for +;; example, takes two parents: a `Point` for its center, and either a `Point`, +;; `Line` or `Circle` for its radial point.) +;; +;; - ``: a map of attributes described under the class's "Attributes +;; Summary" section. +;; +;; **In JSXGraph.cljs** +;; +;; You create a board by providing a vector of the form `[jsx/JSXGraph +;; ]`. The component creates its own `div`. If you want to +;; customize the div, provide `:id` or `:style` keys. +;; +;; To add an element, add a vector of the following form as a child of the +;; `JSXGraph` component: +;; +;; ``` +;; [ {:parents , }] +;; ``` +;; +;; Where `` is the keyword form of the classname (`:circle`, +;; `:functiongraph` etc), `` are the parents described above and +;; `` are any other key-value pairs. +;; +;; ### Keyword vs Component +;; +;; All classes the [JSXGraph API](https://jsxgraph.org/docs/index.html) are +;; exposed as components +;; in [`jsxgraph.core`](https://github.com/mentat-collective/jsxgraph.cljs/blob/main/src/jsxgraph/core.cljs#L149). +;; You can use these components directly in place of the keyword in the example +;; above: + +(cljs + [jsx/JSXGraph {:boundingbox [-5 5 5 -2] :showCopyright false} + [jsx/Point {:name "A" :size 4 :parents [-1 1]}] + [jsx/Point {:name "B" :size 4 :parents [2 -1]}] + [jsx/Line {:strokeColor "#00ff00" :strokeWidth 2 + :parents ["A" "B"]}]]) + +;; ### Imperative Style +;; +;; If you prefer the more imperative style of the original `JSXGraph` library, +;; or if you need immediate access to an element after you add it to the board, +;; you can provide a callback to the `jsx/JSXGraph` component using the `:ref` +;; key and get access to the `board` object. +;; +;; Use `jsxgraph.core/create` just like you'd use `board.create` in JavaScript +;; to add elements: + +(cljs + [jsx/JSXGraph + {:boundingbox [-5 5 5 -2] + :style {:height "200px" :width "100%"} + :ref (fn [b] + ;; `b` will be `nil` when the board is first created. After the board + ;; mounts itself, the `:ref` callback will be called again with the + ;; `JSXGraph` instance. + (when b + (let [p1 (jsx/create b "point" [0 0] {:name "A" :size 4 :face "o"}) + p2 (jsx/create b "point" [2 -1] {:name "B" :size 4 :face "o"})] + (jsx/create b "line" + ;; Note that we have the actual point instances here and + ;; can add them, instead of relying on the string ID. + [p1 p2] + {:strokeColor "#00ff00" :strokeWidth 2}))))}]) + +;; ### Accessing Element Values +;; +;; A big difference between the `JSXGraph.cljs` and `JSXGraph` versions has to +;; do with how data is communicated. In JS, you typically create elements and +;; then query them on demand from other elements. +;; +;; In `JSXGraph.cljs`, elements communicate via updates to a shared `atom` or +;; `reagent/atom`. Many of the examples in the [Guides](#Guides) and [Advanced +;; Examples](#Advanced%20Examples) above use this style, so please study these +;; carefully. +;; +;; > See [Reagent's guide to +;; > state](https://github.com/reagent-project/reagent/blob/master/doc/ManagingState.md) +;; > for more detail. + +;; ## Thanks and Support + +;; To support this work and my other open source projects, consider sponsoring +;; me via my [GitHub Sponsors page](https://github.com/sponsors/sritchie). Thank +;; you to my current sponsors! + +;; I'm grateful to [Clojurists Together](https://www.clojuriststogether.org/) +;; for financial support during this library's creation. Please +;; consider [becoming a member](https://www.clojuriststogether.org/developers/) +;; to support this work and projects like it. +;; +;; For more information on me and my work, visit https://samritchie.io. + +;; ## License + +;; Copyright © 2022 Sam Ritchie. + +;; Distributed under the [MIT +;; License](https://github.com/mentat-collective/jsxgraph.cljs/blob/main/LICENSE). +;; See [LICENSE](https://github.com/mentat-collective/jsxgraph.cljs/blob/main/LICENSE). diff --git a/dev/user.clj b/dev/user.clj index c516ac2..e67d717 100644 --- a/dev/user.clj +++ b/dev/user.clj @@ -1,61 +1,20 @@ (ns user - (:require [clojure.string :as str] - [nextjournal.clerk :as clerk] - [nextjournal.clerk.dev-launcher :as launcher] - [nextjournal.clerk.viewer])) + (:require [nextjournal.clerk :as clerk] + [nextjournal.clerk.config :as config] + [nextjournal.clerk.view])) -(defn start [] - (launcher/start +(defn start! [] + (swap! config/!resource->url merge {"/js/viewer.js" "http://localhost:8765/js/main.js"}) + (clerk/serve! {:browse? true - :out-path "public" - :watch-paths ["dev"] - :show-filter-fn #(str/includes? % "notebook") - :extra-namespaces '[jsxgraph.clerk-ui]})) - - -(comment - (shadow.cljs.devtools.api/stop-worker :viewer) - - (start) - - #_(do - ;; start a dev server on port 7999 - (defonce !server (atom nil)) - (some-> @!server future-cancel) - (reset! !server - (future - (sh "python" "-m" "SimpleHTTPServer" "7999" :dir "public/build")))) - - (clerk/clear-cache!)) - -#_#_#_(defn start! [] - (swap! config/!resource->url merge {"/js/viewer.js" "http://localhost:8765/js/main.js"}) - (clerk/serve! - {:browse? true - :watch-paths ["dev"]}) - (Thread/sleep 500) - (clerk/show! "dev/jsxgraph/notebook.clj")) + :watch-paths ["dev"]}) + (Thread/sleep 500) + (clerk/show! "dev/jsxgraph/notebook.clj")) (defn github-pages! [_] - ;; TODO this now defaults to a project page. Do we want to change this? - (swap! config/!resource->url merge {"/js/viewer.js" "/jsxgraph.cljs/js/main.js"}) + (swap! config/!resource->url merge {"/js/viewer.js" "/js/main.js"}) (clerk/build! {:index "dev/jsxgraph/notebook.clj" :bundle? false :browse? false :out-path "public"})) - -(defn publish-local! - ([] (publish-local! nil)) - ([_] - (swap! config/!resource->url merge {"/js/viewer.js" "/js/main.js"}) - (clerk/build! - {:index "dev/jsxgraph/notebook.clj" - :bundle? false - :browse? false - :out-path "public"}))) - -#_(comment - (start!) - (clerk/serve! {:browse? true}) - (publish-local!)) diff --git a/package-lock.json b/package-lock.json index faabd29..4ebf779 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jsxgraph.cljs", - "version": "0.0.1", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "jsxgraph.cljs", - "version": "0.0.1", + "version": "0.1.0", "dependencies": { "@mentatcollective/jsxgraph": "1.5.1" }, @@ -26,6 +26,7 @@ "d3-require": "^1.2.4", "emoji-regex": "^10.0.1", "framer-motion": "^6.2.8", + "gh-pages": "^3.2.3", "http-server": "^14.1.1", "katex": "^0.12.0", "lezer-clojure": "1.0.0-rc.2", @@ -35,8 +36,8 @@ "markdown-it-texmath": "0.9.1", "markdown-it-toc-done-right": "4.2.0", "punycode": "2.1.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "17.0.2", + "react-dom": "17.0.2", "shadow-cljs": "^2.17.8", "tailwindcss": "^3.2.2", "use-sync-external-store": "^1.2.0", @@ -200,30 +201,6 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "peer": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -479,6 +456,27 @@ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "dev": true }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -534,8 +532,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "peer": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -588,7 +585,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -872,11 +868,22 @@ "color-support": "bin.js" } }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "peer": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "node_modules/console-browserify": { "version": "1.2.0", @@ -1140,12 +1147,27 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, + "node_modules/email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==", + "dev": true + }, "node_modules/emoji-regex": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz", "integrity": "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==", "dev": true }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -1208,6 +1230,32 @@ "reusify": "^1.0.4" } }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dev": true, + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1220,6 +1268,36 @@ "node": ">=8" } }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -1270,6 +1348,20 @@ "tslib": "^2.1.0" } }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -1285,8 +1377,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "peer": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "node_modules/fsevents": { "version": "2.3.2", @@ -1342,11 +1433,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gh-pages": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", + "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "dev": true, + "dependencies": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + }, + "bin": { + "gh-pages": "bin/gh-pages.js", + "gh-pages-clean": "bin/gh-pages-clean.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1374,6 +1486,28 @@ "node": ">=10.13.0" } }, + "node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1591,7 +1725,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "peer": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1677,6 +1810,15 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/katex": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/katex/-/katex-0.12.0.tgz", @@ -1689,12 +1831,6 @@ "katex": "cli.js" } }, - "node_modules/katex/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, "node_modules/lezer-clojure": { "version": "1.0.0-rc.2", "resolved": "https://registry.npmjs.org/lezer-clojure/-/lezer-clojure-1.0.0-rc.2.tgz", @@ -1722,6 +1858,18 @@ "uc.micro": "^1.0.1" } }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -1770,6 +1918,20 @@ "node": ">=10" } }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/markdown-it": { "version": "12.2.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.2.0.tgz", @@ -1924,7 +2086,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2157,7 +2318,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "peer": true, "dependencies": { "wrappy": "1" } @@ -2177,6 +2337,42 @@ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "dev": true }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -2201,11 +2397,19 @@ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2259,6 +2463,39 @@ "node": ">=0.10.0" } }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/popmotion": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", @@ -2563,28 +2800,30 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", "dev": true, "dependencies": { - "loose-envify": "^1.1.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", "dev": true, "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "17.0.2" } }, "node_modules/read-cache": { @@ -2724,12 +2963,13 @@ "dev": true }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", "dev": true, "dependencies": { - "loose-envify": "^1.1.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" } }, "node_modules/secure-compare": { @@ -2738,6 +2978,14 @@ "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", "dev": true }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -3042,6 +3290,18 @@ "node": ">=8" } }, + "node_modules/strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/style-mod": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz", @@ -3176,6 +3436,18 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "peer": true }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -3206,6 +3478,15 @@ "node": ">= 0.8.0" } }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", @@ -3309,8 +3590,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "peer": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "node_modules/xtend": { "version": "4.0.2", @@ -3483,23 +3763,6 @@ "tar": "^6.1.11" }, "dependencies": { - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "peer": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "peer": true - } - } - }, "semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -3706,6 +3969,21 @@ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "dev": true }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true + }, "asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -3765,8 +4043,7 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "peer": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-js": { "version": "1.5.1", @@ -3799,7 +4076,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "peer": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4033,11 +4309,22 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "peer": true }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "peer": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-browserify": { "version": "1.2.0", @@ -4268,12 +4555,24 @@ } } }, + "email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==", + "dev": true + }, "emoji-regex": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz", "integrity": "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==", "dev": true }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -4329,6 +4628,23 @@ "reusify": "^1.0.4" } }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", + "dev": true + }, + "filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dev": true, + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -4338,6 +4654,27 @@ "to-regex-range": "^5.0.1" } }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, "follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -4368,6 +4705,17 @@ "tslib": "^2.1.0" } }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -4380,8 +4728,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "peer": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.3.2", @@ -4424,11 +4771,25 @@ "has-symbols": "^1.0.3" } }, + "gh-pages": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", + "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "dev": true, + "requires": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + } + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "peer": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4447,6 +4808,25 @@ "is-glob": "^4.0.3" } }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4602,7 +4982,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "peer": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -4670,6 +5049,15 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "katex": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/katex/-/katex-0.12.0.tgz", @@ -4677,14 +5065,6 @@ "dev": true, "requires": { "commander": "^2.19.0" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } } }, "lezer-clojure": { @@ -4711,6 +5091,15 @@ "uc.micro": "^1.0.1" } }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -4753,6 +5142,14 @@ "yallist": "^4.0.0" } }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + } + }, "markdown-it": { "version": "12.2.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.2.0.tgz", @@ -4883,7 +5280,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "peer": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5068,7 +5464,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "peer": true, "requires": { "wrappy": "1" } @@ -5085,6 +5480,30 @@ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "dev": true }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -5109,11 +5528,16 @@ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "peer": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-parse": { "version": "1.0.7", @@ -5152,6 +5576,30 @@ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, "popmotion": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", @@ -5354,22 +5802,24 @@ } }, "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", "dev": true, "requires": { - "loose-envify": "^1.1.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" } }, "react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", "dev": true, "requires": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" } }, "read-cache": { @@ -5470,12 +5920,13 @@ "dev": true }, "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", "dev": true, "requires": { - "loose-envify": "^1.1.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" } }, "secure-compare": { @@ -5484,6 +5935,11 @@ "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", "dev": true }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -5729,6 +6185,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "style-mod": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz", @@ -5835,6 +6300,15 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "peer": true }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -5862,6 +6336,12 @@ "qs": "^6.4.0" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", @@ -5964,8 +6444,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "peer": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xtend": { "version": "4.0.2", diff --git a/package.json b/package.json index 3174eec..ccbcbe3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jsxgraph.cljs", - "version": "0.0.1", + "version": "0.1.0", "dependencies": { "@mentatcollective/jsxgraph": "1.5.1" }, @@ -20,6 +20,7 @@ "d3-require": "^1.2.4", "emoji-regex": "^10.0.1", "framer-motion": "^6.2.8", + "gh-pages": "^3.2.3", "http-server": "^14.1.1", "katex": "^0.12.0", "lezer-clojure": "1.0.0-rc.2", @@ -29,16 +30,17 @@ "markdown-it-texmath": "0.9.1", "markdown-it-toc-done-right": "4.2.0", "punycode": "2.1.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "17.0.2", + "react-dom": "17.0.2", "shadow-cljs": "^2.17.8", "tailwindcss": "^3.2.2", "use-sync-external-store": "^1.2.0", "w3c-keyname": "2.2.4" }, - "homepage": "https://mentat-collective.github.io/jsxgraph.cljs", + "homepage": "https://jsxgraph.mentat.org", "scripts": { - "serve": "http-server -c-1", + "serve": "http-server -c-1 -o", + "gh-pages": "gh-pages -d public", "watch-clerk": "shadow-cljs watch clerk", "release-clerk": "shadow-cljs release clerk" } diff --git a/shadow-cljs.edn b/shadow-cljs.edn index 0dc297a..9f4a640 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -1,4 +1,4 @@ -{:deps {:aliases [:dev :clerk]} +{:deps {:aliases [:dev :nextjournal/clerk]} :dev-http {8765 "public"} :builds {:clerk @@ -11,6 +11,8 @@ jsxgraph.clerk-ui]}} :output-dir "public/js" :asset-path "/js" + :build-options + {:ns-aliases {nextjournal.devcards nextjournal.devcards-noop}} :compiler-options {:infer-externs :auto :optimizations :advanced}}}} diff --git a/src/jsxgraph/core.cljs b/src/jsxgraph/core.cljs index 772aec7..fe7235f 100644 --- a/src/jsxgraph/core.cljs +++ b/src/jsxgraph/core.cljs @@ -1,272 +1,1340 @@ (ns jsxgraph.core - "Eventually this will contain wrapper forms for jsxgraph. For now ignore the - reactish part and just blow away the full jsxgraph whenever the input - change. + "React/Reagent implementation of a component that exposes the API from + http://jsxgraph.org/ in a more declarative way." + (:require ["@mentatcollective/jsxgraph$default" :as jsx] + [reagent.core :as re] + ["react" :as react])) - This is currently using my fork of jsxgraph at - https://github.com/sritchie/jsxgraph and published to - https://www.npmjs.com/package/@mentatcollective/jsxgraph, but these changes - should appear upstream soon and we can back off to the official library." - (:require ["@mentatcollective/jsxgraph" :default jsx] - [reagent.core :as re :include-macros true])) +;; ## Utilities -;; Utilities +(defonce ^{:no-doc true + :doc "We use this context like a dynamic variable, to provide each + component with a `board` instance that's accessible without passing + the board down via `props`."} + board-context + (react/createContext nil)) -(defn to-fixed [x p] - (.toFixed jsx x p)) +(def ^{:no-doc true + :doc "Use like -;; TODO extract various event handlers and attached -;; -;; TODO probably ALSO switch the order so that props always come first, THEN parents. +```clj +[:> Provider {:value } ] +``` + +to provide any element in `` with access to the bound `board` instance + via `(react/useContext board-context)`."} + Provider + (.-Provider board-context)) + +(defn create + "Similar + to [JXG.Board#create](https://jsxgraph.org/docs/symbols/JXG.Board.html#create), + with two additional features: + + - `parents` and `attributes` and can be written in Clojure + - `attributes` takes an optional `\"on\"` attribute. + + This `\"on\"` map requires kv pairs of type `{ }`. + After element creation, each of these handlers is registered with the new + element via `element.on`. + + Returns the newly created element." + [board element-type parents attributes] + (let [p (.create board + element-type + (clj->js parents) + (clj->js + (dissoc attributes "on")))] + (when-let [m (attributes "on")] + (doseq [[k f] m] + (let [callback (fn [_] + (this-as elem + (f elem)))] + (if (= k "update") + (if-let [coords (.-coords p)] + (.on coords "update" callback) + (.on board "update" callback)) + (.on p k callback))))) + p)) ;; Components +(defn ^:no-doc element + "Given a string `element-type`, returns a Reagent component that will attempt to + mount itself into the board stored in [[board-context]]." + [element-type] + (re/adapt-react-class + (react/forwardRef + (fn [props ref] + (let [board (react/useContext board-context) + {:strs [parents] :as props} (js->clj props) + props (dissoc props "parents")] + + (when-not board + (js/console.error + (str "Warning: instance of type " element-type + " must live under a `JSXGraph` component! " + "Otherwise nothing can mount."))) + + (when-not parents + (throw + (js/Error. + (str "Error: instance of type " + element-type + " is missing a `:parents` property.")))) + + (react/useEffect + (fn mount [] + ;; sometimes a stale dead board is passed in; in this case the + ;; `-renderer` property is nil, so we guard against that. + (when (and board (.-renderer board)) + (let [item (create board element-type parents props)] + (when ref (ref item)) + (fn unmount [] + (when board (.removeObject board item)) + (when ref (ref nil))))))) + nil))))) + +;; ## Elements + +(def ^{:no-doc true + :doc "Map of element type to the actual Reagent component representing + that type."} + k->elem + {"angle" (element "angle") + "arc" (element "arc") + "arrow" (element "arrow") + "arrowparallel" (element "arrowparallel") + "axis" (element "axis") + "bisector" (element "bisector") + "bisectorlines" (element "bisectorlines") + "boxplot" (element "boxplot") + "button" (element "button") + "cardinalspline" (element "cardinalspline") + "chart" (element "chart") + "checkbox" (element "checkbox") + "circle" (element "circle") + "circumcenter" (element "circumcenter") + "circumcircle" (element "circumcircle") + "circumcirclearc" (element "circumcirclearc") + "circumcirclesector" (element "circumcirclesector") + "comb" (element "comb") + "conic" (element "conic") + "curve" (element "curve") + "Curve3d" (element "Curve3d") + "curvedifference" (element "curvedifference") + "curveintersection" (element "curveintersection") + "curveunion" (element "curveunion") + "derivative" (element "derivative") + "ellipse" (element "ellipse") + "foreignobject" (element "foreignobject") + "functiongraph" (element "functiongraph") + "functiongraph3d" (element "functiongraph3d") + "glider" (element "glider") + "grid" (element "grid") + "group" (element "group") + "hatch" (element "hatch") + "hyperbola" (element "hyperbola") + "image" (element "image") + "incenter" (element "incenter") + "incircle" (element "incircle") + "inequality" (element "inequality") + "input" (element "input") + "integral" (element "integral") + "intersection" (element "intersection") + "label" (element "label") + "legend" (element "legend") + "line" (element "line") + "Line3d" (element "Line3d") + "locus" (element "locus") + "majorarc" (element "majorarc") + "majorsector" (element "majorsector") + "metapostspline" (element "metapostspline") + "midpoint" (element "midpoint") + "minorarc" (element "minorarc") + "minorsector" (element "minorsector") + "mirrorelement" (element "mirrorelement") + "mirrorpoint" (element "mirrorpoint") + "nonreflexangle" (element "nonreflexangle") + "normal" (element "normal") + "orthogonalprojection" (element "orthogonalprojection") + "otherintersection" (element "otherintersection") + "parabola" (element "parabola") + "parallel" (element "parallel") + "parallelpoint" (element "parallelpoint") + "parametricsurface3d" (element "parametricsurface3d") + "perpendicular" (element "perpendicular") + "perpendicularpoint" (element "perpendicularpoint") + "perpendicularsegment" (element "perpendicularsegment") + "point" (element "point") + "point3d" (element "point3d") + "polarline" (element "polarline") + "polepoint" (element "polepoint") + "polygon" (element "polygon") + "polygonalchain" (element "polygonalchain") + "radicalaxis" (element "radicalaxis") + "reflection" (element "reflection") + "reflexangle" (element "reflexangle") + "regularpolygon" (element "regularpolygon") + "riemannsum" (element "riemannsum") + "sector" (element "sector") + "segment" (element "segment") + "semicircle" (element "semicircle") + "slider" (element "slider") + "slopetriangle" (element "slopetriangle") + "spline" (element "spline") + "stepfunction" (element "stepfunction") + "tangent" (element "tangent") + "tapemeasure" (element "tapemeasure") + "text" (element "text") + "ticks" (element "ticks") + "tracecurve" (element "tracecurve") + "transformation" (element "transformation") + "turtle" (element "turtle") + "view3d" (element "view3d")}) +;; +;; see `ElementType` in index.d.ts for the full list of valid elements. + +(def ^{:doc "Reagent component representing + the [Angle](https://jsxgraph.org/docs/symbols/Angle.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Angle.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Angle + (k->elem "angle")) + +(def ^{:doc "Reagent component representing + the [Arc](https://jsxgraph.org/docs/symbols/Arc.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Arc.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Arc + (k->elem "arc")) + +(def ^{:doc "Reagent component representing + the [Arrow](https://jsxgraph.org/docs/symbols/Arrow.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Arrow.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Arrow + (k->elem "arrow")) + +(def ^{:doc "Reagent component representing + the [ArrowParallel](https://jsxgraph.org/docs/symbols/ArrowParallel.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/ArrowParallel.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + ArrowParallel + (k->elem "arrowparallel")) + +(def ^{:doc "Reagent component representing + the [Axis](https://jsxgraph.org/docs/symbols/Axis.html) JSXGraph element. This + component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Axis.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Axis + (k->elem "axis")) + +(def ^{:doc "Reagent component representing + the [Bisector](https://jsxgraph.org/docs/symbols/Bisector.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Bisector.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + Bisector + (k->elem "bisector")) + +(def ^{:doc "Reagent component representing + the [Bisectorlines](https://jsxgraph.org/docs/symbols/Bisectorlines.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Bisectorlines.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Bisectorlines + (k->elem "bisectorlines")) + +(def ^{:doc "Reagent component representing + the [Boxplot](https://jsxgraph.org/docs/symbols/Boxplot.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Boxplot.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + + Boxplot + (k->elem "boxplot")) + +(def ^{:doc "Reagent component representing + the [Button](https://jsxgraph.org/docs/symbols/Button.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Button.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Button + (k->elem "button")) + +(def ^{:doc "Reagent component representing + the [CardinalSpline](https://jsxgraph.org/docs/symbols/CardinalSpline.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/CardinalSpline.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + CardinalSpline + (k->elem "cardinalspline")) + +(def ^{:doc "Reagent component representing + the [Chart](https://jsxgraph.org/docs/symbols/Chart.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Chart.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Chart + (k->elem "chart")) + +(def ^{:doc "Reagent component representing + the [Checkbox](https://jsxgraph.org/docs/symbols/Checkbox.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Checkbox.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + Checkbox + (k->elem "checkbox")) + +(def ^{:doc "Reagent component representing + the [Circle](https://jsxgraph.org/docs/symbols/Circle.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Circle.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Circle + (k->elem "circle")) + +(def ^{:doc "Reagent component representing + the [Circumcenter](https://jsxgraph.org/docs/symbols/Circumcenter.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Circumcenter.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + Circumcenter + (k->elem "circumcenter")) + +(def ^{:doc "Reagent component representing + the [Circumcircle](https://jsxgraph.org/docs/symbols/Circumcircle.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Circumcircle.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + Circumcircle + (k->elem "circumcircle")) + +(def ^{:doc "Reagent component representing + the [CircumcircleArc](https://jsxgraph.org/docs/symbols/CircumcircleArc.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/CircumcircleArc.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + CircumcircleArc + (k->elem "circumcirclearc")) + +(def ^{:doc "Reagent component representing + the [CircumcircleSector](https://jsxgraph.org/docs/symbols/CircumcircleSector.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/CircumcircleSector.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + CircumcircleSector + (k->elem "circumcirclesector")) + +(def ^{:doc "Reagent component representing + the [Comb](https://jsxgraph.org/docs/symbols/Comb.html) JSXGraph element. This + component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Comb.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Comb + (k->elem "comb")) + +(def ^{:doc "Reagent component representing + the [Conic](https://jsxgraph.org/docs/symbols/Conic.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Conic.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Conic + (k->elem "conic")) + +(def ^{:doc "Reagent component representing + the [Curve](https://jsxgraph.org/docs/symbols/Curve.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Curve.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Curve + (k->elem "curve")) + +(def ^{:doc "Reagent component representing + the [CurveDifference](https://jsxgraph.org/docs/symbols/CurveDifference.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/CurveDifference.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + CurveDifference + (k->elem "curvedifference")) + +(def ^{:doc "Reagent component representing + the [CurveIntersection](https://jsxgraph.org/docs/symbols/CurveIntersection.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/CurveIntersection.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + CurveIntersection + (k->elem "curveintersection")) + +(def ^{:doc "Reagent component representing + the [CurveUnion](https://jsxgraph.org/docs/symbols/CurveUnion.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/CurveUnion.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + CurveUnion + (k->elem "curveunion")) + +(def ^{:doc "Reagent component representing + the [Derivative](https://jsxgraph.org/docs/symbols/Derivative.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Derivative.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Derivative + (k->elem "derivative")) + +(def ^{:doc "Reagent component representing + the [Ellipse](https://jsxgraph.org/docs/symbols/Ellipse.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Ellipse.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + + Ellipse + (k->elem "ellipse")) + +(def ^{:doc "Reagent component representing + the [ForeignObject](https://jsxgraph.org/docs/symbols/ForeignObject.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/ForeignObject.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + ForeignObject + (k->elem "foreignobject")) + +(def ^{:doc "Reagent component representing + the [FunctionGraph](https://jsxgraph.org/docs/symbols/FunctionGraph.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/FunctionGraph.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + FunctionGraph + (k->elem "functiongraph")) + +(def ^{:doc "Reagent component representing + the [Glider](https://jsxgraph.org/docs/symbols/Glider.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Glider.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + + Glider + (k->elem "glider")) + +(def ^{:doc "Reagent component representing + the [Grid](https://jsxgraph.org/docs/symbols/Grid.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Grid.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Grid + (k->elem "grid")) + +(def ^{:doc "Reagent component representing + the [Group](https://jsxgraph.org/docs/symbols/Group.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Group.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Group + (k->elem "group")) + +(def ^{:doc "Reagent component representing + the [Hatch](https://jsxgraph.org/docs/symbols/Hatch.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Hatch.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Hatch + (k->elem "hatch")) + +(def ^{:doc "Reagent component representing + the [Hyperbola](https://jsxgraph.org/docs/symbols/Hyperbola.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Hyperbola.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + Hyperbola + (k->elem "hyperbola")) + +(def ^{:doc "Reagent component representing + the [Image](https://jsxgraph.org/docs/symbols/Image.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Image.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Image + (k->elem "image")) + +(def ^{:doc "Reagent component representing + the [Incenter](https://jsxgraph.org/docs/symbols/Incenter.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Incenter.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + Incenter + (k->elem "incenter")) + +(def ^{:doc "Reagent component representing + the [Incircle](https://jsxgraph.org/docs/symbols/Incircle.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Incircle.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Incircle + (k->elem "incircle")) + +(def ^{:doc "Reagent component representing + the [Inequality](https://jsxgraph.org/docs/symbols/Inequality.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Inequality.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + Inequality + (k->elem "inequality")) + +(def ^{:doc "Reagent component representing + the [Input](https://jsxgraph.org/docs/symbols/Input.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Input.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Input + (k->elem "input")) + +(def ^{:doc "Reagent component representing + the [Integral](https://jsxgraph.org/docs/symbols/Integral.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Integral.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + Integral + (k->elem "integral")) + +(def ^{:doc "Reagent component representing + the [Intersection](https://jsxgraph.org/docs/symbols/Intersection.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Intersection.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Intersection + (k->elem "intersection")) + +(def ^{:doc "Reagent component representing + the [Label](https://jsxgraph.org/docs/symbols/Label.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Label.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Label + (k->elem "label")) + +(def ^{:doc "Reagent component representing + the [Legend](https://jsxgraph.org/docs/symbols/Legend.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Legend.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + + Legend + (k->elem "legend")) + +(def ^{:doc "Reagent component representing + the [Line](https://jsxgraph.org/docs/symbols/Line.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Line.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Line + (k->elem "line")) + +(def ^{:doc "Reagent component representing + the [Locus](https://jsxgraph.org/docs/symbols/Locus.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Locus.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Locus + (k->elem "locus")) + +(def ^{:doc "Reagent component representing + the [MajorArc](https://jsxgraph.org/docs/symbols/MajorArc.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/MajorArc.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + MajorArc + (k->elem "majorarc")) + +(def ^{:doc "Reagent component representing + the [MajorSector](https://jsxgraph.org/docs/symbols/MajorSector.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/MajorSector.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + MajorSector + (k->elem "majorsector")) + +(def ^{:doc "Reagent component representing + the [MetapostSpline](https://jsxgraph.org/docs/symbols/MetapostSpline.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/MetapostSpline.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + MetapostSpline + (k->elem "metapostspline")) + +(def ^{:doc "Reagent component representing + the [Midpoint](https://jsxgraph.org/docs/symbols/Midpoint.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Midpoint.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + Midpoint + (k->elem "midpoint")) + +(def ^{:doc "Reagent component representing + the [MinorArc](https://jsxgraph.org/docs/symbols/MinorArc.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/MinorArc.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + MinorArc + (k->elem "minorarc")) + +(def ^{:doc "Reagent component representing + the [MinorSector](https://jsxgraph.org/docs/symbols/MinorSector.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/MinorSector.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + MinorSector + (k->elem "minorsector")) + +(def ^{:doc "Reagent component representing + the [MirrorElement](https://jsxgraph.org/docs/symbols/MirrorElement.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/MirrorElement.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + MirrorElement + (k->elem "mirrorelement")) + +(def ^{:doc "Reagent component representing + the [MirrorPoint](https://jsxgraph.org/docs/symbols/MirrorPoint.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/MirrorPoint.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + MirrorPoint + (k->elem "mirrorpoint")) + +(def ^{:doc "Reagent component representing + the [NonReflexAngle](https://jsxgraph.org/docs/symbols/NonReflexAngle.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/NonReflexAngle.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + NonReflexAngle + (k->elem "nonreflexangle")) + +(def ^{:doc "Reagent component representing + the [Normal](https://jsxgraph.org/docs/symbols/Normal.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Normal.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + + Normal + (k->elem "normal")) + +(def ^{:doc "Reagent component representing + the [OrthogonalProjection](https://jsxgraph.org/docs/symbols/OrthogonalProjection.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/OrthogonalProjection.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + OrthogonalProjection + (k->elem "orthogonalprojection")) + +(def ^{:doc "Reagent component representing + the [OtherIntersection](https://jsxgraph.org/docs/symbols/OtherIntersection.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/OtherIntersection.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + OtherIntersection + (k->elem "otherintersection")) + +(def ^{:doc "Reagent component representing + the [Parabola](https://jsxgraph.org/docs/symbols/Parabola.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Parabola.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + Parabola + (k->elem "parabola")) + +(def ^{:doc "Reagent component representing + the [Parallel](https://jsxgraph.org/docs/symbols/Parallel.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Parallel.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Parallel + (k->elem "parallel")) + +(def ^{:doc "Reagent component representing + the [ParallelPoint](https://jsxgraph.org/docs/symbols/ParallelPoint.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/ParallelPoint.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + ParallelPoint + (k->elem "parallelpoint")) + +(def ^{:doc "Reagent component representing + the [Perpendicular](https://jsxgraph.org/docs/symbols/Perpendicular.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Perpendicular.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + Perpendicular + (k->elem "perpendicular")) + +(def ^{:doc "Reagent component representing + the [PerpendicularPoint](https://jsxgraph.org/docs/symbols/PerpendicularPoint.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/PerpendicularPoint.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + PerpendicularPoint + (k->elem "perpendicularpoint")) + +(def ^{:doc "Reagent component representing + the [PerpendicularSegment](https://jsxgraph.org/docs/symbols/PerpendicularSegment.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/PerpendicularSegment.html), the map + must contain an entry `:parents` with value containing the element's required + parents."} + PerpendicularSegment + (k->elem "perpendicularsegment")) + +(def ^{:doc "Reagent component representing + the [Point](https://jsxgraph.org/docs/symbols/Point.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Point.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Point + (k->elem "point")) + +(def ^{:doc "Reagent component representing + the [PolarLine](https://jsxgraph.org/docs/symbols/PolarLine.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/PolarLine.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + PolarLine + (k->elem "polarline")) + +(def ^{:doc "Reagent component representing + the [PolePoint](https://jsxgraph.org/docs/symbols/PolePoint.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/PolePoint.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + PolePoint + (k->elem "polepoint")) + +(def ^{:doc "Reagent component representing + the [Polygon](https://jsxgraph.org/docs/symbols/Polygon.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Polygon.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + + Polygon + (k->elem "polygon")) + +(def ^{:doc "Reagent component representing + the [PolygonalChain](https://jsxgraph.org/docs/symbols/PolygonalChain.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/PolygonalChain.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + PolygonalChain + (k->elem "polygonalchain")) + +(def ^{:doc "Reagent component representing + the [RadicalAxis](https://jsxgraph.org/docs/symbols/RadicalAxis.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/RadicalAxis.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + RadicalAxis + (k->elem "radicalaxis")) + +(def ^{:doc "Reagent component representing + the [Reflection](https://jsxgraph.org/docs/symbols/Reflection.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Reflection.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + Reflection + (k->elem "reflection")) + +(def ^{:doc "Reagent component representing + the [ReflexAngle](https://jsxgraph.org/docs/symbols/ReflexAngle.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/ReflexAngle.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + ReflexAngle + (k->elem "reflexangle")) + +(def ^{:doc "Reagent component representing + the [RegularPolygon](https://jsxgraph.org/docs/symbols/RegularPolygon.html) + JSXGraph element. This component must appear as a child of a [[JSXGraph]] + component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/RegularPolygon.html), the map must + contain an entry `:parents` with value containing the element's required + parents."} + RegularPolygon + (k->elem "regularpolygon")) + +(def ^{:doc "Reagent component representing + the [RiemannSum](https://jsxgraph.org/docs/symbols/RiemannSum.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/RiemannSum.html), the map must contain + an entry `:parents` with value containing the element's required parents."} + + RiemannSum + (k->elem "riemannsum")) + +(def ^{:doc "Reagent component representing + the [Sector](https://jsxgraph.org/docs/symbols/Sector.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Sector.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Sector + (k->elem "sector")) + +(def ^{:doc "Reagent component representing + the [Segment](https://jsxgraph.org/docs/symbols/Segment.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Segment.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + + Segment + (k->elem "segment")) + +(def ^{:doc "Reagent component representing + the [Semicircle](https://jsxgraph.org/docs/symbols/Semicircle.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Semicircle.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Semicircle + (k->elem "semicircle")) + +(def ^{:doc "Reagent component representing + the [Slider](https://jsxgraph.org/docs/symbols/Slider.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Slider.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + + Slider + (k->elem "slider")) + +(def ^{:doc "Reagent component representing + the [SlopeTriangle](https://jsxgraph.org/docs/symbols/SlopeTriangle.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/SlopeTriangle.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + SlopeTriangle + (k->elem "slopetriangle")) + +(def ^{:doc "Reagent component representing + the [Spline](https://jsxgraph.org/docs/symbols/Spline.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Spline.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + + Spline + (k->elem "spline")) + +(def ^{:doc "Reagent component representing + the [StepFunction](https://jsxgraph.org/docs/symbols/StepFunction.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/StepFunction.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + StepFunction + (k->elem "stepfunction")) + +(def ^{:doc "Reagent component representing + the [Tangent](https://jsxgraph.org/docs/symbols/Tangent.html) JSXGraph + element. This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Tangent.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Tangent + (k->elem "tangent")) + +(def ^{:doc "Reagent component representing + the [TapeMeasure](https://jsxgraph.org/docs/symbols/TapeMeasure.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/TapeMeasure.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + TapeMeasure + (k->elem "tapemeasure")) + +(def ^{:doc "Reagent component representing + the [Text](https://jsxgraph.org/docs/symbols/Text.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Text.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Text + (k->elem "text")) + +(def ^{:doc "Reagent component representing + the [Ticks](https://jsxgraph.org/docs/symbols/Ticks.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Ticks.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Ticks + (k->elem "ticks")) + +(def ^{:doc "Reagent component representing + the [TraceCurve](https://jsxgraph.org/docs/symbols/TraceCurve.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/TraceCurve.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + TraceCurve + (k->elem "tracecurve")) + +(def ^{:doc "Reagent component representing + the [Transformation](https://jsxgraph.org/docs/symbols/Transformation.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Transformation.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Transformation + (k->elem "transformation")) + +(def ^{:doc "Reagent component representing + the [Turtle](https://jsxgraph.org/docs/symbols/Turtle.html) JSXGraph element. + This component must appear as a child of a [[JSXGraph]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Turtle.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Turtle + (k->elem "turtle")) + +;; ## 3D Components + +(def ^{:doc "Reagent component representing + the [Curve3D](https://jsxgraph.org/docs/symbols/Curve3D.html) JSXGraph element. + This component must appear as a child of a [[View3D]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Curve3D.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Curve3D + (k->elem "Curve3d")) + +(def ^{:doc "Reagent component representing + the [FunctionGraph3D](https://jsxgraph.org/docs/symbols/FunctionGraph3D.html) JSXGraph element. + This component must appear as a child of a [[View3D]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/FunctionGraph3D.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + FunctionGraph3D + (k->elem "functiongraph3d")) + +(def ^{:doc "Reagent component representing + the [Line3D](https://jsxgraph.org/docs/symbols/Line3D.html) JSXGraph element. + This component must appear as a child of a [[View3D]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Line3D.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Line3D + (k->elem "Line3d")) + +(def ^{:doc "Reagent component representing + the [ParametricSurface3D](https://jsxgraph.org/docs/symbols/ParametricSurface3D.html) JSXGraph element. + This component must appear as a child of a [[View3D]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/ParametricSurface3D.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + ParametricSurface3D + (k->elem "parametricsurface3d")) + +(def ^{:doc "Reagent component representing + the [Point3D](https://jsxgraph.org/docs/symbols/Point3D.html) JSXGraph element. + This component must appear as a child of a [[View3D]] component. + + The element takes a single map of attributes. In addition to the optional + attributes specified in the [API + docs](https://jsxgraph.org/docs/symbols/Point3D.html), the map must contain an + entry `:parents` with value containing the element's required parents."} + Point3D + (k->elem "point3d")) + +(defn ^:no-doc k->component [k] + (if (or (string? k) (keyword? k)) + (k->elem (name k)) + k)) + +;; ## JSXGraph Component + (defn JSXGraph - "TODO note that you can either add children etc... OR you can supply a ref that - just does all of this crap for you. + "Top-level Reagent component used to represent a JSXGraph board. Takes - TODO can I have a component that just messes with the board itself?" + - a `keyword => value` map of attributes (see the `attributes` section under + `Parameters` at [this + page](https://jsxgraph.org/docs/symbols/JXG.JSXGraph.html)) for allowed values + + - Any number of child components representing `JSXGraph` elements. + + Child components are added to the board in the order that they're listed. A + full re-render is triggered any time any of the properties of the board or any + child component changes. + + Pass `:id` and/or `:style` to configure the `div` into which the controlled + instance of `JXG.Board` mounts itself. + + If you need access to the actual instance of + a [`JXG.Board`](https://jsxgraph.org/docs/symbols/JXG.Board.html), pass a + callback function using the `:ref` keyword. The `:ref` function receives an + instance of the board when it is mounted, and `nil` when the board is + destroyed or remounted." [{:keys [id style]} & _] - (let [!board (re/atom nil) - !force (atom 0) + (let [!board (re/atom nil) id (or id (-> (Math/random) (.toString 36) (.substr 2 9))) style (or style {:height "400px" :width "100%"}) - kill! (fn [board] + kill! (fn [board props] (.suspendUpdate board) (-> (.-JSXGraph jsx) (.freeBoard board)) + (when-let [ref (:ref props)] + (ref nil)) nil) init! (fn [props] (let [board (-> (.-JSXGraph jsx) (.initBoard id (clj->js props)))] - ;; initialize. (when-let [ref (:ref props)] (ref board)) board))] (re/create-class {:display-name "JSXGraph" - - ;;called after render. :component-did-mount (fn [this] - (js/console.log "board mounted") (reset! !board (init! (re/props this)))) :component-will-unmount - (fn [_this] - (js/console.log "bye bye board") - (swap! !board kill!)) + (fn [this] + (swap! !board kill! re/props this)) - ;; Update if the props change. Not so bad!! :component-did-update - (fn [this old-argv] - (js/console.log "board-did-update") - (let [old-props (let [p (second old-argv)] - (if (map? p) p {})) + (fn [this [_ p]] + (let [old-props (if (map? p) p {}) new-props (or (re/props this) {})] - (when-not (= old-props new-props) - (js/console.log (str "board resetting!")) + ;; Remount when the values of any properties change, otherwise don't. + (when (not= old-props new-props) (swap! !board (fn [old-board] - (when old-board (kill! old-board)) + (when old-board (kill! old-board new-props)) (init! new-props)))))) :reagent-render (fn [& _] - (let [this (re/current-component) - children (re/children this) - extras {:board @!board :force @!force}] - (js/console.log "rendering board") - ;; TODO note that this trick is forcing the children to re-render - ;; basically every single time. We are using react as a hack here :) - ;; - ;; But this means that, for now, you can't be updating this stuff with - ;; changing properties. You need to use a function that is going to - ;; access some state. That should be fine! - (swap! !force inc) - (into [:div {:id id :style style}] - (map - (fn [[a props & more]] - (if (map? props) - (into [a (into props extras)] more) - (into [a extras props] more)))) - children)))}))) - -(defn add-item! [name board elems props] - (let [p (.create board - name - (clj->js elems) - (clj->js (dissoc props :board)))] - ;; Okay, SO, we can definitely get updates. but we want to UNREGISTER these - ;; if we can when the element gets taken out of commission. - #_(if-let [coords (.-coords p)] - (.on coords "update" (fn [_] (js/console.log (str (pr-str props) name " coords update fired")))) - (.on board "update" (fn [_] (js/console.log (str (.getType p) " update fired"))))) - - ;; TODO what are the possible events for OTHER types?? - (when-let [f (props :on-drag)] - - ;; events can be - ;; drag, mousedrag, touchdrag - ;; move, mousemove, touchmove - ;; over, mouseover - ;; out, mouseout - ;; up, mouseup, touchend - ;; down, mousedown, touchstart - (.on p "drag" (fn [_] - (this-as point - (f point))))) - p)) - -(defn element [name] - (fn [_props _elems] - (let [!item (atom nil) - mount! - (fn [this _old-argv] - (let [[_ props elems] (re/argv this)] - (when-let [board (:board props)] - (swap! !item - (fn [item] - (when item - (.removeObject board item)) - (add-item! name board elems props))))))] - (re/create-class - {:display-name name - :component-did-mount mount! - :component-did-update mount! - :component-will-unmount - (fn [this] - (let [[_ props] (re/argv this)] - (when-let [board (:board props)] - (swap! !item - (fn [item] - (when item - (.removeObject board item)) - nil))))) - :reagent-render - (fn [_props _elems] nil)})))) - -;; ## Elements -;; -;; see `ElementType` in index.d.ts. - -(def Angle (element "angle")) -(def Arc (element "arc")) -(def Arrow (element "arrow")) -(def ArrowParallel (element "arrowparallel")) -(def Axis (element "axis")) -(def Bisector (element "bisector")) -(def Bisectorlines (element "bisectorlines")) -(def Boxplot (element "boxplot")) -(def Button (element "button")) -(def CardinalSpline (element "cardinalspline")) -(def Chart (element "chart")) -(def Checkbox (element "checkbox")) -(def Circle (element "circle")) -(def Circumcenter (element "circumcenter")) -(def Circumcircle (element "circumcircle")) -(def CircumcircleArc (element "circumcirclearc")) -(def CircumcircleSector (element "circumcirclesector")) -(def Comb (element "comb")) -(def Conic (element "conic")) -(def Curve (element "curve")) -(def Curve3D (element "Curve3d")) -(def CurveDifference (element "curvedifference")) -(def CurveIntersection (element "curveintersection")) -(def CurveUnion (element "curveunion")) -(def Derivative (element "derivative")) -(def Ellipse (element "ellipse")) -(def ForeignObject (element "foreignobject")) -(def FunctionGraph (element "functiongraph")) -(def FunctionGraph3D (element "functiongraph3d")) -(def Glider (element "glider")) -(def Grid (element "grid")) -(def Group (element "group")) -(def Hatch (element "hatch")) -(def Hyperbola (element "hyperbola")) -(def Image (element "image")) -(def Incenter (element "incenter")) -(def Incircle (element "incircle")) -(def Inequality (element "inequality")) -(def Input (element "input")) -(def Integral (element "integral")) -(def Intersection (element "intersection")) -(def Label (element "label")) -(def Legend (element "legend")) -(def Line (element "line")) -(def Line3D (element "Line3d")) -(def Locus (element "locus")) -(def MajorArc (element "majorarc")) -(def MajorSector (element "majorsector")) -(def MetapostSpline (element "metapostspline")) -(def Midpoint (element "midpoint")) -(def MinorArc (element "minorarc")) -(def MinorSector (element "minorsector")) -(def MirrorElement (element "mirrorelement")) -(def MirrorPoint (element "mirrorpoint")) -(def NonReflexAngle (element "nonreflexangle")) -(def Normal (element "normal")) -(def OrthogonalProjection (element "orthogonalprojection")) -(def OtherIntersection (element "otherintersection")) -(def Parabola (element "parabola")) -(def Parallel (element "parallel")) -(def ParallelPoint (element "parallelpoint")) -(def ParametricSurface3D (element "parametricsurface3d")) -(def Perpendicular (element "perpendicular")) -(def PerpendicularPoint (element "perpendicularpoint")) -(def PerpendicularSegment (element "perpendicularsegment")) -(def Point (element "point")) -(def Point3D (element "point3d")) -(def PolarLine (element "polarline")) -(def PolePoint (element "polepoint")) -(def Polygon (element "polygon")) -(def PolygonalChain (element "polygonalchain")) -(def RadicalAxis (element "radicalaxis")) -(def Reflection (element "reflection")) -(def ReflexAngle (element "reflexangle")) -(def RegularPolygon (element "regularpolygon")) -(def RiemannSum (element "riemannsum")) -(def Sector (element "sector")) -(def Segment (element "segment")) -(def Semicircle (element "semicircle")) -(def Slider (element "slider")) -(def SlopeTriangle (element "slopetriangle")) -(def Spline (element "spline")) -(def StepFunction (element "stepfunction")) -(def Tangent (element "tangent")) -(def TapeMeasure (element "tapemeasure")) -(def Text (element "text")) -(def Ticks (element "ticks")) -(def TraceCurve (element "tracecurve")) -(def Transformation (element "transformation")) -(def Turtle (element "turtle")) -(def View3D (element "view3D")) - -;; ## Extensions -;; -;; TODO handle :<> in the children update for the board?? otherwise we can't use -;; that without this trick. - -(defn Multi [{:keys [n] :or {n 1} :as m} i->c] - (letfn [(f [i] - (let [[component props & more] (i->c i)] - (into [component (into m props)] more)))] - (into [:<>] (map f) (range n)))) - -(defn PointLine - "Annoyingly, if you want to make NEW components that wrap the others, you need - to be careful about passing along props down the line. - - ``` - [jsx/PointLine {} 3] - ```" - [props x] - (letfn [(f [i] - [Point [(- i) i] (assoc props :name (str i) :strokecolor "red")])] - (into [:<>] (map f) (range x)))) - -;; TODO -;; -;; - add jsxgraph prefix to my special keywords like board, etc so they don't clash, counter too. -;; - document the madness -;; - rebuild one of the Sam Zhang essays using mathbox and jsxgraph. Get some! + (let [board @!board + base [:div {:id id :style style}]] + ;; On the first load, the board has not mounted itself yet, so ignore + ;; all children and mount the target div. This will trigger a `reset!` + ;; of `!board`, which will mount all child components. + (if-not board + base + [:div {:id id :style style} + (into [:> Provider {:value board}] + ;; TODO error if component not found! + (map (fn [form] + (when form + (let [[k & xs] form] + (if-let [c (k->component k)] + (into [c] xs) + (throw + (js/Error. + (str "Component " form " has " + "`nil` or an unrecognized string or " + "keyword in its first entry.")))))))) + (re/children + (re/current-component)))])))}))) diff --git a/template/pom.xml b/template/pom.xml index 251977b..85ff30a 100644 --- a/template/pom.xml +++ b/template/pom.xml @@ -7,7 +7,7 @@ jsxgraph.cljs jsxgraph.cljs Clojurescript / Reagent interface for JSXGraph. - http://github.com/mentat-collective/jsxgraph.cljs + https://jsxgraph.mentat.org MIT