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

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: "";
// }
// }
}