Skip to content

Commit

Permalink
Merge branch 'handsontable-feature/issue-1441'
Browse files Browse the repository at this point in the history
  • Loading branch information
sequba committed Oct 22, 2024
2 parents b89d2ce + a4a8c0f commit 97e5df1
Show file tree
Hide file tree
Showing 80 changed files with 8,394 additions and 228 deletions.
9 changes: 9 additions & 0 deletions .config/webpack/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const path = require('path');
const fs = require('fs');
const { BannerPlugin } = require('webpack');

const licenseComment = fs.readFileSync(path.resolve(__dirname, '../source-license-header.js'), 'utf8').trim();
let licensePreamble = fs.readFileSync(path.resolve(__dirname, '../../LICENSE.txt'), 'utf8');

licensePreamble += '\n\nVersion: ' + process.env.HT_VERSION;
Expand Down Expand Up @@ -42,6 +43,14 @@ module.exports.create = function create(processedFile) {
}
]
},
{
test: /\.js$/,
loader: 'string-replace-loader',
options: {
search: licenseComment,
replace: '',
}
}
]
},
plugins: [
Expand Down
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Common files
node_modules

# example files
docs/examples/

# 3rd party
src/interpreter/plugin/3rdparty

Expand Down
39 changes: 39 additions & 0 deletions docs/.vuepress/components/ScriptLoader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<template>
</template>

<script>
const decode = (base64data) => {
return decodeURI(base64data);
};
const useCodePreview = (code) => {
const scriptElement = document.createElement('script');
scriptElement.type = 'text/javascript';
scriptElement.innerHTML = code;
return [
(container) => {
container.appendChild(scriptElement);
return () => { scriptElement.parentElement.removeChild(scriptElement); };
},
];
};
export default {
name: 'ScriptLoader',
props: ['code'],
mounted() {
const [append] = useCodePreview(decode(this.$props.code));
this.removeScript = append(this.$el);
},
beforeDestroy() {
this.removeScript();
},
methods: {
decode
}
};
</script>
30 changes: 15 additions & 15 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ const highlight = require('./highlight');
const regexPlugin = require('markdown-it-regex').default;
const footnotePlugin = require('markdown-it-footnote');
const searchBoxPlugin = require('./plugins/search-box');
const examples = require('./plugins/examples/examples');
const HyperFormula = require('../../dist/hyperformula.full');
const fs = require('fs');
const path = require('path');
const includeCodeSnippet = require('./plugins/markdown-it-include-code-snippet');

const searchPattern = new RegExp('^/api', 'i');

module.exports = {
title: 'HyperFormula (v' + HyperFormula.version + ')',
description: 'HyperFormula is an open-source, high-performance calculation engine for spreadsheets and web applications.',
head: [
// Import HF (required for the examples)
[ 'script', { src: 'https://cdn.jsdelivr.net/npm/hyperformula/dist/hyperformula.full.min.js' } ],
[ 'script', { src: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/languages/enUS.js' } ],
[ 'script', { src: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/languages/frFR.js' } ],
// Import moment (required for the examples)
[ 'script', { src: 'https://cdn.jsdelivr.net/npm/moment/moment.min.js' } ],
// Google Tag Manager, an extra element within the `ssr.html` file.
['script', {}, `
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
Expand Down Expand Up @@ -41,11 +47,11 @@ module.exports = {
`],
[
'script',
{
id: 'Sentry.io',
src: 'https://js.sentry-cdn.com/50617701901516ce348cb7b252564a60.min.js',
crossorigin: 'anonymous',
},
{
id: 'Sentry.io',
src: 'https://js.sentry-cdn.com/50617701901516ce348cb7b252564a60.min.js',
crossorigin: 'anonymous',
},
],
// Favicon
['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/favicon/apple-touch-icon.png' }],
Expand All @@ -57,14 +63,7 @@ module.exports = {
base: '/',
plugins: [
searchBoxPlugin,
// [
// 'vuepress-plugin-clean-urls',
// {
// normalSuffix: '',
// indexSuffix: '/',
// notFoundPath: '/404.html',
// },
// ],
['container', examples()],
{
extendPageData ($page) {
// inject current HF version as {{ $page.version }} variable
Expand Down Expand Up @@ -109,6 +108,7 @@ module.exports = {
replace: () => `'${HyperFormula.releaseDate}'`
})
md.use(footnotePlugin)
md.use(includeCodeSnippet)
}
},
// TODO: It doesn't work. It's seems that this option is bugged. Documentation says that this option is configurable,
Expand Down
32 changes: 32 additions & 0 deletions docs/.vuepress/plugins/examples/code-builder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const { transformSync } = require('@babel/core');

const babelConfig = {
presets: [
'@babel/preset-env',
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: [
'@babel/plugin-transform-modules-commonjs',
'@babel/plugin-syntax-class-properties',
['@babel/plugin-proposal-decorators', { legacy: true }],
['@babel/plugin-proposal-class-properties', { loose: true }],
['@babel/plugin-proposal-private-methods', { loose: true }],
['@babel/plugin-proposal-private-property-in-object', { loose: true }]
],
targets: {
ie: 9
}
};

const buildCode = (filename, contentJs, relativePath = '') => {
try {
return transformSync(contentJs, { ...babelConfig, filename }).code;
} catch (error) {
// eslint-disable-next-line
console.error(`Babel error when building ${relativePath}`);
throw error;
}
};

module.exports = { buildCode };
119 changes: 119 additions & 0 deletions docs/.vuepress/plugins/examples/examples.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
const Token = require('markdown-it/lib/token');
const { buildCode } = require('./code-builder');
const { stackblitz } = require('./stackblitz');

/**
* Matches into: `example #ID .class :preset --css 2 --html 0 --js 1 --ts 3 --no-edit`.
*
* @type {RegExp}
*/
const EXAMPLE_REGEX = /^(example)\s*(#\S*|)\s*(\.\S*|)\s*(:\S*|)\s*([\S|\s]*)$/;

const parseCode = (content) => {
if (!content) return '';

return content
// Remove the all "/* start:skip-in-preview */" and "/* end:skip-in-preview */" comments
.replace(/\/\*(\s+)?(start|end):skip-in-preview(\s+)?\*\/\n/gm, '')
// Remove the all "/* start:skip-in-sandbox */" and "/* end:skip-in-sandbox */" comments
.replace(/\/\*(\s+)?(start|end):skip-in-sandbox(\s+)?\*\/\n/gm, '')
// Remove the code between "/* start:skip-in-compilation */" and "/* end:skip-in-compilation */" expressions
.replace(/\/\*(\s+)?start:skip-in-compilation(\s+)?\*\/\n.*?\/\*(\s+)?end:skip-in-compilation(\s+)?\*\/\n/msg, '')
// Remove /* end-file */
.replace(/\/\* end-file \*\//gm, '');
};

const parseCodeSandbox = (content) => {
if (!content) return '';

return content
// Remove the code between "/* start:skip-in-sandbox */" and "/* end:skip-in-sandbox */" expressions
.replace(/\/\*(\s+)?start:skip-in-sandbox(\s+)?\*\/\n.*?\/\*(\s+)?end:skip-in-sandbox(\s+)?\*\/\n/msg, '')
// Remove the all "/* start:skip-in-preview */" and "/* end:skip-in-preview */" comments
.replace(/\/\*(\s+)?(start|end):skip-in-preview(\s+)?\*\/\n/gm, '')
// Remove the all "/* start:skip-in-compilation */" and "/* end:skip-in-compilation */" comments
.replace(/\/\*(\s+)?(start|end):skip-in-compilation(\s+)?\*\/\n/gm, '');
};

module.exports = function() {
return {
type: 'example',
render(tokens, index, _opts, env) {
const token = tokens[index];
const m = token.info.trim().match(EXAMPLE_REGEX);

if (token.nesting !== 1 || !m) {
return '';
}

let [, , id, klass, preset, args] = m;

id = id ? id.substring(1) : 'example1';
klass = klass ? klass.substring(1) : '';
preset = preset ? preset.substring(1) : 'hot';
args = args || '';

const htmlPos = args.match(/--html (\d*)/)?.[1];
const htmlIndex = htmlPos ? index + Number.parseInt(htmlPos, 10) : 0;
const htmlToken = htmlPos ? tokens[htmlIndex] : undefined;
const htmlContent = htmlToken
? htmlToken.content
: `<div id="${id}" class="hot ${klass}"></div>`;
const htmlContentRoot = `<div data-preset-type="${preset}" data-example-id="${id}" >${htmlContent}</div>`;

const cssPos = args.match(/--css (\d*)/)?.[1];
const cssIndex = cssPos ? index + Number.parseInt(cssPos, 10) : 0;
const cssToken = cssPos ? tokens[cssIndex] : undefined;
const cssContent = cssToken ? cssToken.content : '';

const jsPos = args.match(/--js (\d*)/)?.[1] || 1;
const jsIndex = jsPos ? index + Number.parseInt(jsPos, 10) : 0;
const jsToken = jsPos ? tokens[jsIndex] : undefined;

const tsPos = args.match(/--ts (\d*)/)?.[1];
const tsIndex = tsPos ? index + Number.parseInt(tsPos, 10) : 0;
const tsToken = tsPos ? tokens[tsIndex] : undefined;

// Parse code
const codeToCompile = parseCode(jsToken?.content);
const tsCodeToCompileSandbox = parseCodeSandbox(tsToken?.content);

[htmlIndex, jsIndex, tsIndex, cssIndex].filter(x => !!x).sort().reverse().forEach((x) => {
tokens.splice(x, 1);
});

const newTokens = [
new Token('container_div_open', 'div', 1),
new Token('container_div_close', 'div', -1),
new Token('container_div_open', 'div', 1),
new Token('container_div_close', 'div', -1),
];

tokens.splice(index + 1, 0, ...newTokens);

const builtCode = buildCode(
id + '.js',
codeToCompile,
env.relativePath
);
const encodedCode = encodeURI(builtCode);

return `
<div class="example-container">
<style v-pre>${cssContent}</style>
<div v-pre>${htmlContentRoot}</div>
<ScriptLoader code="${encodedCode}"></ScriptLoader>
</div>
<div class="example-controls">
${stackblitz(
id,
htmlContent,
tsCodeToCompileSandbox,
cssContent,
'ts'
)}
</div>
`;
},
};
};
77 changes: 77 additions & 0 deletions docs/.vuepress/plugins/examples/stackblitz.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const buildJavascriptBody = ({ id, html, js, css, hyperformulaVersion, lang }) => {
return {
files: {
'package.json': {
content: `{
"name": "hyperformula-demo",
"version": "1.0.0",
"main": "index.html",
"dependencies": {
"hyperformula": "${hyperformulaVersion}",
"moment": "latest"
}
}`
},
'index.html': {
content: `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HyperFormula demo</title>
</head>
<body>
${html || `<div id="${id}"></div>`}
</body>
</html>`
},
'styles.css': {
content: css
},
[`index.${lang}`]: {
content: `import './styles.css'
${js}`
},
}
};
};

const stackblitz = (id, html, js, css, lang) => {
const hyperformulaVersion = 'latest';
const body = buildJavascriptBody({ id, html, js, css, hyperformulaVersion, lang });
const template = lang === 'ts' ? 'typescript' : 'node';

const projects = body?.files
? Object.entries(body?.files).map(([key, value]) => (
`<textarea class="hidden" name="project[files][${key}]" readOnly v-pre>${value.content}</textarea>`
)) : [];

return `
<form
class="form-stackblitz-external"
action="https://stackblitz.com/run"
method="post"
target="_blank"
>
${projects.join('\n')}
<input type="hidden" name="project[title]" value="hyperformula-demo"/>
<input type="hidden" name="project[dependencies]"
value='{"hyperformula":"${hyperformulaVersion}", "moment": "latest"}'
/>
<input type="hidden" name="project[template]" value="${template}"/>
<div class="js-stackblitz-link">
<button type="submit">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"
width="10.43" height="15" preserveAspectRatio="xMidYMid" viewBox="0 0 256 368" class="icon outbound">
<path fill="currentColor" d="M109.586 217.013H0L200.34 0l-53.926 150.233H256L55.645 367.246l53.927-150.233z"/>
</svg>
Open in Stackblitz
</button>
</div>
</form>
`;
};

module.exports = { stackblitz };
Loading

0 comments on commit 97e5df1

Please sign in to comment.