move export to web worker
This commit is contained in:
parent
df7011329b
commit
7cf7e0c90a
13 changed files with 248 additions and 133 deletions
|
@ -55,5 +55,8 @@
|
|||
},
|
||||
"browserslist": [
|
||||
"last 2 versions"
|
||||
]
|
||||
],
|
||||
"engines": {
|
||||
"node": "14.x"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Page from 'site/components/wrappers/page.js'
|
||||
import useApp from 'site/hooks/useApp.js'
|
||||
import WorkbenchWrapper from 'shared/components/wrappers/workbench.js'
|
||||
import WorkbenchWrapper from 'shared/components/wrappers/workbench'
|
||||
import { useRouter } from 'next/router'
|
||||
import Layout from 'site/components/layouts/lab'
|
||||
|
||||
|
|
56
sites/shared/components/workbench/exporting/export_worker.js
Normal file
56
sites/shared/components/workbench/exporting/export_worker.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
import yaml from 'js-yaml'
|
||||
import axios from 'axios'
|
||||
import PdfExporter from './pdfExporter'
|
||||
|
||||
addEventListener('message', async(e) => {
|
||||
const {format, gist, svg} = e.data
|
||||
// handle the data exports
|
||||
|
||||
if (format === 'json') return exportJson(gist)
|
||||
if (format === 'yaml') return exportYaml(gist)
|
||||
if (format === 'github gist') return exportGithubGist(gist)
|
||||
|
||||
if (format === 'svg') return exportSvg(gist, svg)
|
||||
|
||||
new PdfExporter(e.data).export(postSuccess)
|
||||
})
|
||||
|
||||
const postSuccess = (blob) => {
|
||||
postMessage({success: true, blob})
|
||||
close()
|
||||
}
|
||||
|
||||
const exportJson = gist => {
|
||||
const blob = new Blob([JSON.stringify(gist, null, 2)], {
|
||||
type: 'application/json;charset=utf-8'
|
||||
})
|
||||
postSuccess(blob)
|
||||
}
|
||||
|
||||
const exportYaml = gist => {
|
||||
const blob = new Blob([yaml.dump(gist)], {
|
||||
type: 'application/x-yaml;charset=utf-8'
|
||||
})
|
||||
postSuccess(blob)
|
||||
}
|
||||
|
||||
const exportSvg = (gist, svg) => {
|
||||
const blob = new Blob([svg], {
|
||||
type: 'image/svg+xml;charset=utf-8'
|
||||
})
|
||||
postSuccess(blob)
|
||||
}
|
||||
|
||||
const exportGithubGist = (data) => {
|
||||
axios.post('https://backend.freesewing.org/github/gist', {
|
||||
design: data.design,
|
||||
data: yaml.dump(data)
|
||||
})
|
||||
.then(res => {
|
||||
postMessage({
|
||||
success: true,
|
||||
link: 'https://gist.github.com/' + res.data.id
|
||||
})
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
}
|
|
@ -1,13 +1,11 @@
|
|||
import { useState } from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import fileSaver from 'file-saver'
|
||||
import yaml from 'js-yaml'
|
||||
import axios from 'axios'
|
||||
import Popout from 'shared/components/popout'
|
||||
import WebLink from 'shared/components/web-link'
|
||||
import Worker from 'web-worker';
|
||||
import fileSaver from 'file-saver'
|
||||
import theme from '@freesewing/plugin-theme'
|
||||
import {pagesPlugin} from '../layout/print/plugin'
|
||||
import PdfExporter from './pdfExporter'
|
||||
|
||||
export const exports = {
|
||||
exportForPrinting: ['a4', 'a3', 'a2', 'a1', 'a0', 'letter', 'tabloid'],
|
||||
|
@ -22,28 +20,35 @@ export const defaultPdfSettings = {
|
|||
coverPage: true
|
||||
}
|
||||
|
||||
export const handleExport = (format, gist, design, t, app, setLink, setFormat) => {
|
||||
export const handleExport = async(format, gist, design, t, app, onComplete) => {
|
||||
app.startLoading();
|
||||
|
||||
// handle state setting if there's supposed to be any
|
||||
setLink && setLink(false)
|
||||
setFormat && setFormat(format)
|
||||
const worker = new Worker(new URL('./export_worker.js', import.meta.url), {type: module});
|
||||
|
||||
// handle the data exports
|
||||
if (exports.exportAsData.indexOf(format) !== -1) {
|
||||
if (format === 'json') exportJson(gist)
|
||||
else if (format === 'yaml') exportYaml(gist)
|
||||
else if (format === 'github gist') exportGithubGist(gist, app, setLink)
|
||||
|
||||
return
|
||||
worker.addEventListener('message', e => {
|
||||
if (e.data.blob) {
|
||||
const fileType = exports.exportForPrinting.indexOf(format) === -1 ? format : 'pdf'
|
||||
fileSaver.saveAs(e.data.blob, `freesewing-${gist.design || 'gist'}.${fileType}`)
|
||||
}
|
||||
app.stopLoading()
|
||||
onComplete && onComplete(e)
|
||||
})
|
||||
|
||||
gist.embed=false
|
||||
let svg = ''
|
||||
// pdf settings
|
||||
const settings = {
|
||||
...defaultPdfSettings,
|
||||
...(gist._state.layout?.forPrinting?.page || {})
|
||||
}
|
||||
const workerArgs = {format, gist, settings}
|
||||
|
||||
if (exports.exportAsData.indexOf(format) === -1) {
|
||||
// gist.embed=false
|
||||
// make a pattern instance for export rendering
|
||||
const layout = gist.layouts?.printingLayout || gist.layout || true
|
||||
let pattern = new design({...gist, layout})
|
||||
|
||||
|
||||
// add the theme and translation to the pattern
|
||||
pattern.use(theme, {stripped: format !== 'svg'})
|
||||
pattern.use({
|
||||
|
@ -52,12 +57,6 @@ export const handleExport = (format, gist, design, t, app, setLink, setFormat) =
|
|||
}
|
||||
},{t})
|
||||
|
||||
// pdf settings
|
||||
const settings = {
|
||||
...defaultPdfSettings,
|
||||
...(gist._state.layout?.forPrinting?.page || {})
|
||||
}
|
||||
|
||||
// a specified size should override the gist one
|
||||
if (format !== 'pdf') {
|
||||
settings.size = format
|
||||
|
@ -66,47 +65,28 @@ export const handleExport = (format, gist, design, t, app, setLink, setFormat) =
|
|||
try {
|
||||
// add pages to pdf exports
|
||||
if (format !== 'svg') {
|
||||
pattern.use(pagesPlugin(settings, true))
|
||||
pattern.use(pagesPlugin({
|
||||
...settings,
|
||||
printStyle: true,
|
||||
renderBlanks: false,
|
||||
setPatternSize: true
|
||||
}))
|
||||
}
|
||||
|
||||
pattern.draft();
|
||||
svg = pattern.render()
|
||||
workerArgs.svg = svg
|
||||
if (pattern.parts.pages) {
|
||||
workerArgs.pages = pattern.parts.pages.pages
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err)
|
||||
app.stopLoading();
|
||||
}
|
||||
|
||||
if (format === 'svg') return exportSvg(gist, svg)
|
||||
}
|
||||
|
||||
return new PdfExporter(gist.design, pattern, svg, settings).export();
|
||||
}
|
||||
|
||||
const exportJson = gist => {
|
||||
const blob = new Blob([JSON.stringify(gist, null, 2)], {
|
||||
type: 'application/json;charset=utf-8'
|
||||
})
|
||||
fileSaver.saveAs(blob, `freesewing-${gist.design || 'gist'}.json`)
|
||||
}
|
||||
const exportYaml = gist => {
|
||||
const blob = new Blob([yaml.dump(gist)], {
|
||||
type: 'application/x-yaml;charset=utf-8'
|
||||
})
|
||||
fileSaver.saveAs(blob, `freesewing-${gist.design || 'gist'}.yaml`)
|
||||
}
|
||||
const exportSvg = (gist, svg) => {
|
||||
const blob = new Blob([svg], {
|
||||
type: 'image/svg+xml;charset=utf-8'
|
||||
})
|
||||
fileSaver.saveAs(blob, `freesewing-${gist.design || 'pattern'}.svg`)
|
||||
}
|
||||
const exportGithubGist = (data, app, setLink) => {
|
||||
app.setLoading(true)
|
||||
axios.post('https://backend.freesewing.org/github/gist', {
|
||||
design: data.design,
|
||||
data: yaml.dump(data)
|
||||
})
|
||||
.then(res => setLink('https://gist.github.com/' + res.data.id))
|
||||
.catch(err => console.log(err))
|
||||
.finally(() => app.stopLoading())
|
||||
worker.postMessage(workerArgs)
|
||||
}
|
||||
|
||||
const ExportDraft = ({ gist, design, app }) => {
|
||||
|
@ -115,6 +95,13 @@ const ExportDraft = ({ gist, design, app }) => {
|
|||
const [format, setFormat] = useState(false)
|
||||
|
||||
const { t } = useTranslation(['app'])
|
||||
const doExport = (format) => {
|
||||
setLink(false)
|
||||
setFormat(format)
|
||||
handleExport(format, gist, design, t, app, (e) => {
|
||||
if (e.data.link) {setLink(e.data.link)}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-screen-xl m-auto">
|
||||
|
@ -135,7 +122,7 @@ const ExportDraft = ({ gist, design, app }) => {
|
|||
{exports[type].map(format => (
|
||||
<button key={format}
|
||||
className="btn btn-primary"
|
||||
onClick={() => handleExport(format, gist, design, t, app, setLink, setFormat)}
|
||||
onClick={() => doExport(format)}
|
||||
>
|
||||
{type === 'exportForPrinting' ? `${format} pdf` : format }
|
||||
</button>
|
||||
|
|
|
@ -13,7 +13,8 @@ const pxToPoints = (72/96);
|
|||
// multiply a mm value by this to get a pixel value
|
||||
const mmToPx = 3.77953
|
||||
// multiply a mm value by this to get a points value
|
||||
const mmToPoints = mmToPx * pxToPoints
|
||||
// const mmToPoints = mmToPx * pxToPoints
|
||||
const mmToPoints = 2.834645669291339
|
||||
|
||||
/**
|
||||
* Freesewing's first explicit class?
|
||||
|
@ -30,6 +31,7 @@ export default class Exporter {
|
|||
pattern
|
||||
/** the pdfKit instance that is writing the document */
|
||||
pdf
|
||||
buffers
|
||||
|
||||
/** the usable width (excluding margin) of the pdf page, in points */
|
||||
pageWidth
|
||||
|
@ -51,36 +53,40 @@ export default class Exporter {
|
|||
pagesWithContent = {}
|
||||
|
||||
|
||||
constructor(designName, pattern, svg, settings) {
|
||||
// default just in case
|
||||
this.designName = designName || 'freesewing'
|
||||
constructor({svg, settings, pages}) {
|
||||
this.settings = settings
|
||||
this.pattern = pattern
|
||||
|
||||
this.pagesWithContent = pages.withContent;
|
||||
this.svg = svg
|
||||
this.createPdf()
|
||||
|
||||
this.margin = this.settings.margin * mmToPoints // margin is in mm because it comes from us, so we convert it to points
|
||||
this.pageHeight = this.pdf.page.height - this.margin // this is in points because it comes from pdfKit
|
||||
this.pageWidth = this.pdf.page.width - this.margin// this is in points because it comes from pdfKit
|
||||
this.pageHeight = this.pdf.page.height - this.margin * 2 // this is in points because it comes from pdfKit
|
||||
this.pageWidth = this.pdf.page.width - this.margin * 2// this is in points because it comes from pdfKit
|
||||
// console.log(pages.width * pages.cols, pages.height * pages.rows, this.pageWidth, this.pageHeight)
|
||||
// console.log(svg)
|
||||
|
||||
// we pass the svg as a string, so we need to make it a DOM element so we can manipulate it
|
||||
const divElem = document.createElement('div');
|
||||
divElem.innerHTML = svg;
|
||||
this.svg = divElem.firstElementChild;
|
||||
// const divElem = document.createElement('div');
|
||||
// divElem.innerHTML = svg;
|
||||
// this.svg = divElem.firstElementChild;
|
||||
|
||||
// get the pages data
|
||||
const pages = this.pattern.parts.pages.pages
|
||||
this.columns = pages.cols
|
||||
this.rows = pages.rows
|
||||
|
||||
// then set the svg's width and height in points to include all pages in full (the original svg will have been set to show only as much page as is occupied)
|
||||
this.svgWidth = this.columns * this.pageWidth
|
||||
this.svgHeight = this.rows * this.pageHeight
|
||||
this.svg.setAttribute('height', this.svgWidth + 'pt')
|
||||
this.svg.setAttribute('width', this.svgHeight + 'pt')
|
||||
this.svgWidth = this.columns * pages.width * mmToPoints
|
||||
this.svgHeight = this.rows * pages.height * mmToPoints
|
||||
|
||||
console.log(this.columns, this.rows, pages.width, pages.height, this.svgWidth, this.svgHeight)
|
||||
console.log(svg)
|
||||
// this.svg.replace(/width="\d+mm"/, `width="${this.svgWidth}pt"`)
|
||||
// this.svg.replace(/height="\d+mm"/, `height="${this.svgHeight}pt"`)
|
||||
// this.svg.setAttribute('height', this.svgWidth + 'pt')
|
||||
// this.svg.setAttribute('width', this.svgHeight + 'pt')
|
||||
|
||||
// set the viewbox to include all pages in full as well, this time in mm
|
||||
this.svg.setAttribute('viewBox', `0 0 ${this.columns * this.pageWidthInMm} ${this.rows * this.pageHeightInMm}`)
|
||||
// this.svg.setAttribute('viewBox', `0 0 ${this.columns * this.pageWidthInMm} ${this.rows * this.pageHeightInMm}`)
|
||||
}
|
||||
|
||||
/** pdf page usable (excluding margin) width, in mm */
|
||||
|
@ -99,18 +105,9 @@ export default class Exporter {
|
|||
|
||||
// PdfKit wants to flush the buffer on each new page.
|
||||
// We don't want to try to save the document until it's complete, so we have to manage the buffers ourselves
|
||||
const buffers = [];
|
||||
this.buffers = [];
|
||||
// add new data to our buffer storage
|
||||
this.pdf.on('data', buffers.push.bind(buffers));
|
||||
// when the end event fires, then we save the whole thing
|
||||
this.pdf.on('end', () => {
|
||||
// convert buffers to a blob
|
||||
const blob = new Blob(buffers, {
|
||||
type: 'application/pdf'
|
||||
})
|
||||
// save the blob
|
||||
fileSaver.saveAs(blob, `freesewing-${this.designName}.pdf`)
|
||||
});
|
||||
this.pdf.on('data', this.buffers.push.bind(this.buffers));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,8 +194,16 @@ export default class Exporter {
|
|||
}
|
||||
|
||||
/** export to pdf */
|
||||
async export() {
|
||||
this.scanPages()
|
||||
async export(onComplete) {
|
||||
// when the end event fires, then we save the whole thing
|
||||
this.pdf.on('end', () => {
|
||||
// convert buffers to a blob
|
||||
const blob = new Blob(this.buffers, {
|
||||
type: 'application/pdf'
|
||||
})
|
||||
onComplete(blob)
|
||||
});
|
||||
|
||||
await this.generateCoverPage()
|
||||
await this.generatePages();
|
||||
this.save()
|
||||
|
@ -218,7 +223,7 @@ export default class Exporter {
|
|||
let coverWidth = this.pageWidth - coverMargin * 2
|
||||
|
||||
// add the entire pdf to the page, so that it fills the available space as best it can
|
||||
await SVGtoPDF(this.pdf, this.svg.outerHTML, coverMargin, coverMargin, {
|
||||
await SVGtoPDF(this.pdf, this.svg, coverMargin, coverMargin, {
|
||||
width: coverWidth,
|
||||
height: coverHeight,
|
||||
assumePt: true,
|
||||
|
@ -234,11 +239,12 @@ export default class Exporter {
|
|||
assumePt: true,
|
||||
width: this.svgWidth,
|
||||
height: this.svgHeight,
|
||||
preserveAspectRatio: 'xMinYMin slice'
|
||||
preserveAspectRatio: 'xMinYMin slice',
|
||||
useCSS: true
|
||||
}
|
||||
|
||||
// everything is offset by half a margin so that it's centered on the page
|
||||
const startMargin = 0.5 * this.margin
|
||||
const startMargin = this.margin
|
||||
for (var h = 0; h < this.rows; h++) {
|
||||
for (var w = 0; w < this.columns; w++) {
|
||||
// skip empty pages
|
||||
|
@ -255,7 +261,7 @@ export default class Exporter {
|
|||
}
|
||||
|
||||
// add the pdf to the page, offset by the page distances
|
||||
await SVGtoPDF(this.pdf, this.svg.outerHTML, x, y, options)
|
||||
await SVGtoPDF(this.pdf, this.svg, x, y, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch"
|
|||
const Draft = props => {
|
||||
const { draft, patternProps, gist, updateGist, app, bgProps={}, fitLayoutPart = false, layoutType="printingLayout"} = props
|
||||
|
||||
const svgRef = useRef(null);
|
||||
if (!patternProps) return null
|
||||
// keep a fresh copy of the layout because we might manipulate it without saving to the gist
|
||||
let layout = draft.settings.layout === true ? {
|
||||
...patternProps.autoLayout,
|
||||
|
@ -14,9 +16,7 @@ const Draft = props => {
|
|||
height: patternProps.height
|
||||
} : {...draft.settings.layout}
|
||||
|
||||
const svgRef = useRef(null);
|
||||
|
||||
if (!patternProps) return null
|
||||
|
||||
// Helper method to update part layout and re-calculate width * height
|
||||
const updateLayout = (name, config, history=true) => {
|
||||
|
|
|
@ -35,7 +35,7 @@ const PrintLayout = props => {
|
|||
const bgProps = { fill: "url(#page)" }
|
||||
|
||||
const exportIt = () => {
|
||||
handleExport('pdf', props.gist, props.design, t)
|
||||
handleExport('pdf', props.gist, props.design, t, props.app)
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -16,15 +16,55 @@ const indexLetter = (i) => String.fromCharCode('A'.charCodeAt(0) + i - 1)
|
|||
/**
|
||||
* A plugin to add printer pages
|
||||
* */
|
||||
export const pagesPlugin = ({size='a4', orientation='portrait', margin=10}, printStyle = false /** should the pages be rendered for printing or for screen viewing? */ ) => {
|
||||
export const pagesPlugin = ({
|
||||
size='a4',
|
||||
orientation='portrait',
|
||||
margin=10,
|
||||
printStyle = false, /** should the pages be rendered for printing or for screen viewing? */
|
||||
setPatternSize = true
|
||||
}) => {
|
||||
const ls = orientation === 'landscape'
|
||||
let sheetHeight = sizes[size][ls ? 1 : 0]
|
||||
let sheetWidth = sizes[size][ls ? 0 : 1]
|
||||
sheetWidth -= margin
|
||||
sheetHeight -= margin
|
||||
return basePlugin({sheetWidth, sheetHeight, orientation, printStyle})
|
||||
let sheetHeight = sizes[size][ls ? 0 : 1]
|
||||
let sheetWidth = sizes[size][ls ? 1 : 0]
|
||||
sheetWidth -= margin * 2
|
||||
sheetHeight -= margin * 2
|
||||
return basePlugin({sheetWidth, sheetHeight, orientation, printStyle, setPatternSize})
|
||||
}
|
||||
|
||||
const doScanForBlanks = (parts, layout, x, y, w, h) => {
|
||||
let hasContent = false
|
||||
for (var p in parts) {
|
||||
let part = parts[p]
|
||||
// skip the pages part and any that aren't rendered
|
||||
if (part === this || part.render === false || part.isEmpty()) continue
|
||||
|
||||
// get the position of the part
|
||||
let partLayout = layout.parts[p]
|
||||
let partMinX = (partLayout.tl?.x || (partLayout.move.x + part.topLeft.x))
|
||||
let partMinY = (partLayout.tl?.y || (partLayout.move.y + part.topLeft.y))
|
||||
let partMaxX = (partLayout.br?.x || (partMinX + part.width))
|
||||
let partMaxY = (partLayout.br?.y || (partMinY + part.height))
|
||||
|
||||
// check if the part overlaps the page extents
|
||||
if (
|
||||
// if the left of the part is further left than the right end of the page
|
||||
partMinX < x + w &&
|
||||
// and the top of the part is above the bottom of the page
|
||||
partMinY < y + h &&
|
||||
// and the right of the part is further right than the left of the page
|
||||
partMaxX > x &&
|
||||
// and the bottom of the part is below the top to the page
|
||||
partMaxY > y
|
||||
) {
|
||||
// the part has content inside the page
|
||||
hasContent = true;
|
||||
// so we stop looking
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hasContent
|
||||
}
|
||||
/**
|
||||
* The base plugin for adding a layout helper part like pages or fabric
|
||||
* sheetWidth: the width of the helper part
|
||||
|
@ -39,7 +79,10 @@ const basePlugin = ({
|
|||
boundary=false,
|
||||
partName="pages",
|
||||
responsiveColumns=true,
|
||||
printStyle=false
|
||||
printStyle=false,
|
||||
scanForBlanks=true,
|
||||
renderBlanks=true,
|
||||
setPatternSize=false
|
||||
}) => ({
|
||||
name,
|
||||
version,
|
||||
|
@ -62,9 +105,13 @@ const basePlugin = ({
|
|||
responsiveColumns && (width += pattern.settings.layout.topLeft.x)
|
||||
}
|
||||
|
||||
macro('addPages', { size: [sheetWidth, sheetHeight], height, width })
|
||||
macro('addPages', { size: [sheetHeight,sheetWidth, ], height, width })
|
||||
|
||||
if (boundary) pattern.parts[partName].boundary();
|
||||
if (setPatternSize) {
|
||||
pattern.width = sheetWidth * pattern.parts[partName].pages.cols
|
||||
pattern.height = sheetHeight * pattern.parts[partName].pages.rows
|
||||
}
|
||||
}
|
||||
},
|
||||
macros: {
|
||||
|
@ -74,13 +121,22 @@ const basePlugin = ({
|
|||
const cols = Math.ceil(so.width / w)
|
||||
const rows = Math.ceil(so.height / h)
|
||||
const { points, Point, paths, Path, macro } = this.shorthand()
|
||||
let x = 0
|
||||
let y = 0
|
||||
let count = 0
|
||||
let withContent = {}
|
||||
// get the layout from the pattern
|
||||
const layout = typeof this.context.settings.layout === 'object' ? this.context.settings.layout : this.context.autoLayout;
|
||||
for (let row=0;row<rows;row++) {
|
||||
x=0
|
||||
let y = row * h
|
||||
withContent[row] = {}
|
||||
for (let col=0;col<cols;col++) {
|
||||
count++
|
||||
let x = col * w
|
||||
let hasContent = true
|
||||
if (scanForBlanks && layout) {
|
||||
hasContent = doScanForBlanks(this.context.parts, layout, x, y, w, h)
|
||||
withContent[row][col] = hasContent
|
||||
if (!renderBlanks && !hasContent) continue
|
||||
}
|
||||
if (hasContent) count++
|
||||
const pageName = `_pages__row${row}-col${col}`
|
||||
points[`${pageName}-tl`] = new Point(x,y)
|
||||
points[`${pageName}-tr`] = new Point(x+w,y)
|
||||
|
@ -114,12 +170,10 @@ const basePlugin = ({
|
|||
macro('addRuler', {xAxis: true, pageName})
|
||||
macro('addRuler', {xAxis: false, pageName})
|
||||
}
|
||||
x += w
|
||||
}
|
||||
y += h
|
||||
}
|
||||
// Store page count in part
|
||||
this.pages = { cols, rows, count: cols*rows }
|
||||
this.pages = { cols, rows, count, withContent, width: w, height: h}
|
||||
|
||||
},
|
||||
/** add a ruler to the top left corner of the page */
|
||||
|
|
|
@ -49,7 +49,7 @@ const PrintLayoutSettings = props => {
|
|||
<input
|
||||
type="range"
|
||||
max={50}
|
||||
min={5}
|
||||
min={0}
|
||||
step={1}
|
||||
onChange={setMargin}
|
||||
value={margin}
|
||||
|
|
|
@ -11,7 +11,7 @@ import Modal from 'shared/components/modal'
|
|||
import Measurements from 'shared/components/workbench/measurements/index.js'
|
||||
import LabDraft from 'shared/components/workbench/draft/index.js'
|
||||
import LabSample from 'shared/components/workbench/sample.js'
|
||||
import ExportDraft from 'shared/components/workbench/exporting/index.js'
|
||||
import ExportDraft from 'shared/components/workbench/exporting/index'
|
||||
import GistAsJson from 'shared/components/workbench/json.js'
|
||||
import GistAsYaml from 'shared/components/workbench/yaml.js'
|
||||
import DraftEvents from 'shared/components/workbench/events.js'
|
||||
|
|
|
@ -39,7 +39,8 @@
|
|||
"remark-smartypants": "^2.0.0",
|
||||
"svg-to-pdfkit": "^0.1.8",
|
||||
"to-vfile": "^7.2.2",
|
||||
"unist-util-visit": "^4.1.0"
|
||||
"unist-util-visit": "^4.1.0",
|
||||
"web-worker": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.0",
|
||||
|
@ -47,5 +48,8 @@
|
|||
"postcss": "^8.4.4",
|
||||
"tailwindcss": "^3.0.1",
|
||||
"tailwindcss-open-variant": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "14.x"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ const loadFromUnpkg = (design, version) => {
|
|||
const pageTemplate = design => `${header}
|
||||
import design from 'designs/${design}/src/index.js'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import PageTemplate from 'site/page-templates/workbench.js'
|
||||
import PageTemplate from 'site/page-templates/workbench'
|
||||
|
||||
const Page = (props) => <PageTemplate {...props} design={design} version="next"/>
|
||||
export default Page
|
||||
|
|
|
@ -24378,6 +24378,11 @@ web-streams-polyfill@^3.0.3:
|
|||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
|
||||
integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
|
||||
|
||||
web-worker@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.2.0.tgz#5d85a04a7fbc1e7db58f66595d7a3ac7c9c180da"
|
||||
integrity sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue