Browse Source

Initial commit! 🎉

tags/0.1.0^0
Bauke 1 month ago
commit
6d5709bfc9
Signed by: Bauke <me@bauke.xyz> GPG Key ID: C1C0F29952BCF558
13 changed files with 12510 additions and 0 deletions
  1. +107
    -0
      .gitignore
  2. +21
    -0
      LICENSE
  3. +15
    -0
      README.md
  4. +57
    -0
      package.json
  5. BIN
      source/images/favicon.png
  6. +58
    -0
      source/index.html
  7. +126
    -0
      source/js/index.js
  8. +77
    -0
      source/scss/_codemirror.scss
  9. +33
    -0
      source/scss/_colors.scss
  10. +14
    -0
      source/scss/_reset.scss
  11. +10
    -0
      source/scss/_variables.scss
  12. +85
    -0
      source/scss/index.scss
  13. +11907
    -0
      yarn.lock

+ 107
- 0
.gitignore View File

@@ -0,0 +1,107 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Build directory.
public/

+ 21
- 0
LICENSE View File

@@ -0,0 +1,21 @@
The MIT License

Copyright (c) 2020 Bauke <me@bauke.xyz>

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 NONINFRINGEMENT. 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.

+ 15
- 0
README.md View File

@@ -0,0 +1,15 @@
# Nunjucks Playground

> Render Nunjucks templates in the browser.

[![Netlify deploy status](https://api.netlify.com/api/v1/badges/512e6d8e-19dc-4707-bbdf-3467d6ea3bbc/deploy-status)](https://app.netlify.com/sites/nunjucks/deploys)

## Building

To build the site [Node](https://nodejs.org) and [Yarn](https://yarnpkg.com) are required.

Then, run `yarn start` to build and serve the site at `localhost:1234` or `yarn build` to just build it. The site will be built to the `public/` directory.

## License

Open-sourced with the [MIT license](https://git.holllo.cc/Bauke/nunjucks-playground/src/branch/main/LICENSE).

+ 57
- 0
package.json View File

@@ -0,0 +1,57 @@
{
"name": "nunjucks-playground",
"description": "Render Nunjucks templates in the browser.",
"version": "0.1.0",
"author": "Bauke <me@bauke.xyz>",
"homepage": "https://nunjucks.bauke.xyz",
"repository": "https://git.holllo.cc/Bauke/nunjucks-playground",
"license": "MIT",
"scripts": {
"start": "parcel 'source/index.html' -d 'public' --open",
"build": "parcel build 'source/index.html' -d 'public' --no-source-maps",
"test": "xo && stylelint 'source/scss/*.scss'",
"deploy": "rm -rf 'public/' && yarn build && yarn deploy:netlify",
"deploy:netlify": "netlify deploy --prod --dir 'public/' -s 512e6d8e-19dc-4707-bbdf-3467d6ea3bbc"
},
"dependencies": {
"codemirror": "^5.56.0",
"modern-normalize": "^0.7.0",
"nunjucks": "^3.2.2"
},
"devDependencies": {
"husky": "^4.2.5",
"netlify-cli": "^2.58.0",
"parcel-bundler": "^1.12.4",
"sass": "^1.26.10",
"stylelint": "^13.6.1",
"stylelint-config-xo-scss": "^0.13.0",
"stylelint-config-xo-space": "^0.14.0",
"xo": "^0.32.1"
},
"stylelint": {
"extends": [
"stylelint-config-xo-scss",
"stylelint-config-xo-space"
],
"rules": {
"at-rule-no-unknown": null,
"no-descending-specificity": null
}
},
"xo": {
"globals": [
"atob",
"btoa",
"document",
"window"
],
"prettier": true,
"space": true
},
"husky": {
"hooks": {
"pre-commit": "yarn test",
"pre-push": "yarn test"
}
}
}

BIN
source/images/favicon.png View File

Before After
Width: 900  |  Height: 900  |  Size: 9.7 KiB

+ 58
- 0
source/index.html View File

@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nunjucks Playground</title>
<link rel="shortcut icon" href="images/favicon.png" type="image/x-icon">
<link rel="stylesheet" href="../node_modules/modern-normalize/modern-normalize.css">
<link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="scss/index.scss">
</head>

<body>
<header>
<h1>Nunjucks Playground</h1>
</header>

<main>
<noscript>
<p class="noscript">
It appears JavaScript is disabled. Because this playground is a fully
client-side website, unfortunately it cannot function without
JavaScript. Please make sure it is enabled and refresh the page.
</p>
</noscript>

<details open id="template" class="hidden">
<summary>Nunjucks</summary>
<textarea id="nunjucks-template"></textarea>
</details>

<details open id="context" class="hidden">
<summary>JSON</summary>
<textarea id="nunjucks-context"></textarea>
</details>

<details open id="output" class="hidden">
<summary>Output</summary>
<textarea id="nunjucks-output"></textarea>
</details>
</main>

<footer>
<p>
&copy; Bauke - Open-sourced with the
<a href="https://git.holllo.cc/Bauke/nunjucks-playground/src/branch/main/LICENSE"
target="_blank">MIT license</a>.
</p>
<p>
<a href="images/favicon.png">The Nunjucks favicon</a> used belongs to their respective owners.
</p>
</footer>

<script src="js/index.js"></script>
</body>

</html>

+ 126
- 0
source/js/index.js View File

@@ -0,0 +1,126 @@
import codemirror from 'codemirror';
import nunjucks from 'nunjucks';

// eslint-disable-next-line import/no-unassigned-import
import 'codemirror/mode/htmlmixed/htmlmixed';

// Rename these functions so they're clearer.
const encodeBase64 = btoa;
const decodeBase64 = atob;

// Initialize the default state.
let state = {
detailsOpen: {
template: true,
context: true,
output: true
},
template: '<h1>{{ title }}</h1>\n',
context: `{
"title": "Nunjucks Playground"
}\n`
};

// Try to parse the state from the window location hash.
try {
if (window.location.hash === '') {
console.log('No location hash present, continuing with default state.');
} else {
console.log('Location hash present, parsing it.');
const locationHash = window.location.hash.slice(1);
const newState = JSON.parse(decodeBase64(locationHash));
state = newState;
}
} catch (error) {
console.error('Failed to parse state from location hash:', error);
}

// Default CodeMirror config, used multiple times.
const codemirrorConfig = {
lineNumbers: true,
theme: 'love-dark'
};

// Create the Nunjucks Template editor.
const nunjucksTemplateElement = document.querySelector('#nunjucks-template');

// Unhide the element, it's hidden by default so nothing is rendered if JS is disabled.
nunjucksTemplateElement.parentElement.classList.remove('hidden');
const nunjucksTemplate = codemirror.fromTextArea(nunjucksTemplateElement, {
...codemirrorConfig
});

// Set the value to the current state.
nunjucksTemplate.setValue(state.template);

// Create the Nunjucks Context editor.
const nunjucksContextElement = document.querySelector('#nunjucks-context');

// Unhide the element, it's hidden by default so nothing is rendered if JS is disabled.
nunjucksContextElement.parentElement.classList.remove('hidden');
const nunjucksContext = codemirror.fromTextArea(nunjucksContextElement, {
...codemirrorConfig,
mode: 'javascript'
});

// Set the value to the current state.
nunjucksContext.setValue(state.context);

// Create the Nunjucks Output editor but set it to be read-only.
const nunjucksOutputElement = document.querySelector('#nunjucks-output');

// Unhide the element, it's hidden by default so nothing is rendered if JS is disabled.
nunjucksOutputElement.parentElement.classList.remove('hidden');
const nunjucksOutput = codemirror.fromTextArea(nunjucksOutputElement, {
...codemirrorConfig,
readOnly: true
});

// When the template is changed, update the state and set the location hash.
nunjucksTemplate.on('change', () => {
state.template = nunjucksTemplate.getValue();
updateLocationHash();
renderTemplate();
});

// When the context is changed, update the state and set the location hash.
nunjucksContext.on('change', () => {
state.context = nunjucksContext.getValue();
updateLocationHash();
renderTemplate();
});

// Go over all the details elements and update the detailsOpen state when
// they are opened/closed.
const allDetails = document.querySelectorAll('details');
for (const details of allDetails) {
details.open = state.detailsOpen[details.id];

details.firstElementChild.addEventListener('click', () => {
state.detailsOpen[details.id] = !details.open;
updateLocationHash();
});
}

// Sets the location hash to the base-64 encoded state.
function updateLocationHash() {
window.location.hash = encodeBase64(JSON.stringify(state));
}

// Tries to render the template with the context and outputs it in the output.
function renderTemplate() {
try {
nunjucksOutput.setValue(
nunjucks.renderString(
nunjucksTemplate.getValue(),
JSON.parse(nunjucksContext.getValue())
)
);
} catch (error) {
console.error('Error rendering template:', error.message);
}
}

// Run the update and render once at the start so everything is good to go.
updateLocationHash();
renderTemplate();

+ 77
- 0
source/scss/_codemirror.scss View File

@@ -0,0 +1,77 @@
// stylelint-disable selector-class-pattern

// Custom Love Dark CodeMirror theme (https://love.holllo.cc).

.cm-s-love-dark {
&.CodeMirror,
.CodeMirror-gutters {
background-color: var(--background-2);
border: none;
color: var(--foreground-1);
}

&.CodeMirror {
border: 1px solid var(--primary-accent-5);
}

.CodeMirror-cursor {
border-left: 1px solid var(--foreground-1);
}

.CodeMirror-linenumbers {
border-right: 1px solid var(--primary-accent-5);
}

.CodeMirror-linenumber {
color: var(--foreground-1);
}

.CodeMirror-selected {
background-color: #fff1;
}

span.cm-comment {
color: var(--primary-gray-3);
}

span.cm-string,
span.cm-string-2 {
color: var(--primary-accent-4);
}

span.cm-number {
color: var(--primary-accent-3);
}

span.cm-variable {
color: var(--primary-accent-2);
}

span.cm-variable-2 {
color: var(--primary-accent-7);
}

span.cm-def {
color: var(--primary-accent-2);
}

span.cm-operator {
color: var(--primary-accent-9);
}

span.cm-keyword {
color: var(--primary-accent-5);
}

span.cm-atom {
color: var(--primary-accent-3);
}

span.cm-tag {
color: var(--primary-accent-2);
}

span.cm-property {
color: var(--primary-accent-7);
}
}

+ 33
- 0
source/scss/_colors.scss View File

@@ -0,0 +1,33 @@
:root {
--foreground-1: #f2efff;
--foreground-2: #e6deff;
--background-1: #1f1731;
--background-2: #2a2041;
--primary-accent-1: #f99fb1;
--primary-accent-2: #faa56c;
--primary-accent-3: #d2b83a;
--primary-accent-4: #96c839;
--primary-accent-5: #3bd18a;
--primary-accent-6: #3ecdbf;
--primary-accent-7: #41c8e5;
--primary-accent-8: #98b9f8;
--primary-accent-9: #d5a6f8;
--primary-accent-10: #f99add;
--primary-gray-1: #e2e2e2;
--primary-gray-2: #c6c6c6;
--primary-gray-3: #ababab;
--secondary-accent-1: #8b123c;
--secondary-accent-2: #6a3b11;
--secondary-accent-3: #514610;
--secondary-accent-4: #384d10;
--secondary-accent-5: #115133;
--secondary-accent-6: #124f49;
--secondary-accent-7: #144d5a;
--secondary-accent-8: #17477e;
--secondary-accent-9: #6f1995;
--secondary-accent-10: #81156a;
--secondary-gray-1: #1b1b1b;
--secondary-gray-2: #303030;
--secondary-gray-3: #474747;
--transparent-accent-1: #000a;
}

+ 14
- 0
source/scss/_reset.scss View File

@@ -0,0 +1,14 @@
%zero-margin {
margin: 0;
}

%zero-padding {
padding: 0;
}

h1,
h2,
p,
pre {
@extend %zero-margin;
}

+ 10
- 0
source/scss/_variables.scss View File

@@ -0,0 +1,10 @@
$small-breakpoint: 600px;
$medium-breakpoint: 900px;
$large-breakpoint: 1200px;
$extra-large-breakpoint: 1800px;

%responsive-centered {
margin-left: auto;
margin-right: auto;
max-width: $medium-breakpoint;
}

+ 85
- 0
source/scss/index.scss View File

@@ -0,0 +1,85 @@
@import 'colors';
@import 'reset';
@import 'variables';

html {
font-size: 62.5%;
}

body {
background-color: var(--background-1);
color: var(--foreground-1);
font-size: 1.5rem;
padding: 16px;
}

a {
border: 1px dashed transparent;
color: var(--primary-accent-7);
display: inline-block;
text-decoration: none;
}

a:hover {
background-color: var(--primary-accent-3);
color: var(--background-1);
}

a:active,
a:focus {
border: 1px dashed #f2efff;
outline: none;
}

header {
@extend %responsive-centered;
background-color: var(--background-2);
border: 1px solid var(--primary-accent-5);
padding: 16px;
}

main {
@extend %responsive-centered;
}

details {
background-color: var(--background-2);
margin-top: 16px;

&[open] > summary {
border-bottom: none;
}

> summary {
border: 1px solid var(--primary-accent-5);
cursor: pointer;
font-size: 2rem;
font-weight: bold;
padding: 16px;

&:hover {
background-color: #fff1;
}
}
}

footer {
@extend %responsive-centered;
border: 1px solid var(--primary-accent-5);
font-weight: bold;
margin-top: 16px;
padding: 16px;
}

.hidden {
display: none;
}

.noscript {
background-color: var(--background-2);
border: 1px solid var(--primary-accent-5);
margin-top: 16px;
padding: 16px;
}

@import 'codemirror';

+ 11907
- 0
yarn.lock
File diff suppressed because it is too large
View File


Loading…
Cancel
Save