From 59843f9e3d222901421a92fff793a1e031f38f65 Mon Sep 17 00:00:00 2001 From: Xia Li-yao Date: Wed, 27 Feb 2019 21:53:27 -0500 Subject: Menu item controlling which instances are expanded/collapsed (#1007) Adds a menu item (like "Quick Jump") for options related to displaying instances. This provides functionality for: * expanding/collapsing all instances on the currently opened page * controlling whether instances are expanded/collapsed by default * controlling whether the state of instances should be "remembered" This new functionality is implemented in Typescript in `details-helper`. The built-in-themes style switcher also got a revamp so that all three of QuickJump, the style switcher, and instance preferences now have the same style and implementation structure. See also: https://mail.haskell.org/pipermail/haskell-cafe/2019-January/130495.html Fixes #698. Co-authored-by: Lysxia Co-authored-by: Nathan Collins --- haddock-api/resources/html/js-src/style-menu.tsx | 177 ++++++++++++----------- 1 file changed, 93 insertions(+), 84 deletions(-) (limited to 'haddock-api/resources/html/js-src/style-menu.tsx') diff --git a/haddock-api/resources/html/js-src/style-menu.tsx b/haddock-api/resources/html/js-src/style-menu.tsx index bab840ca..2eb8344e 100644 --- a/haddock-api/resources/html/js-src/style-menu.tsx +++ b/haddock-api/resources/html/js-src/style-menu.tsx @@ -4,91 +4,14 @@ import {getCookie, setCookie, clearCookie} from "./cookies"; import preact = require("preact"); const { h, Component } = preact; -const rspace = /\s\s+/g, - rtrim = /^\s+|\s+$/g; - -function spaced(s: string) { return (" " + s + " ").replace(rspace, " "); } -function trim(s: string) { return s.replace(rtrim, ""); } - -function hasClass(elem: Element, value: string) { - const className = spaced(elem.className || ""); - return className.indexOf( " " + value + " " ) >= 0; -} - -function addClass(elem: Element, value: string) { - const className = spaced(elem.className || ""); - if ( className.indexOf( " " + value + " " ) < 0 ) { - elem.className = trim(className + " " + value); - } -} - -function removeClass(elem: Element, value: string) { - let className = spaced(elem.className || ""); - className = className.replace(" " + value + " ", " "); - elem.className = trim(className); -} - -function toggleClass(elem: Element, valueOn: string, valueOff: string, bool?: boolean): boolean { - if (bool == null) { bool = ! hasClass(elem, valueOn); } - if (bool) { - removeClass(elem, valueOff); - addClass(elem, valueOn); - } - else { - removeClass(elem, valueOn); - addClass(elem, valueOff); - } - return bool; -} - -function makeClassToggle(valueOn: string, valueOff: string): (elem: Element, bool?: boolean) => boolean { - return function(elem, bool) { - return toggleClass(elem, valueOn, valueOff, bool); - } -} - -const toggleShow = makeClassToggle("show", "hide"); +// Get all of the styles that are available function styles(): HTMLLinkElement[] { const es = Array.prototype.slice.call(document.getElementsByTagName("link")); return es.filter((a: HTMLLinkElement) => a.rel.indexOf("style") != -1 && a.title); } -class StyleMenuButton extends Component { - - render(props: { stys: string[] }) { - function action() { - styleMenu(); - return false; - }; - - return
  • - Style ▾ -
      - {props.stys.map((sty) => { - function action() { - setActiveStyleSheet(sty); - return false; - }; - - return
    • {sty}
    • ; - })} -
    -
  • ; - } - -} - -function addStyleMenu() { - const stys = styles().map((s) => s.title); - if (stys.length > 1) { - const pageMenu = document.querySelector('#page-menu') as HTMLUListElement; - const dummy = document.createElement('li'); - pageMenu.appendChild(dummy); - preact.render(, pageMenu, dummy); - } -} - +// Set a style (including setting the cookie) function setActiveStyleSheet(title: string) { const as = styles(); let found: null | HTMLLinkElement = null; @@ -110,17 +33,103 @@ function setActiveStyleSheet(title: string) { } } +// Reset the style based on the cookie function resetStyle() { const s = getCookie("haddock-style"); if (s) setActiveStyleSheet(s); } -function styleMenu(show?: boolean) { - const m = document.getElementById('style-menu'); - if (m) toggleShow(m, show); +class StylesButton extends Component { + render(props: { title: string, onClick: () => void }) { + function onClick(e: Event) { + e.preventDefault(); + props.onClick(); + } + return
  • {props.title}
  • ; + } } -export function init() { - addStyleMenu(); +// Add the style menu button +function addStyleMenu(stys: string[], action: () => void) { + if (stys.length > 1) { + const pageMenu = document.querySelector('#page-menu') as HTMLUListElement; + const dummy = document.createElement('li'); + pageMenu.appendChild(dummy); + preact.render(, pageMenu, dummy); + } +} + +type StyleProps = { + styles: string[] + showHideTrigger: (action: () => void) => void +} + +type StyleState = { + isVisible: boolean +} + +// Represents the full style dropdown +class Styles extends Component { + + componentWillMount() { + document.addEventListener('mousedown', this.hide.bind(this)); + + document.addEventListener('keydown', (e) => { + if (this.state.isVisible) { + if (e.key === 'Escape') { + this.hide(); + } + } + }) + } + + hide() { + this.setState({ isVisible: false }); + } + + show() { + if (!this.state.isVisible) { + this.setState({ isVisible: true }); + } + } + + toggleVisibility() { + if (this.state.isVisible) { + this.hide(); + } else { + this.show(); + } + } + + componentDidMount() { + this.props.showHideTrigger(this.toggleVisibility.bind(this)); + } + + render(props: StyleProps, state: StyleState) { + const stopPropagation = (e: Event) => { e.stopPropagation(); }; + + return
    + +
    ; + } +} + + +export function init(showHide?: (action: () => void) => void) { + const stys = styles().map((s) => s.title); + const addStylesButton = (action: () => void) => addStyleMenu(stys, action) resetStyle(); + preact.render( + , + document.body + ); } -- cgit v1.2.3