Added clipboard integration

This commit is contained in:
squidfunk 2020-02-02 17:18:18 +01:00
parent 18c8c4c2ba
commit 4cc07912df
12 changed files with 208 additions and 134 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -65,7 +65,6 @@ button {
margin: 0;
padding: 0;
border: 0;
outline-style: none;
background: transparent;
font-size: inherit; }
@ -73,7 +72,7 @@ input {
border: 0;
outline: 0; }
.md-icon, .md-clipboard::before, .md-nav__title::before, .md-nav__button, .md-nav__link::after, .md-search-result__article--document::before, .md-source-file::before, .md-typeset .admonition > .admonition-title::before, .md-typeset details > .admonition-title::before, .md-typeset .admonition > summary::before, .md-typeset details > summary::before, .md-typeset .footnote-backref, .md-typeset .critic.comment::before, .md-typeset summary::after, .md-typeset .task-list-control .task-list-indicator::before {
.md-icon, .md-nav__title::before, .md-nav__button, .md-nav__link::after, .md-search-result__article--document::before, .md-source-file::before, .md-typeset .admonition > .admonition-title::before, .md-typeset details > .admonition-title::before, .md-typeset .admonition > summary::before, .md-typeset details > summary::before, .md-typeset .footnote-backref, .md-typeset .critic.comment::before, .md-typeset summary::after, .md-typeset .task-list-control .task-list-indicator::before {
font-family: "Material Icons";
font-style: normal;
font-variant: normal;
@ -494,64 +493,26 @@ hr {
right: 0.3rem;
width: 1.4rem;
height: 1.4rem;
-webkit-transition: color 0.25s;
transition: color 0.25s;
border-radius: 0.1rem;
color: rgba(0, 0, 0, 0.07);
font-size: 0.8rem;
cursor: pointer;
z-index: 1;
-webkit-backface-visibility: hidden;
backface-visibility: hidden; }
.md-clipboard::before {
-webkit-transition: color 0.25s, opacity 0.25s;
transition: color 0.25s, opacity 0.25s;
color: rgba(0, 0, 0, 0.07);
content: "\E14D"; }
pre:hover .md-clipboard::before,
.codehilite:hover .md-clipboard::before,
.md-typeset .highlight:hover .md-clipboard::before {
color: rgba(0, 0, 0, 0.54); }
.md-clipboard:focus::before, .md-clipboard:hover::before {
pre:hover .md-clipboard,
.codehilite:hover .md-clipboard,
.md-typeset .highlight:hover .md-clipboard {
color: rgba(0, 0, 0, 0.54); }
pre .md-clipboard:focus,
pre .md-clipboard:hover,
.codehilite .md-clipboard:focus,
.md-typeset .highlight .md-clipboard:focus,
.codehilite .md-clipboard:hover,
.md-typeset .highlight .md-clipboard:hover {
color: #536dfe; }
.md-clipboard__message {
display: block;
position: absolute;
top: 0;
right: 1.7rem;
padding: 0.3rem 0.5rem;
-webkit-transform: translateX(0.4rem);
transform: translateX(0.4rem);
-webkit-transition: opacity 0.175s, -webkit-transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0);
transition: opacity 0.175s, -webkit-transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0);
transition: transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0), opacity 0.175s;
transition: transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0), opacity 0.175s, -webkit-transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0);
border-radius: 0.1rem;
background-color: rgba(0, 0, 0, 0.54);
color: white;
font-size: 0.64rem;
white-space: nowrap;
opacity: 0;
pointer-events: none; }
.md-clipboard__message--active {
-webkit-transform: translateX(0);
transform: translateX(0);
-webkit-transition: opacity 0.175s 0.075s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
transition: opacity 0.175s 0.075s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.175s 0.075s;
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.175s 0.075s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
opacity: 1;
pointer-events: initial; }
.md-clipboard__message::before {
content: attr(aria-label); }
.md-clipboard__message::after {
display: block;
position: absolute;
top: 50%;
right: -0.2rem;
width: 0;
margin-top: -0.2rem;
border-width: 0.2rem 0 0.2rem 0.2rem;
border-style: solid;
border-color: transparent rgba(0, 0, 0, 0.54);
content: ""; }
.md-content__inner {
margin: 0 0.8rem 1.2rem;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

47
package-lock.json generated
View File

@ -234,6 +234,12 @@
"integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==",
"dev": true
},
"@types/clipboard": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.1.tgz",
"integrity": "sha512-gJJX9Jjdt3bIAePQRRjYWG20dIhAgEqonguyHxXuqALxsoDsDLimihqrSg8fXgVTJ4KZCzkfglKtwsh/8dLfbA==",
"dev": true
},
"@types/escape-html": {
"version": "0.0.20",
"resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-0.0.20.tgz",
@ -2781,7 +2787,8 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@ -2805,13 +2812,15 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -2828,19 +2837,22 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -2971,7 +2983,8 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -2985,6 +2998,7 @@
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -3001,6 +3015,7 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -3009,13 +3024,15 @@
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -3036,6 +3053,7 @@
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -3124,7 +3142,8 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -3138,6 +3157,7 @@
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -3233,7 +3253,8 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@ -3275,6 +3296,7 @@
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -3296,6 +3318,7 @@
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -3344,13 +3367,15 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"dev": true
"dev": true,
"optional": true
}
}
},

View File

@ -41,6 +41,7 @@
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.12.0",
"@types/clipboard": "^2.0.1",
"@types/escape-html": "0.0.20",
"@types/lunr": "^2.3.2",
"@types/lz-string": "^1.3.33",

View File

@ -23,6 +23,7 @@
// TODO: remove this after we finished refactoring
// tslint:disable
import * as Clipboard from "clipboard"
import { identity, values } from "ramda"
import {
EMPTY,
@ -88,6 +89,7 @@ import {
} from "./workers"
import { renderSource } from "templates"
import { switchMapIf, not, takeIf } from "extensions"
import { renderClipboard } from "templates/clipboard"
/* ----------------------------------------------------------------------------
* Types
@ -557,7 +559,7 @@ export function initialize(config: unknown) {
fromEvent(window, "beforeprint") // IE, FF
)
.subscribe(() => {
const details = document.querySelectorAll("details")
const details = getElements("details")
Array.prototype.forEach.call(details, detail => {
detail.setAttribute("open", "")
})
@ -566,11 +568,39 @@ export function initialize(config: unknown) {
// Close drawer and search on hash change
agent.location.hash$.subscribe(() => {
setToggle(drawer, false)
setToggle(search, false)
setToggle(search, false) // we probably need to delay the anchor jump for search
})
/* ----------------------------------------------------------------------- */
/* Clipboard integration */
if (Clipboard.isSupported()) {
const blocks = getElements(".codehilite > pre, .highlight> pre, pre > code")
Array.prototype.forEach.call(blocks, (block, index) => {
const id = `__code_${index}`
/* Create button with message container */
const button = renderClipboard(id)
/* Link to block and insert button */
const parent = block.parentNode
parent.id = id
parent.insertBefore(button, block)
})
/* Initialize Clipboard listener */
const copy = new Clipboard(".md-clipboard")
/* Success handler */
copy.on("success", action => {
alert("Copied to clipboard") // TODO: integrate snackbar
// TODO: add a snackbar/notification
})
}
/* ----------------------------------------------------------------------- */
const state = {
search: {
query$,

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { h } from "extensions"
import { translate } from "utilities"
/* ----------------------------------------------------------------------------
* Data
* ------------------------------------------------------------------------- */
/**
* CSS classes
*/
const css = {
container: "md-clipboard md-icon"
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Render clipboard
*
* @param id - Unique identifier
*
* @return HTML element
*/
export function renderClipboard(
id: string
): HTMLElement {
return (
<button
class={css.container}
title={translate("clipboard.copy")}
data-clipboard-target={`#${id} pre, #${id} code`}
>&#xE14D;</button>
)
}

View File

@ -120,7 +120,6 @@ button {
margin: 0;
padding: 0;
border: 0;
outline-style: none;
background: transparent;
font-size: inherit;
}

View File

@ -31,7 +31,9 @@
right: px2rem(6px);
width: px2rem(28px);
height: px2rem(28px);
transition: color 0.25s;
border-radius: px2rem(2px);
color: $md-color-black--lightest;
font-size: px2rem(16px);
cursor: pointer;
z-index: 1;
@ -43,75 +45,66 @@
display: none;
}
// Icon
&::before {
@extend %md-icon;
transition:
color 0.25s,
opacity 0.25s;
color: $md-color-black--lightest;
content: "\E14D"; // content_copy
// Show on container hover
pre:hover &,
.codehilite:hover & {
color: $md-color-black--light;
}
// Show on container hover
pre:hover &,
.codehilite:hover & {
color: $md-color-black--light;
}
// Focused or hovered icon
&:focus::before,
&:hover::before {
pre &:focus,
pre &:hover,
.codehilite &:focus,
.codehilite &:hover {
color: $md-color-accent;
}
// Message
&__message {
display: block;
position: absolute;
top: 0;
right: px2rem(34px);
padding: px2rem(6px) px2rem(10px);
transform: translateX(px2rem(8px));
transition:
transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0),
opacity 0.175s;
border-radius: px2rem(2px);
background-color: $md-color-black--light;
color: $md-color-white;
font-size: ms(-1);
white-space: nowrap;
opacity: 0;
pointer-events: none;
// // Message
// &__message {
// display: block;
// position: absolute;
// top: 0;
// right: px2rem(34px);
// padding: px2rem(6px) px2rem(10px);
// transform: translateX(px2rem(8px));
// transition:
// transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0),
// opacity 0.175s;
// border-radius: px2rem(2px);
// background-color: $md-color-black--light;
// color: $md-color-white;
// font-size: ms(-1);
// white-space: nowrap;
// opacity: 0;
// pointer-events: none;
// Active message
&--active {
transform: translateX(0);
transition:
transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.175s 0.075s;
opacity: 1;
pointer-events: initial;
}
// // Active message
// &--active {
// transform: translateX(0);
// transition:
// transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
// opacity 0.175s 0.075s;
// opacity: 1;
// pointer-events: initial;
// }
// Inject content from ARIA label
&::before {
content: attr(aria-label);
}
// // Inject content from ARIA label
// &::before {
// content: attr(aria-label);
// }
// Paint a nice speech bubble
&::after {
display: block;
position: absolute;
top: 50%;
right: px2rem(-4px);
width: 0;
margin-top: px2rem(-4px);
border-width: px2rem(4px) 0 px2rem(4px) px2rem(4px);
border-style: solid;
border-color: transparent $md-color-black--light;
content: "";
}
}
// // Paint a nice speech bubble
// &::after {
// display: block;
// position: absolute;
// top: 50%;
// right: px2rem(-4px);
// width: 0;
// margin-top: px2rem(-4px);
// border-width: px2rem(4px) 0 px2rem(4px) px2rem(4px);
// border-style: solid;
// border-color: transparent $md-color-black--light;
// content: "";
// }
// }
}