This commit is contained in:
Martin McKeaveney 2021-04-12 13:41:46 +01:00
commit b5baf9f892
379 changed files with 21857 additions and 38635 deletions

1
.gitignore vendored
View File

@ -63,6 +63,7 @@ typings/
# dotenv environment variables file # dotenv environment variables file
.env .env
!hosting/.env !hosting/.env
hosting/.generated-envoy.dev.yaml
# parcel-bundler cache (https://parceljs.org/) # parcel-bundler cache (https://parceljs.org/)
.cache .cache

View File

@ -38,7 +38,7 @@
</a> </a>
<img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Code of conduct" /> <img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Code of conduct" />
<a href="https://codecov.io/gh/Budibase/budibase"> <a href="https://codecov.io/gh/Budibase/budibase">
<img src="https://codecov.io/gh/Budibase/budibase/branch/next/graph/badge.svg?token=E8W2ZFXQOH"/> <img src="https://codecov.io/gh/Budibase/budibase/graph/badge.svg?token=E8W2ZFXQOH"/>
</a> </a>
</p> </p>
@ -93,7 +93,7 @@ Watch "releases" of this repo to get notified of major updates, and give the sta
[![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase) [![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
If you are having issues between updates of the builder, please use the guide [here](https://github.com/Budibase/budibase/blob/master/CONTRIBUTING.md#troubleshooting) to clear down your environment. If you are having issues between updates of the builder, please use the guide [here](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) to clear down your environment.
## 🏁 Getting Started with Budibase ## 🏁 Getting Started with Budibase
@ -131,7 +131,7 @@ Checkout our [Public Roadmap](https://github.com/Budibase/budibase/projects/10).
## ❗ Code of Conduct ## ❗ Code of Conduct
Budibase is dedicated to providing a welcoming, diverse, and harrassment-free experience for everyone. We expect everyone in the Budibase community to abide by our [**Code of Conduct**](https://github.com/Budibase/budibase/blob/master/.github/CODE_OF_CONDUCT.md). Please read it. Budibase is dedicated to providing a welcoming, diverse, and harrassment-free experience for everyone. We expect everyone in the Budibase community to abide by our [**Code of Conduct**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Please read it.
## 🙌 Contributing to Budibase ## 🙌 Contributing to Budibase
@ -143,13 +143,13 @@ A good place to start contributing, is the [First time issues project](https://g
### How the repository is organized ### How the repository is organized
Budibase is a monorepo managed by lerna. Lerna manages the building and publishing of the budibase packages. At a high level, here are the packages that make up Budibase. Budibase is a monorepo managed by lerna. Lerna manages the building and publishing of the budibase packages. At a high level, here are the packages that make up Budibase.
- [packages/builder](https://github.com/Budibase/budibase/tree/master/packages/builder) - contains code for the budibase builder client side svelte application. - [packages/builder](https://github.com/Budibase/budibase/tree/HEAD/packages/builder) - contains code for the budibase builder client side svelte application.
- [packages/client](https://github.com/Budibase/budibase/tree/master/packages/client) - A module that runs in the browser responsible for reading JSON definition and creating living, breathing web apps from it. - [packages/client](https://github.com/Budibase/budibase/tree/HEAD/packages/client) - A module that runs in the browser responsible for reading JSON definition and creating living, breathing web apps from it.
- [packages/server](https://github.com/Budibase/budibase/tree/master/packages/server) - The budibase server. This Koa app is responsible for serving the JS for the builder and budibase apps, as well as providing the API for interaction with the database and file system. - [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - The budibase server. This Koa app is responsible for serving the JS for the builder and budibase apps, as well as providing the API for interaction with the database and file system.
For more information, see [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/master/.github/CONTRIBUTING.md) For more information, see [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md)
## 📝 License ## 📝 License

View File

@ -27,7 +27,7 @@ services:
restart: always restart: always
image: envoyproxy/envoy:v1.16-latest image: envoyproxy/envoy:v1.16-latest
volumes: volumes:
- ./envoy.dev.yaml:/etc/envoy/envoy.yaml - ./.generated-envoy.dev.yaml:/etc/envoy/envoy.yaml
ports: ports:
- "${MAIN_PORT}:10000" - "${MAIN_PORT}:10000"
depends_on: depends_on:

View File

@ -22,7 +22,7 @@ services:
JWT_SECRET: ${JWT_SECRET} JWT_SECRET: ${JWT_SECRET}
LOG_LEVEL: info LOG_LEVEL: info
SENTRY_DSN: https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131 SENTRY_DSN: https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131
ENABLE_ANALYTICS: true ENABLE_ANALYTICS: "true"
depends_on: depends_on:
- worker-service - worker-service

View File

@ -26,6 +26,23 @@ static_resources:
cluster: redis-service cluster: redis-service
prefix_rewrite: "/" prefix_rewrite: "/"
- match: { prefix: "/api/" }
route:
cluster: server-dev
- match: { prefix: "/app_" }
route:
cluster: server-dev
- match: { prefix: "/builder/" }
route:
cluster: builder-dev
- match: { prefix: "/builder" }
route:
cluster: builder-dev
prefix_rewrite: "/builder/"
# minio is on the default route because this works # minio is on the default route because this works
# best, minio + AWS SDK doesn't handle path proxy # best, minio + AWS SDK doesn't handle path proxy
- match: { prefix: "/" } - match: { prefix: "/" }
@ -77,3 +94,32 @@ static_resources:
socket_address: socket_address:
address: redis-service address: redis-service
port_value: 6379 port_value: 6379
- name: server-dev
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
load_assignment:
cluster_name: server-dev
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: {{ address }}
port_value: 4001
- name: builder-dev
connect_timeout: 15s
type: strict_dns
lb_policy: round_robin
load_assignment:
cluster_name: builder-dev
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: {{ address }}
port_value: 3000

View File

@ -17,16 +17,19 @@
"svelte": "^3.30.0" "svelte": "^3.30.0"
}, },
"scripts": { "scripts": {
"bootstrap": "lerna bootstrap", "bootstrap": "lerna link && lerna bootstrap",
"build": "lerna run build", "build": "lerna run build",
"initialise": "lerna run initialise", "initialise": "lerna run initialise",
"publishdev": "lerna run publishdev", "publishdev": "lerna run publishdev",
"publishnpm": "yarn build && lerna publish --force-publish", "publishnpm": "yarn build && lerna publish --force-publish",
"restore": "npm run clean && npm run bootstrap && npm run build", "restore": "yarn run clean && yarn run bootstrap && yarn run build",
"nuke": "rimraf ~/.budibase && npm run restore", "nuke": "yarn run nuke:packages && yarn run nuke:docker",
"nuke:packages": "yarn run restore",
"nuke:docker": "lerna run --parallel dev:stack:nuke",
"clean": "lerna clean", "clean": "lerna clean",
"kill-port": "kill-port 4001", "kill-port": "kill-port 4001",
"dev": "yarn run kill-port && node ./scripts/symlinkDev.js && lerna run --parallel dev:builder --concurrency 1", "dev": "yarn run kill-port && lerna link && lerna run --parallel dev:builder --concurrency 1",
"dev:noserver": "lerna link && lerna run --parallel dev:builder --concurrency 1 --ignore @budibase/server",
"test": "lerna run test", "test": "lerna run test",
"lint": "eslint packages", "lint": "eslint packages",
"lint:fix": "eslint --fix packages", "lint:fix": "eslint --fix packages",

5
packages/bbui/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.DS_Store
node_modules
/dist/
/public/svench/
.idea

42
packages/bbui/README.md Normal file
View File

@ -0,0 +1,42 @@
# Budibase bbui
A package that handles all common components across the Budibase organisation. You can find the current live version [Here](http://bbui.budibase.com).
## Install
1. Clone
2. `npm install`
3. `npm run svench`
(Note: yarn won't work!)
## Example workflow to create a component
1. Create a file: `Headline.svelte`
2. Create a Svench file: `Headline.svench`
3. Build component and add variants to the Svench file.
4. Once done, re-export the file in `src/index.js`.
5. Publish, update the package in the main project and profit.
## Guidelines
### Making components
1. Think about re-usability
2. Use the css custom properties (variables) that are in the css stylesheet. This makes it easy to tweak things later down the line.
3. Opt to forward events (`<button on:click>` for example) rather than using callbacks.
4. Avoid adding margins to the outermost container of the component.
### Using components and the styleguide
1. Get familiar with the different props that exist on the component. If something vital is missing, make a PR and add it.
2. Take advantage of the css custom properties in the stylesheet and avoid writing hard-coded values.
4. Since there is no margin on the components, think about the structure of the DOM and how to achieve correct spacing, etc. This can be done using `css grid` + `grid gap` or with a container div where you specify a padding or margin. The best solution depends on the circumstance.
## TODO
* [ ] Figure out a good documentation situation
* [ ] Add testing suite (E2E using Playwright?)
## Other
The project uses [Svench](https://github.com/rixo/svench). It is somewhat akin to Storybook but a lot less bloated and much easier to setup. It also supports HMR for quick development.

View File

@ -0,0 +1,53 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
"version": "1.58.13",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
"exports": {
".": {
"import": "./dist/bbui.es.js"
},
"./package.json": "./package.json",
"./dist/style.css": "./dist/style.css"
},
"scripts": {
"dev:builder": "vite build",
"build": "vite build"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^16.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.5",
"cross-env": "^7.0.2",
"nollup": "^0.14.1",
"postcss": "^8.2.9",
"rollup": "^2.34.0",
"rollup-plugin-copy": "^3.3.0",
"rollup-plugin-delete": "^1.2.0",
"rollup-plugin-hot": "^0.1.1",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-svelte-hot": "^0.11.0",
"semantic-release": "^17.0.8",
"svelte": "^3.29.0",
"svench": "^0.0.10-7",
"vite": "^2.1.5"
},
"keywords": [
"svelte"
],
"files": [
"src",
"dist"
],
"dependencies": {
"markdown-it": "^12.0.4",
"quill": "^1.3.7",
"sirv-cli": "^0.4.6",
"svelte-flatpickr": "^2.4.0",
"svelte-portal": "^1.0.0",
"turndown": "^7.0.0"
}
}

View File

@ -0,0 +1,140 @@
import * as path from "path"
import svelte from "rollup-plugin-svelte-hot"
import resolve from "@rollup/plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs"
import json from "@rollup/plugin-json"
import copy from "rollup-plugin-copy"
import hmr from "rollup-plugin-hot"
import del from "rollup-plugin-delete"
import postcss from "rollup-plugin-postcss"
import { plugin as Svench } from "svench/rollup"
import builtins from "rollup-plugin-node-builtins"
const WATCH = !!process.env.ROLLUP_WATCH
const SVENCH = !!process.env.SVENCH
const HOT = WATCH
const PRODUCTION = !WATCH
const svench = Svench({
// The root dir that Svench will parse and watch.
//
// NOTE Watching the root of the project, to let Svench render *.md for us.
//
// NOTE By default, `node_modules` and `.git` dirs are ignored. This can be
// customized by passing a function to `ignore` option. Default ignore is:
//
// ignore: path => /(?:^|\/)(?:node_modules|\.git)\//.test(path),
//
dir: ".",
// Make `src` dir a section (that is, it will always be "expanded" in the
// menu).
autoSections: ["src"],
// Use custom index.html
index: {
source: "public/index.html",
},
extensions: [".svench", ".svench.svelte", ".svench.svx", ".md"],
serve: WATCH && {
host: "0.0.0.0",
port: 4242,
public: "public",
nollup: "0.0.0.0:42421",
},
})
// NOTE configs are in function form to avoid instantiating plugins of the
// config that is not used for nothing (in particular, the HMR plugin launches
// a dev server on startup, this is not desired when just building for prod)
const configs = {
svench: () => ({
input: ".svench/svench.js",
output: {
format: "es",
dir: "public/svench",
},
plugins: [
builtins(),
// NOTE cleaning old builds is required to avoid serving stale static
// files from a previous build instead of in-memory files from the dev/hmr
// server
del({
targets: "public/svench/*",
runOnce: true,
}),
postcss({
hot: HOT,
extract: path.resolve("public/svench/theme.css"),
sourceMap: true,
}),
svench,
svelte({
dev: !PRODUCTION,
extensions: [".svelte", ".svench", ".svx", ".md"],
// Svench's "combined" preprocessor wraps both Mdsvex preprocessors
// (configured for Svench), and its own preprocessor (for static
// analysis -- eg extract source from views)
preprocess: svench.$.preprocess,
hot: HOT && {
optimistic: true,
noPreserveState: false,
},
}),
resolve({ browser: true }),
commonjs(),
json(),
HOT &&
hmr({
host: "0.0.0.0",
public: "public",
inMemory: true,
compatModuleHot: !HOT, // for terser
}),
],
watch: {
clearScreen: false,
// buildDelay is needed to ensure Svench's code (routes) generator will
// pick file changes before Rollup and prevent a double build (if Rollup
// first sees a change to src/Foo.svench, then to Svench's routes.js)
buildDelay: 100,
},
}),
lib: () => ({
input: "src/index.js",
output: [{ file: "dist/bundle.mjs", format: "es" }],
plugins: [
svelte({
dev: !PRODUCTION,
extensions: [".svelte"],
emitCss: true,
}),
postcss(),
copy({
targets: [
{
src: ".svench/svench.css",
dest: "public",
rename: "global.css",
},
],
}),
resolve(),
commonjs(),
json(),
],
}),
}
export default configs[SVENCH ? "svench" : "lib"]()

View File

@ -0,0 +1,30 @@
<script>
import { View } from "svench";
import Multiselect from "../Form/Multiselect.svelte";
import DropdownMenu from "../DropdownMenu/DropdownMenu.svelte";
import Button from "../Button/Button.svelte";
import Icon from "../Icons/Icon.svelte";
const options = ["Red", "Blue", "Yellow", "Green", "Pink", "Very long color name to show text wrapping"];
let anchorLeft;
let dropdownLeft;
</script>
### Click Outside Action
This action can be used to listen for when you want to call a function whenever a user clicks outside of the element it is applied toString.
```html
<script>
import { clickOutside } from '@budibase/bbui'
const someFunction = () => {
// Some logic to close a dropdown or stop the user from doing something without saving?
}
</script>
<button use:clickOutside={someFunction} class="multiselect" bind:this={anchor}>
Clicking this opens the
</button>
```

View File

@ -0,0 +1,81 @@
<script>
import { View } from "svench";
import Multiselect from "../Form/Multiselect.svelte";
import DropdownMenu from "../DropdownMenu/DropdownMenu.svelte";
import Button from "../Button/Button.svelte";
import Icon from "../Icons/Icon.svelte";
const options = ["Red", "Blue", "Yellow", "Green", "Pink", "Very long color name to show text wrapping"];
let anchorLeft;
let dropdownLeft;
</script>
### Position Dropdown Action
This action positions an element close to it's anchor, either above or below it, depending on the amount of space that exists. There's also an option to align the dropdown to the right instead of the left of the anchor. An example of how to use it follows:
```html
<script>
let visible = false;
let anchor;
let align = 'right';
</script>
<button on:click={() => visible = !visible} class="multiselect" bind:this={anchor}>
Clicking this opens the
</button>
{#if visible}
<Portal>
<div use:positionDropdown={{anchor, align}}>
Some content here.
</div>
</Portal>
{/if}
```
Here are some components that currently use this action:
<View name="Multiselect Example">
<Multiselect name="Test" label="Colours" placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<View name="Dropdown on close event example">
<div bind:this={anchorLeft}>
<Button primary on:click={dropdownLeft.show}>
Field Name
<Icon name="arrowdown" />
</Button>
</div>
<DropdownMenu
on:close={() => alert('Closed!')}
bind:this={dropdownLeft}
width="175px"
borderColor="#d1d1d1ff"
anchor={anchorLeft}
align="left">
<ul>
<li>
<Icon name="edit" />
Edit
</li>
<li>
<Icon name="delete" />
Delete
</li>
<li>
<Icon name="sortascending" />
Sort A - Z
</li>
<li>
<Icon name="sortdescending" />
Sort Z - A
</li>
</ul>
</DropdownMenu>
</View>

View File

@ -0,0 +1,14 @@
function resize({ target }) {
target.style.height = "1px"
target.style.height = +target.scrollHeight + "px"
}
export default function text_area_resize(el) {
resize({ target: el })
el.style.overflow = "hidden"
el.addEventListener("input", resize)
return {
destroy: () => el.removeEventListener("input", resize),
}
}

View File

@ -0,0 +1,18 @@
export default function clickOutside(element, callbackFunction) {
function onClick(event) {
if (!element.contains(event.target)) {
callbackFunction()
}
}
document.body.addEventListener("click", onClick, true)
return {
update(newCallbackFunction) {
callbackFunction = newCallbackFunction
},
destroy() {
document.body.removeEventListener("click", onClick, true)
},
}
}

View File

@ -0,0 +1,66 @@
export default function positionDropdown(element, { anchor, align }) {
let positionSide = "top"
let maxHeight = 0
let dimensions = getDimensions(anchor)
function getDimensions() {
const {
bottom,
top: spaceAbove,
left,
width,
} = anchor.getBoundingClientRect()
const spaceBelow = window.innerHeight - bottom
const containerRect = element.getBoundingClientRect()
let y
if (spaceAbove > spaceBelow) {
positionSide = "bottom"
maxHeight = spaceAbove - 20
y = window.innerHeight - spaceAbove
} else {
positionSide = "top"
y = bottom
maxHeight = spaceBelow - 20
}
return {
[positionSide]: y,
left,
width,
containerWidth: containerRect.width,
}
}
function calcLeftPosition() {
return align === "right"
? dimensions.left + dimensions.width - dimensions.containerWidth
: dimensions.left
}
element.style.position = "absolute"
element.style.zIndex = "9999"
element.style.minWidth = `${dimensions.width}px`
element.style.maxHeight = `${maxHeight.toFixed(0)}px`
element.style.transformOrigin = `center ${positionSide}`
element.style[positionSide] = `${dimensions[positionSide]}px`
element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
const resizeObserver = new ResizeObserver(entries => {
entries.forEach(() => {
dimensions = getDimensions()
element.style[positionSide] = `${dimensions[positionSide]}px`
element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
})
})
resizeObserver.observe(anchor)
resizeObserver.observe(element)
return {
destroy() {
resizeObserver.disconnect()
},
}
}

View File

@ -0,0 +1,212 @@
<script>
export let primary = false,
secondary = false,
blue = false,
disabled = false,
translucent = false,
text = false,
red = false,
yellow = false,
orange = false,
green = false,
purple = false,
small = false,
medium = false,
wide = false,
large = false,
href = false
</script>
{#if href}
<a
{href}
class:primary
class:secondary
class:translucent
class:blue
class:red
class:yellow
class:orange
class:green
class:purple
class:small
class:medium
class:wide
class:large
class:text
{disabled}>
<slot />
</a>
{:else}
<button
class:primary
class:secondary
class:translucent
class:blue
class:red
class:yellow
class:orange
class:green
class:purple
class:small
class:medium
class:wide
class:large
class:text
{disabled}
on:click|preventDefault>
<slot />
</button>
{/if}
<style>
button,
a {
font-family: var(--font-sans);
cursor: pointer;
font-weight: 600;
box-sizing: border-box;
overflow: hidden;
border-radius: var(--border-radius-s);
color: white;
padding: var(--spacing-s) var(--spacing-l);
transition: all 0.2s ease 0s;
display: inline-flex;
text-rendering: optimizeLegibility;
text-decoration: none;
min-width: auto;
outline: none;
font-feature-settings: "case" 1, "rlig" 1, "calt" 0;
-webkit-box-align: center;
user-select: none;
flex-shrink: 0;
align-items: center;
justify-content: center;
margin: 0;
border-width: 2px;
border-style: solid;
border-color: transparent;
}
.primary {
color: var(--background);
border-color: var(--ink);
background-color: var(--ink);
}
button.primary:hover:not([disabled]) {
background-color: var(--background);
color: var(--ink);
}
.secondary {
border-color: var(--grey-4);
background-color: var(--background);
color: var(--grey-8);
font-weight: 500;
}
button.secondary:hover:not([disabled]) {
background-color: var(--grey-2);
color: var(--ink);
}
.translucent {
border-color: rgba(0, 0, 0, 0.1);
background-color: rgba(255, 255, 255, 0.1);
color: var(--grey-6);
font-weight: 500;
}
button.translucent:hover:not([disabled]) {
background-color: var(--grey-2);
color: var(--ink);
}
.blue {
background-color: var(--blue);
border-color: var(--blue);
}
button.blue:hover:not([disabled]) {
background-color: var(--blue-light);
color: var(--blue);
}
.red {
border-color: var(--red);
background-color: var(--red);
color: white;
}
button.red:hover:not([disabled]) {
background-color: var(--red-light);
color: var(--red);
}
.yellow {
border-color: var(--yellow);
background-color: var(--yellow);
color: white;
}
button.yellow:hover:not([disabled]) {
background-color: var(--yellow-light);
color: var(--yellow);
}
.orange {
border-color: var(--orange);
background-color: var(--orange);
color: white;
}
button.orange:hover:not([disabled]) {
background-color: var(--orange-light);
color: var(--orange);
}
.green {
border-color: var(--green);
background-color: var(--green);
color: white;
}
button.green:hover:not([disabled]) {
background-color: var(--green-light);
color: var(--green);
}
.purple {
border-color: var(--purple);
background-color: var(--purple);
color: white;
}
button.purple:hover:not([disabled]) {
background-color: var(--purple-light);
color: var(--purple);
}
.text {
background-color: transparent;
color: var(--grey-7);
border: none;
padding: 0;
font-weight: 500;
}
button.text:hover:not([disabled]) {
color: var(--ink);
}
button.text:active:not([disabled]) {
color: var(--blue);
}
.small {
font-size: var(--font-size-xs);
padding: var(--spacing-xs) var(--spacing-m);
}
.medium {
font-size: var(--font-size-m);
padding: var(--spacing-s) var(--spacing-l);
}
.large {
font-size: var(--font-size-l);
padding: var(--spacing-m) var(--layout-l);
}
.wide {
width: 100%;
display: flex;
}
button:disabled {
background-color: var(--grey-4);
cursor: not-allowed;
border-color: var(--grey-4);
color: var(--grey-5);
}
</style>

View File

@ -0,0 +1,138 @@
<script>
import { View } from "svench";
import Button from "./Button.svelte";
import Icon from "../Icons/Icon.svelte";
</script>
<View name="Primary">
<Button primary small on:click={() => alert('Clicked!')}>Small</Button>
<Button primary on:click={() => alert('Clicked!')}>Default</Button>
<Button primary medium on:click={() => alert('Clicked!')}>Medium</Button>
<Button primary large on:click={() => alert('Clicked!')}>Large</Button>
<Button primary wide on:click={() => alert('Clicked!')}>Wide</Button>
</View>
<View name="Secondary">
<Button secondary small on:click={() => alert('Clicked!')}>Small</Button>
<Button secondary on:click={() => alert('Clicked!')}>Default</Button>
<Button secondary medium on:click={() => alert('Clicked!')}>Medium</Button>
<Button secondary large on:click={() => alert('Clicked!')}>Large</Button>
<Button secondary wide on:click={() => alert('Clicked!')}>Wide</Button>
</View>
<View name="Translucent">
<div class="translucent">
<div class="light">
<Button translucent small on:click={() => alert('Clicked!')}>Small</Button>
<Button translucent on:click={() => alert('Clicked!')}>Default</Button>
<Button translucent medium on:click={() => alert('Clicked!')}>Medium</Button>
<Button translucent large on:click={() => alert('Clicked!')}>Large</Button>
<Button translucent wide on:click={() => alert('Clicked!')}>Wide</Button>
</div>
<div class="dark">
<Button translucent small on:click={() => alert('Clicked!')}>Small</Button>
<Button translucent on:click={() => alert('Clicked!')}>Default</Button>
<Button translucent medium on:click={() => alert('Clicked!')}>Medium</Button>
<Button translucent large on:click={() => alert('Clicked!')}>Large</Button>
<Button translucent wide on:click={() => alert('Clicked!')}>Wide</Button>
</div>
</div>
</View>
<View name="Blue">
<Button blue small on:click={() => alert('Clicked!')}>Small</Button>
<Button blue on:click={() => alert('Clicked!')}>Default</Button>
<Button blue medium on:click={() => alert('Clicked!')}>Medium</Button>
<Button blue large on:click={() => alert('Clicked!')}>Large</Button>
<Button blue wide on:click={() => alert('Clicked!')}>Wide</Button>
</View>
<View name="Red">
<Button red small on:click={() => alert('Clicked!')}>Small</Button>
<Button red on:click={() => alert('Clicked!')}>Default</Button>
<Button red medium on:click={() => alert('Clicked!')}>Medium</Button>
<Button red large on:click={() => alert('Clicked!')}>Large</Button>
<Button red wide on:click={() => alert('Clicked!')}>Wide</Button>
</View>
<View name="Yellow">
<Button yellow small on:click={() => alert('Clicked!')}>Small</Button>
<Button yellow on:click={() => alert('Clicked!')}>Default</Button>
<Button yellow medium on:click={() => alert('Clicked!')}>Medium</Button>
<Button yellow large on:click={() => alert('Clicked!')}>Large</Button>
<Button yellow wide on:click={() => alert('Clicked!')}>Wide</Button>
</View>
<View name="Orange">
<Button orange small on:click={() => alert('Clicked!')}>Small</Button>
<Button orange on:click={() => alert('Clicked!')}>Default</Button>
<Button orange medium on:click={() => alert('Clicked!')}>Medium</Button>
<Button orange large on:click={() => alert('Clicked!')}>Large</Button>
<Button orange wide on:click={() => alert('Clicked!')}>Wide</Button>
</View>
<View name="Green">
<Button green small on:click={() => alert('Clicked!')}>Small</Button>
<Button green on:click={() => alert('Clicked!')}>Default</Button>
<Button green medium on:click={() => alert('Clicked!')}>Medium</Button>
<Button green large on:click={() => alert('Clicked!')}>Large</Button>
<Button green wide on:click={() => alert('Clicked!')}>Wide</Button>
</View>
<View name="Purple">
<Button purple small on:click={() => alert('Clicked!')}>Small</Button>
<Button purple on:click={() => alert('Clicked!')}>Default</Button>
<Button purple medium on:click={() => alert('Clicked!')}>Medium</Button>
<Button purple large on:click={() => alert('Clicked!')}>Large</Button>
<Button purple wide on:click={() => alert('Clicked!')}>Wide</Button>
</View>
<View name="Disabled">
<Button primary disabled on:click={() => alert('Clicked!')}>Disabled</Button>
</View>
<View name="Button With Icon">
<Button primary small on:click={() => alert('Clicked!')}>
<Icon name="addrow" direction="n" />
Small
</Button>
<Button primary on:click={() => alert('Clicked!')}>
<Icon name="add" direction="n" />
Default
</Button>
<Button primary medium on:click={() => alert('Clicked!')}>
<Icon name="addcolumn" direction="n" />
Medium
</Button>
<Button primary large on:click={() => alert('Clicked!')}>
<Icon name="copy" direction="n" />
Large
</Button>
<Button primary wide on:click={() => alert('Clicked!')}>
<Icon name="view" direction="n" />
Wide
</Button>
</View>
<View name="Button with symbols">
<Button primary small on:click={() => alert('Clicked!')}>⊗ Small</Button>
<Button primary on:click={() => alert('Clicked!')}>↓ Default</Button>
<Button primary medium on:click={() => alert('Clicked!')}>+ Medium</Button>
<Button primary large on:click={() => alert('Clicked!')}>⋯ Large</Button>
<Button primary wide on:click={() => alert('Clicked!')}>⊕ Wide</Button>
</View>
<View
name="use Knobs"
knobs={{ primary: true, secondary: false, blue: false, red: false, yellow: false, orange: false, green: false, purple: false, disabled: false, small: false, medium: false, large: false, wide: false, text: false }}
let:knobs>
<Button {...knobs} on:click={() => alert('Clicked!')}>Knooby</Button>
</View>
<View name="a link that looks like a button">
<Button primary href="https://google.com">Linkbutton</Button>
</View>
<style>
.translucent {
display: flex;
gap: 20px;
}
.translucent > div {
padding: 10px;
}
.light {
background-color: white;
}
.dark {
background-color: #2a2a2a;
}
</style>

View File

@ -0,0 +1,59 @@
<script>
export let onClose
export let dark = false
export let small = false
</script>
<button class:small class:dark on:click={onClose}>×</button>
<style>
button {
display: block;
box-sizing: border-box;
position: absolute;
font-size: var(--font-size-l);
z-index: 1000;
top: var(--spacing-l);
right: var(--spacing-l);
margin: 0;
padding: 0;
width: 1.5rem;
height: 1.5rem;
border: 0;
color: black;
border-radius: var(--border-radius-xl);
background: white;
transition: transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1),
background 0.2s cubic-bezier(0.25, 0.1, 0.25, 1);
-webkit-appearance: none;
outline: none;
}
button:hover {
background-color: var(--grey-2);
cursor: pointer;
}
button:active {
background-color: var(--grey-4);
cursor: pointer;
}
.small {
font-size: var(--font-size-m);
line-height: 110%;
width: 1.3rem;
height: 1.3rem;
}
.dark {
color: white;
background: black;
}
.dark:hover {
background-color: var(--grey-8);
cursor: pointer;
}
.dark:active {
background-color: var(--grey-9);
cursor: pointer;
}
</style>

View File

@ -0,0 +1,36 @@
<script>
import { View } from "svench";
import Close from "./Close.svelte";
</script>
<style>
div {
display: block;
position: relative;
min-width: 400px;
height: 200px;
border: var(--border-dark);
border-radius: var(--border-radius-l);
}
</style>
<View name="default">
<div>
<Close />
</div>
</View>
<View name="dark color">
<div>
<Close dark />
</div>
</View>
<View name="small">
<div>
<Close small />
</div>
</View>
<View name="small dark">
<div>
<Close small dark />
</div>
</View>

View File

@ -0,0 +1,128 @@
<script>
export let active = false,
text = false,
small = false,
medium = false,
large = false,
blue = false,
green = false,
yellow = false,
purple = false,
red = false,
orange = false,
disabled = false,
href = false
</script>
{#if href}
<a
{href}
class:active
class:small
class:medium
class:large
class:text
class:blue
class:green
class:yellow
class:purple
class:red
class:orange><slot /></a>
{:else}
<button
class:active
class:small
class:medium
class:large
class:text
class:blue
class:green
class:yellow
class:purple
class:red
class:orange
{disabled}
on:click|preventDefault>
<slot />
</button>
{/if}
<style>
button,
a {
font-family: var(--font-sans);
cursor: pointer;
font-weight: 600;
box-sizing: border-box;
overflow: hidden;
transition: all 0.08s ease 0s;
display: inline-flex;
align-items: center;
text-rendering: optimizeLegibility;
text-decoration: none;
outline: none;
-webkit-box-align: center;
user-select: none;
margin: 0;
padding: 0;
}
.text {
background-color: transparent;
color: var(--grey-7);
border: none;
font-weight: 500;
}
button.text:hover:not([disabled]) {
color: var(--ink);
}
button.text:active:not([disabled]) {
color: var(--blue);
}
button.text.active:not([disabled]) {
color: var(--blue);
}
button.text:disabled {
cursor: not-allowed;
color: var(--grey-4);
}
.blue {
color: var(--blue);
}
.green {
color: var(--green);
}
.red {
color: var(--red);
}
.purple {
color: var(--purple);
}
.yellow {
color: var(--yellow);
}
.orange {
color: var(--orange);
}
.small {
font-size: var(--font-size-xs);
margin: 0;
}
.medium {
font-size: var(--font-size-s);
margin: 0;
}
.large {
font-size: var(--font-size-m);
margin: 0;
}
</style>

View File

@ -0,0 +1,69 @@
<script>
import { View } from "svench";
import TextButton from "./TextButton.svelte";
import Icon from "../Icons/Icon.svelte";
</script>
<style>
div {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: var(--spacing-xl);
}
</style>
<View name="Text">
<div>
<TextButton text small on:click={() => alert('Clicked!')}>
<Icon name="view" />
Add View
</TextButton>
<TextButton text medium on:click={() => alert('Clicked!')}>
<Icon name="addcolumn" />
Add Column
</TextButton>
<TextButton text large on:click={() => alert('Clicked!')}>
<Icon name="addrow" />
Add Row
</TextButton>
<TextButton text disabled on:click={() => alert('Clicked!')}>
<Icon name="arrow" direction="w" />
Disabled Text Button
</TextButton>
<TextButton active text on:click={() => alert('Clicked!')}>
<Icon name="calculate" />
Active Calculation
</TextButton>
</div>
</View>
<View name="Colours">
<div>
<TextButton text medium yellow on:click={() => alert('Clicked!')}>
<Icon name="view" />
Add View
</TextButton>
<TextButton text medium blue on:click={() => alert('Clicked!')}>
<Icon name="addcolumn" />
Add Column
</TextButton>
<TextButton text medium purple on:click={() => alert('Clicked!')}>
<Icon name="addrow" />
Add Row
</TextButton>
<TextButton text medium red on:click={() => alert('Clicked!')}>
<Icon name="arrow" />
Delete
</TextButton>
<TextButton text medium green on:click={() => alert('Clicked!')}>
<Icon name="calculate" />
Calculate
</TextButton>
</div>
</View>
<View name="Usage as a link">
<div>
<TextButton green text href="https://google.com">This is a link</TextButton>
</div>
</View>

View File

@ -0,0 +1,45 @@
<script>
import Flatpickr from "svelte-flatpickr"
import { Label, Input } from "../"
import "flatpickr/dist/flatpickr.css"
const PICKER_OPTIONS = {
enableTime: true,
}
export let label
export let placeholder
export let value
export let thin = false
</script>
<div class:thin>
{#if label}
<Label extraSmall grey>{label}</Label>
{/if}
<Flatpickr {placeholder} options={PICKER_OPTIONS} on:change bind:value />
</div>
<style>
:global(.flatpickr-input) {
width: 100%;
min-width: 0;
box-sizing: border-box;
color: var(--ink);
border-radius: 5px;
border: none;
background-color: var(--grey-2);
padding: var(--spacing-m);
font-size: var(--font-size-s);
margin: 0;
outline: none;
border: var(--border-transparent);
}
:global(.flatpickr-input:focus) {
border: var(--border-blue);
}
div.thin :global(.flatpickr-input) {
font-size: var(--font-size-xs);
}
</style>

View File

@ -0,0 +1,17 @@
<script>
import { View } from "svench";
import DatePicker from "./DatePicker.svelte";
function handleChange(event) {
const [fullDate, shortDate, instance] = event.detail
alert("Date is " + fullDate)
}
</script>
<View name="default">
<DatePicker on:change={handleChange} label="Start Date" placeholder="Pick a date" />
</View>
<View name="thin">
<DatePicker on:change={handleChange} label="Start Date" thin placeholder="Pick a date" />
</View>

View File

@ -0,0 +1,88 @@
<script>
import { slide } from "svelte/transition"
import Portal from "svelte-portal"
import clickOutside from "../Actions/click_outside"
export let title
let visible = false
export function show() {
if (visible) {
return
}
visible = true
}
export function hide() {
if (!visible) {
return
}
visible = false
}
function handleKey(e) {
if (visible && e.key === "Escape") {
hide()
}
}
</script>
<svelte:window on:keydown={handleKey} />
{#if visible}
<Portal>
<section class="drawer" transition:slide>
<header>
<div class="text">
<div class="title">{title}</div>
<slot name="description" />
</div>
<div class="controls">
<slot name="buttons" />
<i class="ri-close-fill close" on:click={hide} />
</div>
</header>
<slot name="body" />
</section>
</Portal>
{/if}
<style>
.drawer {
position: absolute;
bottom: 0;
left: 260px;
width: calc(100% - 520px);
background: var(--background);
border: var(--border-light);
z-index: 2;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: var(--border-light);
padding: var(--spacing-m);
}
.controls {
display: grid;
grid-auto-flow: column;
grid-gap: var(--spacing-m);
align-items: center;
}
.close {
font-size: var(--font-size-xl);
cursor: pointer;
}
.title {
font-weight: bold;
margin-right: var(--spacing-m);
}
.text {
display: flex;
}
</style>

View File

@ -0,0 +1,42 @@
<script>
import { View } from "svench";
import Button from "../Button/Button.svelte";
import Drawer from "./Drawer.svelte";
let drawer1;
let drawer2;
</script>
<style>
.drawer {
height: 40vh;
}
</style>
<View name="Basic Bottom Drawer">
<Button primary on:click={drawer1.show}>Open Drawer</Button>
<Drawer
bind:this={drawer1}
title="A basic Drawer with some text"
on:close={() => alert('You closed the drawer!')}>
<p slot="body">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat,
architecto assumenda! Quia harum hic numquam, soluta maiores facere
explicabo vero obcaecati voluptas, qui placeat ad, dolorem recusandae
labore quos? Nisi!
</p>
</Drawer>
</View>
<View name="Basic Bottom Drawer">
<Button blue on:click={drawer2.show}>Open Drawer</Button>
<Drawer
bind:this={drawer2}
title={'Actions'}
on:close={() => alert('You closed the drawer!')}>
<heading slot="buttons">
<Button thin blue on:click={drawer2.hide}>Save</Button>
</heading>
<div slot="description">This describes the drawer!</div>
<div class="drawer" slot="body">Some content here</div>
</Drawer>
</View>

View File

@ -0,0 +1,77 @@
<script>
import Portal from "svelte-portal"
import { createEventDispatcher } from "svelte"
import buildStyle from "../utils/buildStyle"
import positionDropdown from "../Actions/position_dropdown"
import clickOutside from "../Actions/click_outside"
const dispatch = createEventDispatcher()
export let anchor
export let align = "right"
export let borderColor = ""
export const show = () => {
dispatch("open")
open = true
}
export const hide = () => {
dispatch("close")
open = false
}
let open = null
function handleEscape(e) {
if (open && e.key === "Escape") {
hide()
}
}
$: menuStyle = buildStyle({
borderColor,
})
</script>
{#if open}
<Portal>
<div
tabindex="0"
class:open
use:positionDropdown={{ anchor, align }}
use:clickOutside={hide}
style={menuStyle}
on:keydown={handleEscape}
class="menu-container">
<slot />
</div>
</Portal>
{/if}
<style>
.menu-container {
position: fixed;
margin-top: var(--spacing-xs);
outline: none;
box-sizing: border-box;
opacity: 0;
min-width: 200px;
z-index: 2;
color: var(--ink);
font-weight: 400;
height: fit-content !important;
border: var(--border-dark);
border-radius: var(--border-radius-m);
transform: scale(0);
transition: opacity 0.13s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1);
overflow-y: auto;
background: var(--background);
box-shadow: 0 5px 12px rgba(0, 0, 0, 0.15);
}
.open {
transform: scale(1);
opacity: 1;
}
</style>

View File

@ -0,0 +1,161 @@
<script>
import { View } from "svench";
import DropdownMenu from "./DropdownMenu.svelte";
import Button from "../Button/Button.svelte";
import Icon from "../Icons/Icon.svelte";
let anchorRight;
let anchorLeft;
let dropdownRight;
let dropdownLeft;
</script>
<style>
ul {
list-style: none;
padding-left: 0;
margin: 0;
padding: var(--spacing-s) 0;
}
li {
display: flex;
font-family: var(--font-sans);
font-size: var(--font-size-xs);
color: var(--ink);
padding: var(--spacing-s) var(--spacing-m);
margin: auto 0px;
align-items: center;
cursor: pointer;
}
li:hover {
background-color: var(--grey-2);
}
li:active {
color: var(--blue);
}
</style>
<View name="Right Align (default)">
<div bind:this={anchorRight}>
<Button primary on:click={dropdownRight.show}>Right Align</Button>
</div>
<DropdownMenu bind:this={dropdownRight} anchor={anchorRight}>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</DropdownMenu>
</View>
<View name="Left Align">
<div bind:this={anchorLeft}>
<Button primary on:click={dropdownLeft.show}>Left Align</Button>
</div>
<DropdownMenu bind:this={dropdownLeft} anchor={anchorLeft} align="left">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</DropdownMenu>
</View>
<View name="Left Align, TextButton, Small, Icons">
<div bind:this={anchorLeft}>
<Button text on:click={dropdownLeft.show}>
Field Name
<Icon name="arrowdown" />
</Button>
</div>
<DropdownMenu bind:this={dropdownLeft} anchor={anchorLeft} align="left">
<ul>
<li>
<Icon name="edit" />
Edit
</li>
<li>
<Icon name="delete" />
Delete
</li>
<li>
<Icon name="sortascending" />
Sort A - Z
</li>
<li>
<Icon name="sortdescending" />
Sort Z - A
</li>
</ul>
</DropdownMenu>
</View>
<View name="Dropdown menu with slim menu and border color">
<div bind:this={anchorLeft}>
<Button primary on:click={dropdownLeft.show}>
Field Name
<Icon name="arrowdown" />
</Button>
</div>
<DropdownMenu
bind:this={dropdownLeft}
width="175px"
borderColor="#d1d1d1ff"
anchor={anchorLeft}
align="left">
<ul>
<li>
<Icon name="edit" />
Edit
</li>
<li>
<Icon name="delete" />
Delete
</li>
<li>
<Icon name="sortascending" />
Sort A - Z
</li>
<li>
<Icon name="sortdescending" />
Sort Z - A
</li>
</ul>
</DropdownMenu>
</View>
<View name="Dropdown on close event example">
<div bind:this={anchorLeft}>
<Button primary on:click={dropdownLeft.show}>
Field Name
<Icon name="arrowdown" />
</Button>
</div>
<DropdownMenu
on:close={() => alert('Closed!')}
bind:this={dropdownLeft}
width="175px"
borderColor="#d1d1d1ff"
anchor={anchorLeft}
align="left">
<ul>
<li>
<Icon name="edit" />
Edit
</li>
<li>
<Icon name="delete" />
Delete
</li>
<li>
<Icon name="sortascending" />
Sort A - Z
</li>
<li>
<Icon name="sortdescending" />
Sort Z - A
</li>
</ul>
</DropdownMenu>
</View>

View File

@ -0,0 +1,295 @@
<script>
import { Heading, Body, Button } from "../"
import { FILE_TYPES } from "./fileTypes"
const BYTES_IN_KB = 1000
const BYTES_IN_MB = 1000000
export let icons = {
image: "fas fa-file-image",
code: "fas fa-file-code",
file: "fas fa-file",
fileUpload: "fas fa-upload",
}
export let files = []
export let fileSizeLimit = BYTES_IN_MB * 20
export let processFiles
export let handleFileTooLarge
let selectedImageIdx = 0
let fileDragged = false
// Generate a random ID so that multiple dropzones on the page don't conflict
let id = Math.random()
.toString(36)
.substring(7)
$: selectedImage = files ? files[selectedImageIdx] : null
function determineFileIcon(extension) {
const ext = extension.toLowerCase()
if (FILE_TYPES.IMAGE.includes(ext)) return icons.image
if (FILE_TYPES.CODE.includes(ext)) return icons.code
return icons.file
}
async function processFileList(fileList) {
if (Array.from(fileList).some(file => file.size >= fileSizeLimit)) {
handleFileTooLarge(fileSizeLimit, file)
return
}
const processedFiles = await processFiles(fileList)
files = [...processedFiles, ...files]
selectedImageIdx = 0
}
async function removeFile() {
files.splice(selectedImageIdx, 1)
files = files
selectedImageIdx = 0
}
function navigateLeft() {
selectedImageIdx -= 1
}
function navigateRight() {
selectedImageIdx += 1
}
function handleFile(evt) {
processFileList(evt.target.files)
}
function handleDragOver(evt) {
evt.preventDefault()
fileDragged = true
}
function handleDragLeave(evt) {
evt.preventDefault()
fileDragged = false
}
function handleDrop(evt) {
evt.preventDefault()
processFileList(evt.dataTransfer.files)
fileDragged = false
}
</script>
<div
class="dropzone"
on:dragover={handleDragOver}
on:dragleave={handleDragLeave}
on:dragenter={handleDragOver}
on:drop={handleDrop}
class:fileDragged>
{#if selectedImage}
<ul>
<li>
<header>
<div>
<i class={determineFileIcon(selectedImage.extension)} />
<span class="filename">{selectedImage.name}</span>
</div>
<p>
{#if selectedImage.size <= BYTES_IN_MB}
{selectedImage.size / BYTES_IN_KB}KB
{:else}{selectedImage.size / BYTES_IN_MB}MB{/if}
</p>
</header>
<div class="delete-button" on:click={removeFile}>
<i class="ri-close-circle-fill" />
</div>
{#if selectedImageIdx !== 0}
<div class="nav left" on:click={navigateLeft}>
<i class="ri-arrow-left-circle-fill" />
</div>
{/if}
<img alt="preview" src={selectedImage.url} />
{#if selectedImageIdx !== files.length - 1}
<div class="nav right" on:click={navigateRight}>
<i class="ri-arrow-right-circle-fill" />
</div>
{/if}
</li>
</ul>
{/if}
<i class={icons.fileUpload} />
<input {id} type="file" multiple on:change={handleFile} {...$$restProps} />
<i class="ri-upload-cloud-line" />
<p class="drop">Drop your files here</p>
<label for={id}>Select a file from your computer</label>
</div>
<style>
.dropzone {
padding: var(--spacing-l);
border: 2px dashed var(--grey-4);
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
border-radius: 10px;
transition: all 0.3s;
}
.fileDragged {
border: 2px dashed var(--grey-7);
background: var(--blue-light);
}
input[type="file"] {
display: none;
}
label {
font-family: var(--font-sans);
font-size: var(--font-size-s);
cursor: pointer;
overflow: hidden;
color: var(--grey-7);
text-rendering: optimizeLegibility;
min-width: auto;
outline: none;
font-feature-settings: "case" 1, "rlig" 1, "calt" 0;
-webkit-box-align: center;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 100%;
text-decoration: underline;
}
.drop {
font-family: var(--font-sans);
font-size: var(--font-size-s);
margin: 12px 0;
}
div.nav {
padding: var(--spacing-xs);
position: absolute;
display: flex;
align-items: center;
bottom: var(--spacing-s);
border-radius: 5px;
transition: 0.2s transform;
}
.nav:hover {
cursor: pointer;
color: var(--blue);
}
.left {
left: var(--spacing-s);
}
.right {
right: var(--spacing-s);
}
li {
position: relative;
height: 300px;
background: var(--grey-7);
display: flex;
justify-content: center;
border-radius: 10px;
}
img {
border-radius: 10px;
width: 100%;
box-shadow: 0 var(--spacing-s) 12px rgba(0, 0, 0, 0.15);
object-fit: contain;
}
i {
font-size: 2rem;
color: var(--ink);
}
i:hover {
cursor: pointer;
color: var(--background);
}
.file-icon {
color: var(--background);
font-size: 2em;
margin-right: var(--spacing-s);
}
ul {
padding: 0;
display: grid;
grid-gap: var(--spacing-s);
list-style-type: none;
width: 100%;
}
header {
display: flex;
align-items: center;
justify-content: space-between;
position: absolute;
background: linear-gradient(
180deg,
rgb(255, 255, 255),
rgba(255, 255, 255, 0)
);
width: 100%;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
height: 60px;
}
header > div {
color: var(--ink);
display: flex;
align-items: center;
font-size: 12px;
margin-left: var(--spacing-m);
width: 60%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.filename {
overflow: hidden;
margin-left: 5px;
text-overflow: ellipsis;
}
header > p {
color: var(--grey-5);
margin-right: var(--spacing-m);
}
.delete-button {
position: absolute;
top: var(--spacing-s);
right: var(--spacing-s);
padding: var(--spacing-s);
border-radius: 10px;
transition: all 0.3s;
}
.delete-button i {
font-size: 2em;
color: var(--ink);
}
.delete-button:hover {
cursor: pointer;
color: var(--red);
}
</style>

View File

@ -0,0 +1,17 @@
<script>
import { View } from "svench";
import Dropzone from "./Dropzone.svelte";
async function processFiles(files) {
console.log("Processing", files);
return files;
}
function handleFileTooLarge() {
alert("File too large.");
}
</script>
<View name="dropzone">
<Dropzone {processFiles} {handleFileTooLarge} />
</View>

View File

@ -0,0 +1,5 @@
export const FILE_TYPES = {
IMAGE: ["png", "tiff", "gif", "raw", "jpg", "jpeg", "svg"],
CODE: ["js", "rs", "py", "java", "rb", "hs", "yml"],
DOCUMENT: ["odf", "docx", "doc", "pdf", "csv"],
}

View File

@ -0,0 +1,140 @@
<script>
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
export let checked = false
export let value
export let name
export let disabled
function handleChange() {
if (disabled) return
checked = !checked
dispatch("change", checked)
}
</script>
<div class="container">
<input
{disabled}
on:change={handleChange}
{value}
bind:checked
type="checkbox"
{name}
class="checkbox"
id={value} />
<div class="checkbox-container" on:click={handleChange}>
<div class:disabled class="check-div" class:checked>
<div class="tick_mark" />
</div>
</div>
<slot />
</div>
<style>
.container {
display: flex;
gap: var(--spacing-s);
}
.checkbox-container {
position: relative;
z-index: 0;
}
.checkbox {
display: none;
}
.check-div {
position: relative;
width: 20px;
height: 20px;
background-color: var(--grey-2);
cursor: pointer;
transition: 0.2s ease transform, 0.2s ease background-color,
0.2s ease box-shadow;
overflow: hidden;
border-radius: 4px;
}
.check-div:before {
content: "";
position: absolute;
top: 50%;
right: 0;
left: 0;
width: 12px;
height: 12px;
margin: 0 auto;
background-color: var(--background);
transform: translateY(-50%);
transition: 0.2s ease width, 0.2s ease height;
border-radius: 2px;
}
.check-div:active {
transform: translateY(-50%) scale(0.9);
}
.tick_mark {
position: absolute;
top: 50%;
left: 6px;
width: 5px;
height: 4px;
margin: 0 auto;
transform: rotateZ(-40deg);
}
.tick_mark:before,
.tick_mark:after {
content: "";
position: absolute;
background-color: var(--ink);
border-radius: 2px;
opacity: 0;
transition: 0.2s ease transform, 0.2s ease opacity;
}
.tick_mark:before {
left: 0;
bottom: 0;
width: 2px;
height: 6px;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.23);
transform: translateY(-68px);
}
.tick_mark:after {
left: 0;
bottom: 0;
width: 12px;
height: 2px;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.23);
transform: translateX(78px);
}
.check-div.disabled:active {
transform: none;
}
.checked {
background-color: var(--grey-2);
}
.checked.disabled {
background-color: var(--grey-5);
}
.checked:before {
width: 0;
height: 0;
}
.checked .tick_mark:before,
.checked .tick_mark:after {
transform: translate(0);
opacity: 1;
}
</style>

View File

@ -0,0 +1,67 @@
<script>
import { View } from "svench";
import Checkbox from "./Checkbox.svelte";
let checked = false
let menu = [
{text: 'Cookies and cream', checked: false},
{text: 'Mint choc chip', checked: false},
{text: 'Raspberry ripple', checked: true}
];
</script>
<View name="Single checkbox">
<Checkbox bind:checked value="value">
<label for="value">One single checkbox with text</label>
</Checkbox>
</View>
<View name="Single disabled checkbox">
<Checkbox disabled checked value="value">
<label for="someOtherValue">A disabled checkbox</label>
</Checkbox>
</View>
<View name="No text">
<Checkbox bind:checked value="somevalue" />
</View>
## Multiple checkboxes
Use an array and an each block to use multiple checkboxes
```svelte
<script>
let menu = [
{text: 'Cookies and cream', checked: false},
{text: 'Mint choc chip', checked: false},
{text: 'Raspberry ripple', checked: true}
];
</script>
{#each menu as {text, checked}}
<Checkbox value={text} bind:checked>
<label for={text}>{text}</label>
</Checkbox>
{/each}
```
<View name="Multiple checkboxes">
<div class="container">
{#each menu as {text, checked}}
<Checkbox value={text} bind:checked>
<label for={text}>{text}</label>
</Checkbox>
{/each}
</div>
</View>
<style>
label {
display: grid;
place-items: center;
}
.container {
display: grid;
grid-gap: 10px;
}
</style>

View File

@ -0,0 +1,158 @@
<script>
import Icon from "../Icons/Icon.svelte"
import Label from "../Styleguide/Label.svelte"
import { createEventDispatcher } from "svelte"
export let label = undefined
export let value = ""
export let name = undefined
export let thin = false
export let extraThin = false
export let secondary = false
export let outline = false
export let disabled = false
const dispatch = createEventDispatcher()
let focus = false
const updateValue = e => {
value = e.target.value
}
function handleFocus(e) {
focus = true
dispatch("focus", e)
}
function handleBlur(e) {
focus = false
dispatch("blur", e)
}
</script>
{#if label}
<Label extraSmall grey forAttr={name}>{label}</Label>
{/if}
<div class="container" class:disabled class:secondary class:outline class:focus>
<select
{name}
class:thin
class:extraThin
class:secondary
{disabled}
on:change
on:focus={handleFocus}
on:blur={handleBlur}
bind:value>
<slot />
</select>
<slot name="custom-input" />
<input
class:thin
class:extraThin
class:secondary
class:disabled
{disabled}
on:change={updateValue}
on:input={updateValue}
on:focus={handleFocus}
on:blur={e => {
updateValue(e)
handleBlur(e)
}}
value={value || ''}
type="text" />
<div class="pointer editable-pointer">
<Icon name="arrowdown" />
</div>
</div>
<style>
.container {
position: relative !important;
display: block;
border-radius: var(--border-radius-s);
border: var(--border-transparent);
background-color: var(--background);
}
.container.outline {
border: var(--border-dark);
}
.container.focus {
border: var(--border-blue);
}
input,
select {
border-radius: var(--border-radius-s);
font-size: var(--font-size-m);
outline: none;
border: none;
color: var(--ink);
text-align: left;
background-color: transparent;
}
select {
display: block !important;
width: 100% !important;
padding: var(--spacing-m) 2rem var(--spacing-m) var(--spacing-m);
appearance: none !important;
-webkit-appearance: none !important;
-moz-appearance: none !important;
align-items: center;
white-space: pre;
opacity: 0;
}
input {
position: absolute;
top: 0;
left: 0;
width: calc(100% - 30px);
height: 100%;
border: none;
box-sizing: border-box;
padding: var(--spacing-m) 0 var(--spacing-m) var(--spacing-m);
}
select.thin,
input.thin {
font-size: var(--font-size-xs);
}
select.extraThin,
input.extraThin {
font-size: var(--font-size-xs);
padding: var(--spacing-s) 0 var(--spacing-s) var(--spacing-m);
}
.secondary {
background: var(--grey-2);
}
select:disabled,
input:disabled,
.disabled {
background: var(--grey-4);
color: var(--grey-6);
}
.pointer {
right: 0 !important;
top: 0 !important;
bottom: 0 !important;
position: absolute !important;
pointer-events: none !important;
align-items: center !important;
display: flex !important;
box-sizing: border-box;
}
.editable-pointer {
border-style: solid;
border-width: 0 0 0 1px;
border-color: var(--grey-4);
padding-left: var(--spacing-xs);
}
.editable-pointer :global(svg) {
margin-right: var(--spacing-xs);
fill: var(--ink);
}
</style>

View File

@ -0,0 +1,57 @@
<script>
import { View } from "svench";
import Select from "./Select.svelte";
import DataList from "./DataList.svelte";
import Spacer from "../Spacer/Spacer.svelte"
const options = ["Chocolate", "Vanilla", "Strawberry Cheesecake"];
</script>
<View name="default">
<DataList name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>
<View name="secondary">
<DataList secondary name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>
<View name="outline">
<DataList outline name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>
<View name="disabled">
<DataList disabled name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>
<View name="thin">
<DataList thin name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>
<View name="extraThin">
<DataList extraThin name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>

View File

@ -0,0 +1,190 @@
<script>
import { createEventDispatcher } from "svelte"
import Button from "../Button/Button.svelte"
import Label from "../Styleguide/Label.svelte"
const dispatch = createEventDispatcher()
export let name = undefined
export let label = undefined
export let outline = false
export let presentation = false
export let thin = false
export let extraThin = false
export let large = false
export let border = false
export let edit = false
export let disabled = false
export let type = undefined
export let placeholder = ""
export let value = ""
export let error = false
export let validator = () => {}
// This section handles the edit mode and dispatching of things to the parent when saved
let editMode = false
const updateValue = e => {
if (type === "number") {
const num = parseFloat(e.target.value)
value = isNaN(num) ? "" : num
} else {
value = e.target.value
}
}
const save = () => {
editMode = false
dispatch("save", value)
}
const enableEdit = () => {
editMode = true
}
</script>
<div class="container">
{#if label || edit}
<div class="label-container">
{#if label}
<Label extraSmall grey forAttr={name}>{label}</Label>
{/if}
{#if edit}
<div class="controls">
<Button small secondary disabled={editMode} on:click={enableEdit}>
Edit
</Button>
<Button small blue disabled={!editMode} on:click={save}>Save</Button>
</div>
{/if}
</div>
{/if}
<input
class:outline
class:presentation
class:thin
class:extraThin
class:large
class:border
on:change
on:input
on:change={updateValue}
on:input={updateValue}
on:blur={updateValue}
use:validator
disabled={disabled || (edit && !editMode)}
value={value == null ? '' : value}
{type}
{name}
{placeholder} />
{#if error}
<div class="error">{error}</div>
{/if}
</div>
<style>
.container {
min-width: 0;
display: flex;
flex-direction: column;
}
.label-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: flex-end;
margin-bottom: var(--spacing-s);
}
.label-container :global(label) {
margin-bottom: 0;
}
.controls {
align-items: center;
display: grid;
grid-template-columns: auto auto;
grid-gap: 12px;
margin-left: auto;
padding-left: 12px;
}
.controls :global(button) {
min-width: 100px;
font-size: var(--font-size-s);
border-radius: var(--rounded-small);
}
input {
min-width: 0;
box-sizing: border-box;
color: var(--ink);
font-size: var(--font-size-s);
border-radius: var(--border-radius-s);
border: none;
background-color: var(--grey-2);
padding: var(--spacing-m);
margin: 0;
outline: none;
font-family: var(--font-sans);
border: var(--border-transparent);
transition: all 0.2s ease-in-out;
}
input.presentation {
background-color: var(--background);
border: var(--background) 2px solid;
}
input.presentation:hover {
background-color: var(--grey-2);
border: var(--grey-4) 2px solid;
}
input.thin {
font-size: var(--font-size-xs);
}
input.extraThin {
font-size: var(--font-size-xs);
padding: var(--spacing-s) var(--spacing-m);
}
input.large {
font-size: var(--font-size-m);
padding: var(--spacing-l);
}
input.border {
border: var(--border-grey-2);
}
input.border:active {
border: var(--border-blue);
}
input.border:focus {
border: var(--border-blue);
}
input.outline {
border: var(--border-light-2);
background: var(--background);
}
input.outline:active {
border: var(--border-blue);
}
input.outline:focus {
border: var(--border-blue);
}
input:hover {
border: var(--grey-4) 2px solid;
}
input::placeholder {
color: var(--grey-6);
}
input:focus {
border: var(--border-blue);
}
input:disabled {
background: var(--grey-4);
color: var(--grey-6);
}
.error {
margin-top: 10px;
font-size: var(--font-size-xs);
font-family: var(--font-sans);
line-height: 1.17;
color: var(--red);
}
</style>

View File

@ -0,0 +1,62 @@
<script>
import { View } from "svench";
import Input from "./Input.svelte";
import Button from "../Button/Button.svelte";
</script>
<View name="default">
<Input placeholder="Enter your name" label="Name" />
</View>
<View name="presentation">
<Input presentation placeholder="Enter your name" label="Name" />
</View>
<View name="outline">
<Input outline placeholder="Enter your name" label="Name" />
</View>
<View name="disabled">
<Input disabled placeholder="Enter your name" label="Name" />
</View>
<View name="disabled with value">
<Input value="Some text" disabled placeholder="Enter your name" label="Name" />
</View>
<View name="thin">
<Input thin placeholder="Enter your name" label="Name" />
</View>
<View name="extraThin">
<Input extraThin placeholder="Enter your name" label="Name" />
</View>
<View name="large">
<Input large placeholder="Enter your name" label="Name" />
</View>
<View name="border">
<Input border presentation placeholder="Enter your name" label="Name" />
</View>
<View name="number">
<Input type="number" placeholder="Enter your age" label="Age" />
</View>
<View name="with edit buttons">
<Input
thin
edit
placeholder="Enter your name"
label="Name"
on:save={console.log} />
</View>
<View name="with error message">
<Input
placeholder="Enter your name"
label="Name"
error="This is an error message!"
on:save={console.log} />
</View>

View File

@ -0,0 +1,324 @@
<script>
import Portal from "svelte-portal"
import { afterUpdate } from "svelte"
import { createEventDispatcher } from "svelte"
import { fly } from "svelte/transition"
import Label from "../Styleguide/Label.svelte"
const xPath =
"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
import positionDropdown from "../Actions/position_dropdown"
import clickOutside from "../Actions/click_outside"
const dispatch = createEventDispatcher()
export let value = []
export let label = undefined
export let align = "left"
export let secondary = false
export let outline = false
export let disabled = false
export let placeholder = undefined
export let extraThin = false
let options = []
let optionsVisible = false
let slot
let anchor
$: lookupMap = mapValues(value)
$: selectedOptions = options.filter(option => lookupMap[option.value])
afterUpdate(() => {
// Update available options
const domOptions = Array.from(slot.querySelectorAll("option"))
options = domOptions.map(option => ({
value: option.value,
name: option.textContent,
}))
})
function mapValues(value) {
let map = {}
if (value) {
value.forEach(option => {
map[option] = true
})
}
return map
}
function add(val) {
value = [...value, val]
dispatch("change", value)
}
function remove(val) {
value = value.filter(option => option !== val)
dispatch("change", value)
}
function showOptions(show) {
optionsVisible = show
}
function handleClick() {
showOptions(!optionsVisible)
}
function handleOptionMousedown(e) {
const value = e.target.dataset.value
if (value == null) {
return
}
if (lookupMap[value]) {
remove(value)
} else {
add(value)
}
}
</script>
{#if label}
<Label extraSmall grey>{label}</Label>
{/if}
<div class="multiselect" bind:this={anchor}>
<div class="tokens-wrapper">
<div
class="tokens"
class:outline
class:disabled
class:secondary
class:extraThin
class:optionsVisible
on:click|self={handleClick}
class:empty={!value || !value.length}>
{#each selectedOptions as option}
<div
class="token"
class:extraThin
data-id={option.value}
on:click|self={handleClick}>
<span>{option.name}</span>
<div
class="token-remove"
title="Remove {option.name}"
on:click={() => remove(option.value)}>
<svg
class="icon-clear"
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24">
<path d={xPath} />
</svg>
</div>
</div>
{/each}
{#if !value || !value.length}
{#if placeholder && placeholder.length}
<div class:disabled class="placeholder">{placeholder}</div>
{:else}
<div class="placeholder">&nbsp;</div>
{/if}
{/if}
</div>
</div>
<select bind:this={slot} type="multiple" class="hidden">
<slot />
</select>
{#if optionsVisible}
<Portal>
<ul
class="options"
use:positionDropdown={{ anchor, align }}
use:clickOutside={() => showOptions(false)}
transition:fly={{ duration: 200, y: 5 }}
on:mousedown|preventDefault={handleOptionMousedown}>
{#each options as option}
<li
class:selected={lookupMap[option.value]}
data-value={option.value}>
{option.name}
</li>
{/each}
{#if !options.length}
<li class="no-results">No results</li>
{/if}
</ul>
</Portal>
{/if}
</div>
<style>
.multiselect {
position: relative;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
font-family: var(--font-sans);
min-width: 0;
}
.multiselect:hover {
border-bottom-color: hsl(0, 0%, 50%);
}
.tokens-wrapper {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
flex: 0 1 auto;
}
.tokens {
align-items: center;
display: flex;
flex-wrap: wrap;
position: relative;
width: 0;
flex: 1 1 auto;
background-color: var(--background);
border-radius: var(--border-radius-m);
padding: 0 var(--spacing-m) calc(var(--spacing-m) - var(--spacing-xs))
calc(var(--spacing-m) / 2);
border: var(--border-transparent);
}
.tokens.disabled {
background-color: var(--grey-4);
pointer-events: none;
}
.tokens.outline {
border: var(--border-dark);
}
.tokens.secondary {
background-color: var(--grey-2);
}
.tokens.extraThin {
padding: 0 var(--spacing-m) calc(var(--spacing-s) - var(--spacing-xs))
calc(var(--spacing-m) / 2);
}
.tokens:hover {
cursor: pointer;
}
.tokens.optionsVisible {
border: var(--border-blue);
}
.tokens.empty {
padding: var(--spacing-m);
font-size: var(--font-size-xs);
user-select: none;
}
.tokens.empty.extraThin {
padding: var(--spacing-s) var(--spacing-m);
}
.tokens::after {
width: 100%;
left: 0;
}
.token {
font-size: var(--font-size-xs);
background-color: var(--ink);
color: var(--background);
border-radius: var(--border-radius-l);
display: flex;
flex-direction: row;
align-items: center;
margin: calc(var(--spacing-m) - var(--spacing-xs)) 0 0
calc(var(--spacing-m) / 2);
max-height: 1.3rem;
padding: var(--spacing-xs) var(--spacing-s);
transition: background-color 0.3s;
white-space: nowrap;
overflow: hidden;
}
.token.extraThin {
margin: calc(var(--spacing-s) - var(--spacing-xs)) 0 0
calc(var(--spacing-m) / 2);
}
.token span {
pointer-events: none;
user-select: none;
flex: 1 1 auto;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.token .token-remove {
align-items: center;
background-color: var(--grey-7);
border-radius: 50%;
display: flex;
justify-content: center;
height: 1rem;
width: 1rem;
margin: calc(-1 * var(--spacing-xs)) 0 calc(-1 * var(--spacing-xs))
var(--spacing-xs);
flex: 0 0 auto;
}
.token path {
fill: var(--background);
}
.token .token-remove:hover {
background-color: var(--grey-6);
cursor: pointer;
}
.placeholder {
pointer-events: none;
color: var(--ink);
}
.placeholder.disabled {
color: var(--grey-6);
}
.icon-clear path {
fill: white;
}
.options {
left: 0;
list-style: none;
margin-block-end: 0;
margin-block-start: 0;
overflow-y: auto;
padding-inline-start: 0;
position: absolute;
border: var(--border-dark);
border-radius: var(--border-radius-m);
box-shadow: 0 5px 12px rgba(0, 0, 0, 0.15);
margin: var(--spacing-xs) 0;
padding: var(--spacing-s) 0;
background-color: var(--background);
max-height: 200px;
}
li {
cursor: pointer;
padding: var(--spacing-s) var(--spacing-m);
font-size: var(--font-size-xs);
color: var(--ink);
}
li.selected {
background-color: var(--blue);
color: white;
}
li:not(.selected):hover {
background-color: var(--grey-1);
}
li.no-results:hover {
background-color: white;
cursor: initial;
}
.hidden {
height: 0;
overflow: hidden;
visibility: hidden;
padding: 0;
margin: 0;
border: 0;
outline: 0;
}
</style>

View File

@ -0,0 +1,63 @@
<script>
import { View } from "svench";
import Multiselect from "./Multiselect.svelte";
const options = ["Red", "Blue", "Yellow", "Green", "Pink", "Very long color name to show text wrapping"];
</script>
<View name="default">
<Multiselect name="Test" label="Colours" placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<View name="right aligned">
<div class="max-width">
<Multiselect align="right" name="Test" label="Colours" placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</div>
</View>
<View name="secondary">
<Multiselect name="Test" label="Colours" secondary placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<View name="outline">
<Multiselect name="Test" label="Colours" outline placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<View name="disabled">
<Multiselect name="Test" label="Colours" disabled placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<View name="extraThin">
<Multiselect name="Test" label="Colours" extraThin placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<style>
.max-width {
align-self: flex-end;
max-width: 150px;
}
</style>

View File

@ -0,0 +1,140 @@
<script>
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
export let value
export let group
export let name
export let disabled = false
function handleChange() {
if (disabled) return
group = value
dispatch("change", group)
}
</script>
<div class="container">
<input
{disabled}
on:change={handleChange}
{value}
bind:group
type="radio"
{name}
class="checkbox"
id={value} />
<div class="checkbox-container" on:click={handleChange}>
<div class:disabled class="check-div" class:checked={group === value}>
<div class="tick_mark" />
</div>
</div>
<slot />
</div>
<style>
.container {
display: flex;
gap: var(--spacing-s);
}
.checkbox-container {
position: relative;
z-index: 0;
}
.checkbox {
display: none;
}
.check-div {
position: relative;
width: 20px;
height: 20px;
background-color: var(--grey-2);
cursor: pointer;
transition: 0.2s ease transform, 0.2s ease background-color,
0.2s ease box-shadow;
overflow: hidden;
border-radius: 4px;
}
.check-div:before {
content: "";
position: absolute;
top: 50%;
right: 0;
left: 0;
width: 12px;
height: 12px;
margin: 0 auto;
background-color: var(--background);
transform: translateY(-50%);
transition: 0.2s ease width, 0.2s ease height;
border-radius: 2px;
}
.check-div:active {
transform: translateY(-50%) scale(0.9);
}
.tick_mark {
position: absolute;
top: 50%;
left: 6px;
width: 5px;
height: 4px;
margin: 0 auto;
transform: rotateZ(-40deg);
}
.tick_mark:before,
.tick_mark:after {
content: "";
position: absolute;
background-color: var(--ink);
border-radius: 2px;
opacity: 0;
transition: 0.2s ease transform, 0.2s ease opacity;
}
.tick_mark:before {
left: 0;
bottom: 0;
width: 2px;
height: 6px;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.23);
transform: translateY(-68px);
}
.tick_mark:after {
left: 0;
bottom: 0;
width: 12px;
height: 2px;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.23);
transform: translateX(78px);
}
.check-div.disabled:active {
transform: none;
}
.checked {
background-color: var(--grey-2);
}
.checked.disabled {
background-color: var(--grey-5);
}
.checked:before {
width: 0;
height: 0;
}
.checked .tick_mark:before,
.checked .tick_mark:after {
transform: translate(0);
opacity: 1;
}
</style>

View File

@ -0,0 +1,64 @@
<script>
import { View } from "svench";
import Radio from "./Radio.svelte";
let selected = 'Cookies and cream'
let selected2 = 'Mint choc chip'
let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
</script>
## Multiple checkboxes
Use an array and an each block to use the radio button.
```svelte
<script>
let selected = 'Cookies and cream'
let selected2 = 'Cookies and cream'
let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
</script>
{#each menu as flavour}
<Radio name="Ice Cream Flavour" value={flavour} bind:group={selected} label={flavour} showLabel/>
{/each}
```
<View name="Multiple radio buttons">
<div class="container">
{#each menu as flavour}
<Radio name="Ice Cream Flavour" value={flavour} bind:group={selected}>
<label for={flavour}>{flavour}</label>
</Radio>
{/each}
</div>
</View>
<View name="Disabled Radio inputs">
<div class="container">
{#each menu as flavour}
<Radio disabled name="Ice Cream Flavour" value={flavour} bind:group={selected2}>
<label for={flavour}>{flavour}</label>
</Radio>
{/each}
</div>
</View>
<style>
label {
display: grid;
place-items: center;
}
.container {
display: grid;
grid-gap: 10px;
}
</style>

View File

@ -0,0 +1,59 @@
<script>
import * as Quill from "quill"
import * as MarkdownIt from "markdown-it"
import TurndownService from "turndown"
import { onMount } from "svelte"
import "quill/dist/quill.snow.css"
const convertMarkdown = new MarkdownIt()
convertMarkdown.set({
html: true,
})
const turndownService = new TurndownService()
export let value = ""
export let options = null
export let width = 400
let quill
let container
let defaultOptions = {
modules: {
toolbar: [
[{ header: [1, 2, 3, false] }],
["bold", "italic", "underline", "strike"],
],
},
placeholder: "Type something...",
theme: "snow", // or 'bubble'
}
let mergedOptions = { ...defaultOptions, ...options }
const updateContent = () => {
value = turndownService.turndown(quill.container.firstChild.innerHTML)
}
onMount(() => {
quill = new Quill(container, mergedOptions)
if (value)
quill.clipboard.dangerouslyPasteHTML(convertMarkdown.render(value + "\n"))
quill.on("text-change", updateContent)
return () => {
quill.off("text-change", updateContent)
}
})
</script>
<svelte:head>
{#if mergedOptions.theme !== 'snow'}
<link
rel="stylesheet"
href="//cdn.quilljs.com/1.3.6/quill.{mergedOptions.theme}.css" />
{/if}
</svelte:head>
<div style="width: {width}px">
<div bind:this={container} />
</div>

View File

@ -0,0 +1,40 @@
<script>
import { View } from "svench";
import RichText from "./RichText.svelte";
const options = { placeholder: "this is not the default value!" };
let value;
</script>
### Rich Text Component
This component uses the QuillJS library to add Rich Text editing functionality.
It exposes a <code>content</code> variable that you can bind to in order to get Markdown out of the component.
As well as the content you can also pass in an option object that looks like so:
```js
let options = {
modules: {
toolbar: [
[{ header: [1, 2, 3, false] }],
['bold', 'italic', 'underline', 'strike']
]
},
placeholder: 'Type something...',
theme: 'snow'
}
```
<View name="default">
<RichText bind:value />
</View>
<View name="passing in Markdown">
<RichText value="# This is an h1 heading!" />
</View>
<View name="passing in custom options">
<RichText {options} />
</View>

View File

@ -0,0 +1,95 @@
<script>
import Icon from "../Icons/Icon.svelte"
import Label from "../Styleguide/Label.svelte"
export let value = ""
export let name = undefined
export let label = undefined
export let thin = false
export let extraThin = false
export let secondary = false
export let outline = false
export let disabled = false
</script>
<div>
{#if label}
<Label extraSmall grey forAttr={name}>{label}</Label>
{/if}
<div class="relative">
<select
{name}
class:thin
class:extraThin
class:secondary
class:outline
{disabled}
on:change
bind:value>
<slot />
</select>
<div class="pointer">
<Icon name="arrowdown" />
</div>
</div>
</div>
<style>
select {
font-family: var(--font-sans);
display: block !important;
width: 100% !important;
border-radius: var(--border-radius-s);
border: none;
text-align: left;
color: var(--ink);
font-size: var(--font-size-s);
padding: var(--spacing-m) 2rem var(--spacing-m) var(--spacing-m) !important;
appearance: none !important;
-webkit-appearance: none !important;
-moz-appearance: none !important;
align-items: center;
white-space: pre;
outline: none;
border: var(--border-transparent);
background-color: var(--background);
}
select.thin {
padding: var(--spacing-m);
font-size: var(--font-size-xs);
}
select.extraThin {
padding: var(--spacing-s) 2rem var(--spacing-s) var(--spacing-m) !important;
font-size: var(--font-size-xs);
}
select.secondary {
background: var(--grey-2);
}
select.outline {
border: var(--border-light-2);
}
select:focus {
border: var(--border-blue);
}
select:disabled {
background: var(--grey-4);
color: var(--grey-6);
}
.relative {
position: relative !important;
display: block;
}
.pointer {
right: 0 !important;
top: 0 !important;
bottom: 0 !important;
position: absolute !important;
pointer-events: none !important;
padding-left: 0.5rem !important;
align-items: center !important;
display: flex !important;
color: var(--ink);
}
</style>

View File

@ -0,0 +1,62 @@
<script>
import { View } from "svench";
import Select from "./Select.svelte";
import Spacer from "../Spacer/Spacer.svelte"
const options = ["Chocolate", "Vanilla", "Strawberry Cheesecake"];
</script>
<View name="default">
<Select name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>
<View name="secondary">
<Select secondary name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>
<View name="outline">
<Select outline name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>
<View name="disabled">
<Select disabled name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>
<View name="thin">
<Select thin name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>
<View name="extraThin">
<Select extraThin name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>

View File

@ -0,0 +1,88 @@
<script>
import Label from "../Styleguide/Label.svelte"
export let label
export let min = 0
export let max = 100
export let step = 1
export let value
export let showValue = false
export let showRange = false
</script>
<div>
{#if label}
<Label extraSmall grey>
{label}
{#if showValue && value != null}({value}){/if}
</Label>
{/if}
<div class="container">
{#if showRange && min != null}<span>{min}</span>{/if}
<input type="range" bind:value {min} {max} {step} />
{#if showRange && max != null}<span>{max}</span>{/if}
</div>
</div>
<style>
.container {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: var(--spacing-s);
}
span {
flex: 0 0 auto;
color: var(--grey-5);
font-family: var(--font-sans);
font-size: var(--font-size-xs);
font-weight: 400;
}
input[type="range"] {
width: 100%;
margin: 0;
background-color: transparent;
-webkit-appearance: none;
flex: 1 1 auto;
}
input[type="range"]:focus {
outline: none;
}
input[type="range"]::-webkit-slider-runnable-track {
background: var(--grey-4);
border-radius: 9px;
width: 100%;
height: 18px;
cursor: pointer;
padding: 0 2px;
}
input[type="range"]::-webkit-slider-thumb {
width: 14px;
height: 14px;
background: white;
border-radius: 100%;
cursor: pointer;
-webkit-appearance: none;
border: none;
margin-top: 2px;
}
input[type="range"]::-moz-range-track {
background: var(--grey-4);
border-radius: 9px;
width: 100%;
height: 18px;
cursor: pointer;
padding: 0 2px;
}
input[type="range"]::-moz-range-thumb {
width: 14px;
height: 14px;
background: white;
border-radius: 100%;
cursor: pointer;
border: none;
}
</style>

View File

@ -0,0 +1,26 @@
<script>
import { View } from "svench";
import Slider from "./Slider.svelte";
</script>
<View name="default">
<Slider label=Quantity value="50" />
</View>
<View name="show value">
<Slider label="Quantity" value="50" showValue />
</View>
<View name="show range">
<Slider label="Quantity" value="25" showValue showRange min="0" max="100" />
</View>
<View name="custom min and max">
<Slider label="Quantity" value="350" showValue showRange min="50" max="500" />
</View>
<View name="custom step">
<Slider label="Quantity" value="25" step="25" showValue showRange min="0" max="100" />
</View>

View File

@ -0,0 +1,132 @@
<script>
import { createEventDispatcher } from "svelte"
import Button from "../Button/Button.svelte"
import Label from "../Styleguide/Label.svelte"
import text_area_resize from "../Actions/autoresize_textarea.js"
const dispatch = createEventDispatcher()
export let name = false
export let label = false
export let thin = false
export let extraThin = false
export let edit = false
export let disabled = false
export let placeholder
export let validator = () => {}
export let value = ""
export const getCaretPosition = () => {
return { start: textarea.selectionStart, end: textarea.selectionEnd }
}
let textarea
// This section handles the edit mode and dispatching of things to the parent when saved
let editMode = false
const save = () => {
editMode = false
dispatch("save", value)
}
const enableEdit = () => {
editMode = true
}
</script>
<div class="container">
{#if label || edit}
<div class="label-container">
{#if label}
<Label extraSmall grey forAttr={name}>{label}</Label>
{/if}
{#if edit}
<div class="controls">
<Button small secondary disabled={editMode} on:click={enableEdit}>
Edit
</Button>
<Button small blue disabled={!editMode} on:click={save}>Save</Button>
</div>
{/if}
</div>
{/if}
<textarea
class:thin
class:extraThin
bind:value
bind:this={textarea}
on:change
disabled={disabled || (edit && !editMode)}
{placeholder}
{name}
use:text_area_resize />
</div>
<style>
.container {
min-width: 0;
display: flex;
flex-direction: column;
}
.label-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: flex-end;
margin-bottom: var(--spacing-s);
}
.label-container :global(label) {
margin-bottom: 0;
}
.controls {
align-items: center;
display: grid;
grid-template-columns: auto auto;
grid-gap: 12px;
margin-left: auto;
padding-left: 12px;
}
.controls :global(button) {
min-width: 100px;
font-size: var(--font-size-s);
border-radius: var(--rounded-small);
}
textarea {
min-width: 0;
color: var(--ink);
font-size: var(--font-size-s);
font-family: var(--font-sans);
border: none;
border-radius: var(--border-radius-s);
background-color: var(--grey-2);
padding: var(--spacing-m);
margin: 0;
border: var(--border-transparent);
outline: none;
}
textarea::placeholder {
color: var(--grey-6);
}
textarea.thin {
font-size: var(--font-size-xs);
}
textarea.extraThin {
font-size: var(--font-size-xs);
padding: var(--spacing-s) var(--spacing-m);
}
textarea:focus {
border: var(--border-blue);
}
textarea:disabled {
background: var(--grey-4);
}
textarea:disabled {
background: var(--grey-4);
}
textarea:disabled::placeholder {
color: var(--grey-6);
}
</style>

View File

@ -0,0 +1,30 @@
<script>
import { View } from "svench";
import TextArea from "./TextArea.svelte";
import Button from "../Button/Button.svelte";
</script>
<View name="default">
<TextArea placeholder="Enter your email text" label="Email Body" />
</View>
<View name="disabled">
<TextArea disabled placeholder="Enter your email text" label="Email Body" />
</View>
<View name="no label">
<TextArea placeholder="Enter your email text" />
</View>
<View name="thin">
<TextArea thin placeholder="Enter your email text" label="Email Body" />
</View>
<View name="extraThin">
<TextArea extraThin placeholder="Enter your email text" label="Email Body" />
</View>
<View name="with buttons">
<TextArea edit placeholder="Enter your email text" label="Email Body" />
</View>

View File

@ -0,0 +1,110 @@
<script>
export let name = undefined
export let text = ""
export let checked = false
export let disabled = false
export let screenreader = true
export let thin = false
</script>
<label for={name} class="container">
<div class="toggle">
<input
id={name}
{name}
type="checkbox"
class:screenreader
{disabled}
bind:checked
on:change />
<div class="track">
<div class="thumb" />
</div>
</div>
{#if text}<span class="text" class:thin>{text}</span>{/if}
</label>
<style>
.container {
display: flex;
align-items: center;
gap: var(--spacing-s);
}
.container:disabled {
background-color: var(--grey-2);
cursor: not-allowed;
}
.toggle {
position: relative;
display: inline-block;
align-self: center;
cursor: pointer;
-webkit-user-select: none;
background: transparent;
}
.track {
width: 32px;
height: 18px;
background-color: var(--grey-4);
border-radius: 9px;
transition-delay: 0.12s;
transition-duration: 0.2s;
transition-property: background;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
position: relative;
}
.thumb {
cursor: pointer;
position: absolute;
transition-duration: 0.28s;
transition-property: all;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
top: 2px;
left: 2px;
width: 14px;
height: 14px;
background-color: white;
border-radius: var(--border-radius-xl);
}
input[type="checkbox"]:checked ~ .track .thumb {
transform: translateX(14px);
}
input[type="checkbox"]:checked ~ .track {
background-color: var(--blue);
}
input[type="checkbox"]:disabled ~ .track {
background-color: var(--grey-4);
cursor: not-allowed;
}
input[type="checkbox"]:disabled ~ .track .thumb {
background-color: var(--grey-2);
cursor: not-allowed;
}
.screenreader {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.text {
font-size: var(--font-size-s);
font-family: var(--font-sans);
cursor: pointer;
user-select: none;
color: var(--ink);
}
.text.thin {
font-size: var(--font-size-xs);
}
</style>

View File

@ -0,0 +1,21 @@
<script>
import { View } from "svench";
import Toggle from "./Toggle.svelte";
</script>
<View name="default">
<Toggle />
</View>
<View name="checked with text">
<Toggle text="Display on mobile?" />
</View>
<View name="thin">
<Toggle text="Display on mobile?" thin />
</View>
<View name="disabled">
<Toggle disabled={true} />
</View>

View File

@ -0,0 +1,14 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M14.121 10.48a1 1 0 0 0-1.414 0l-.707.706a2 2 0 1
1-2.828-2.828l5.63-5.632a6.5 6.5 0 0 1 6.377 10.568l-2.108
2.135-4.95-4.95zM3.161 4.468a6.503 6.503 0 0 1 8.009-.938L7.757 6.944a4 4 0
0 0 5.513 5.794l.144-.137 4.243 4.242-4.243 4.243a2 2 0 0 1-2.828 0L3.16
13.66a6.5 6.5 0 0 1 0-9.192z"
fill="rgba(128,129,146,1)" />
</svg>

After

Width:  |  Height:  |  Size: 493 B

View File

@ -0,0 +1,34 @@
<script context="module">
import pathsByName from "./icon-paths"
export const iconOptions = Object.keys(pathsByName)
export const directions = ["n", "ne", "e", "se", "s", "sw", "w", "nw"]
</script>
<script>
export let name = "arrow"
export let direction = "n"
$: paths = pathsByName[name] || []
$: rotation = directions.indexOf(direction) * 45
</script>
<svg
class="c"
viewBox="0 0 24 24"
fill-rule="evenodd"
clip-rule="evenodd"
style={`transform: rotate(${rotation}deg)`}>
{#each paths as path}
<path d={path} />
{/each}
</svg>
<style>
.c {
width: 1.25em;
height: 1.25em;
fill: currentColor;
overflow: visible;
margin-right: var(--spacing-xs);
}
</style>

View File

@ -0,0 +1,109 @@
<script>
import { View } from "svench";
import Icon from "./Icon.svelte";
</script>
<View name="add icon">
<Icon name="add" />
</View>
<View name="Add Row icon">
<Icon name="addrow" />
</View>
<View name="Add Column icon">
<Icon name="addcolumn" />
</View>
<View name="View icon">
<Icon name="view" />
</View>
<View name="Table icon">
<Icon name="table" />
</View>
<View name="Edit icon">
<Icon name="edit" />
</View>
<View name="Delete icon">
<Icon name="delete" />
</View>
<View name="Close icon">
<Icon name="close" />
</View>
<View name="Arrow Up icon">
<Icon name="arrowup" />
</View>
<View name="Arrow Right icon">
<Icon name="arrowright" />
</View>
<View name="Arrow Down icon">
<Icon name="arrowdown" />
</View>
<View name="Arrow Left icon">
<Icon name="arrowleft" />
</View>
<View name="Search icon">
<Icon name="search" />
</View>
<View name="Preview icon">
<Icon name="preview" />
</View>
<View name="Settings icon">
<Icon name="settings" />
</View>
<View name="Add User icon">
<Icon name="adduser" />
</View>
<View name="Plugin icon">
<Icon name="plugin" />
</View>
<View name="Help icon">
<Icon name="help" />
</View>
<View name="Sort Ascending icon">
<Icon name="sortascending" />
</View>
<View name="Sort Descending icon">
<Icon name="sortdescending" />
</View>
<View name="Calculate icon">
<Icon name="calculate" />
</View>
<View name="Filter icon">
<Icon name="filter" />
</View>
<View name="Add Fill">
<Icon name="addfill" />
</View>
<View name="Group">
<Icon name="group" />
</View>
<View name="Lightning">
<Icon name="lightning" />
</View>
<View name="Download">
<Icon name="download" />
</View>

View File

@ -0,0 +1,128 @@
export default {
arrow: [
"M0.200275 13.2782C0.200275 12.4153 0.89983 11.7157 1.76278 11.7157H23.6378C24.5007 11.7157 25.2003 12.4153 25.2003 13.2782C25.2003 14.1411 24.5007 14.8407 23.6378 14.8407H1.76278C0.89983 14.8407 0.200275 14.1411 0.200275 13.2782Z",
"M11.5954 1.23584C12.2056 0.62565 13.1949 0.62565 13.8051 1.23584L24.7426 12.1733C25.3528 12.7835 25.3528 13.7729 24.7426 14.3831L13.8051 25.3206C13.1949 25.9307 12.2056 25.9307 11.5954 25.3206C10.9852 24.7104 10.9852 23.721 11.5954 23.1108L21.4281 13.2782L11.5954 3.44555C10.9852 2.83536 10.9852 1.84604 11.5954 1.23584Z",
],
check: [
"M24.3522 3.64786C23.4883 2.78405 22.0878 2.78405 21.224 3.64786L8.64198 16.2299L3.77601 11.3639C2.9122 10.5001 1.51168 10.5001 0.647861 11.3639C-0.215954 12.2277 -0.215954 13.6283 0.647861 14.4921L7.07791 20.9221C7.94172 21.7859 9.34224 21.7859 10.2061 20.9221L24.3522 6.77601L23.6974 6.12128L24.3522 6.77601C25.216 5.9122 25.216 4.51168 24.3522 3.64786L23.6974 4.30259L24.3522 3.64786Z",
],
coffee: [
"M6.69069 1.96879C6.69069 1.36848 6.20405 0.881836 5.60374 0.881836C5.00343 0.881836 4.51678 1.36848 4.51678 1.96879V5.22966C4.51678 5.82997 5.00343 6.31662 5.60374 6.31662C6.20405 6.31662 6.69069 5.82997 6.69069 5.22966V1.96879ZM0.168955 9.57749C0.168955 8.97718 0.655602 8.49053 1.25591 8.49053H18.6472H19.7342C21.1756 8.49053 22.5579 9.06312 23.5771 10.0823C24.5964 11.1016 25.169 12.4839 25.169 13.9253C25.169 15.3667 24.5964 16.7491 23.5771 17.7683C22.5579 18.7875 21.1756 19.3601 19.7342 19.3601C19.7342 20.8015 19.1616 22.1838 18.1424 23.2031C17.1231 24.2223 15.7408 24.7949 14.2994 24.7949H5.60374C4.16234 24.7949 2.77998 24.2223 1.76077 23.2031C0.741547 22.1838 0.168955 20.8015 0.168955 19.3601V9.57749ZM19.7342 17.1862V10.6644C20.599 10.6644 21.4284 11.008 22.04 11.6195C22.6515 12.2311 22.995 13.0605 22.995 13.9253C22.995 14.7901 22.6515 15.6196 22.04 16.2311C21.4284 16.8426 20.599 17.1862 19.7342 17.1862ZM17.5603 10.6644V18.2731V19.3601C17.5603 20.2249 17.2167 21.0543 16.6052 21.6659C15.9936 22.2774 15.1642 22.621 14.2994 22.621H5.60374C4.7389 22.621 3.90949 22.2774 3.29795 21.6659C2.68642 21.0543 2.34287 20.2249 2.34287 19.3601V10.6644H17.5603ZM9.95156 0.881836C10.5519 0.881836 11.0385 1.36848 11.0385 1.96879V5.22966C11.0385 5.82997 10.5519 6.31662 9.95156 6.31662C9.35125 6.31662 8.86461 5.82997 8.86461 5.22966V1.96879C8.86461 1.36848 9.35125 0.881836 9.95156 0.881836ZM15.3863 1.96879C15.3863 1.36848 14.8997 0.881836 14.2994 0.881836C13.6991 0.881836 13.2124 1.36848 13.2124 1.96879V5.22966C13.2124 5.82997 13.6991 6.31662 14.2994 6.31662C14.8997 6.31662 15.3863 5.82997 15.3863 5.22966V1.96879Z",
],
copy: [
"M2.98325 3.58322C3.19636 3.37011 3.48539 3.25039 3.78678 3.25039H14.014C14.3154 3.25039 14.6045 3.37011 14.8176 3.58322C15.0307 3.79633 15.1504 4.08537 15.1504 4.38675V5.52312C15.1504 6.15071 15.6592 6.65948 16.2868 6.65948C16.9144 6.65948 17.4231 6.15071 17.4231 5.52312V4.38675C17.4231 3.4826 17.064 2.61549 16.4246 1.97616C15.7853 1.33683 14.9182 0.977661 14.014 0.977661H3.78678C2.88263 0.977661 2.01551 1.33683 1.37619 1.97616C0.736856 2.61549 0.377686 3.4826 0.377686 4.38675V14.614C0.377686 15.5182 0.736857 16.3853 1.37619 17.0246C2.01551 17.6639 2.88263 18.0231 3.78678 18.0231H4.92314C5.55074 18.0231 6.0595 17.5143 6.0595 16.8868C6.0595 16.2592 5.55074 15.7504 4.92314 15.7504H3.78678C3.48539 15.7504 3.19636 15.6307 2.98325 15.4176C2.77014 15.2044 2.65041 14.9154 2.65041 14.614V4.38675C2.65041 4.08537 2.77014 3.79633 2.98325 3.58322ZM10.605 12.3413C10.605 11.7137 11.1137 11.2049 11.7413 11.2049H21.9686C22.5962 11.2049 23.105 11.7137 23.105 12.3413V22.5686C23.105 23.1962 22.5962 23.7049 21.9686 23.7049H11.7413C11.1137 23.7049 10.605 23.1962 10.605 22.5686V12.3413ZM11.7413 8.93221C9.85853 8.93221 8.33223 10.4585 8.33223 12.3413V22.5686C8.33223 24.4514 9.85853 25.9777 11.7413 25.9777H21.9686C23.8514 25.9777 25.3777 24.4514 25.3777 22.5686V12.3413C25.3777 10.4585 23.8514 8.93221 21.9686 8.93221H11.7413Z",
],
downloadalt: [
"M2.21191 15.4277C2.90227 15.4277 3.46191 15.9874 3.46191 16.6777V21.6777C3.46191 22.0093 3.59361 22.3272 3.82803 22.5616C4.06245 22.796 4.38039 22.9277 4.71191 22.9277H22.2119C22.5434 22.9277 22.8614 22.796 23.0958 22.5616C23.3302 22.3272 23.4619 22.0093 23.4619 21.6777V16.6777C23.4619 15.9874 24.0216 15.4277 24.7119 15.4277C25.4023 15.4277 25.9619 15.9874 25.9619 16.6777V21.6777C25.9619 22.6723 25.5668 23.6261 24.8636 24.3294C24.1603 25.0326 23.2065 25.4277 22.2119 25.4277H4.71191C3.71735 25.4277 2.76352 25.0326 2.06026 24.3294C1.357 23.6261 0.961914 22.6723 0.961914 21.6777V16.6777C0.961914 15.9874 1.52156 15.4277 2.21191 15.4277Z",
"M6.32803 9.54385C6.81619 9.0557 7.60764 9.0557 8.0958 9.54385L13.4619 14.91L18.828 9.54385C19.3162 9.0557 20.1076 9.0557 20.5958 9.54385C21.084 10.032 21.084 10.8235 20.5958 11.3116L14.3458 17.5616C13.8576 18.0498 13.0662 18.0498 12.578 17.5616L6.32803 11.3116C5.83988 10.8235 5.83988 10.032 6.32803 9.54385Z",
"M13.4619 0.427734C14.1523 0.427734 14.7119 0.987378 14.7119 1.67773V16.6777C14.7119 17.3681 14.1523 17.9277 13.4619 17.9277C12.7716 17.9277 12.2119 17.3681 12.2119 16.6777V1.67773C12.2119 0.987378 12.7716 0.427734 13.4619 0.427734Z",
],
external: [
"M25.3673 1.28312C25.3077 1.13904 25.22 1.00384 25.1042 0.88591C25.0988 0.88042 25.0933 0.87498 25.0878 0.86959C24.8623 0.648163 24.5532 0.511597 24.2122 0.511597H24.2119H16.7122C16.0218 0.511597 15.4622 1.07124 15.4622 1.7616C15.4622 2.45195 16.0218 3.0116 16.7122 3.0116H21.1944L9.57827 14.6277C9.09012 15.1159 9.09012 15.9073 9.57827 16.3955C10.0664 16.8836 10.8579 16.8836 11.346 16.3955L22.9622 4.77936V9.2616C22.9622 9.95195 23.5218 10.5116 24.2122 10.5116C24.9025 10.5116 25.4622 9.95195 25.4622 9.2616V1.76269C25.4622 1.75848 25.4621 1.75427 25.4621 1.75006C25.4606 1.59108 25.429 1.43233 25.3673 1.28312ZM4.21216 6.7616C3.88064 6.7616 3.5627 6.89329 3.32827 7.12771C3.09385 7.36213 2.96216 7.68008 2.96216 8.0116V21.7616C2.96216 22.0931 3.09385 22.4111 3.32827 22.6455C3.5627 22.8799 3.88064 23.0116 4.21216 23.0116H17.9622C18.2937 23.0116 18.6116 22.8799 18.846 22.6455C19.0805 22.4111 19.2122 22.0931 19.2122 21.7616V14.2616C19.2122 13.5712 19.7718 13.0116 20.4622 13.0116C21.1525 13.0116 21.7122 13.5712 21.7122 14.2616V21.7616C21.7122 22.7562 21.3171 23.71 20.6138 24.4132C19.9105 25.1165 18.9567 25.5116 17.9622 25.5116H4.21216C3.2176 25.5116 2.26377 25.1165 1.56051 24.4132C0.857246 23.71 0.462158 22.7562 0.462158 21.7616V8.0116C0.462158 7.01704 0.857246 6.06321 1.56051 5.35995C2.26377 4.65668 3.2176 4.2616 4.21216 4.2616H11.7122C12.4025 4.2616 12.9622 4.82124 12.9622 5.5116C12.9622 6.20195 12.4025 6.7616 11.7122 6.7616H4.21216Z",
],
file: [
"M4.00045 1.63229C4.63978 0.99296 5.5069 0.633789 6.41104 0.633789H14.3656C14.667 0.633789 14.956 0.753513 15.1691 0.966622L23.1237 8.92117C23.3368 9.13428 23.4565 9.42332 23.4565 9.7247V22.2247C23.4565 23.1288 23.0973 23.996 22.458 24.6353C21.8187 25.2746 20.9516 25.6338 20.0474 25.6338H6.41104C5.5069 25.6338 4.63978 25.2746 4.00045 24.6353C3.36112 23.996 3.00195 23.1288 3.00195 22.2247V4.04288C3.00195 3.13873 3.36112 2.27162 4.00045 1.63229ZM6.41104 2.90652C6.10966 2.90652 5.82062 3.02624 5.60751 3.23935C5.3944 3.45246 5.27468 3.7415 5.27468 4.04288V22.2247C5.27468 22.5261 5.3944 22.8151 5.60751 23.0282C5.82062 23.2413 6.10966 23.3611 6.41104 23.3611H20.0474C20.3488 23.3611 20.6378 23.2413 20.8509 23.0282C21.064 22.8151 21.1838 22.5261 21.1838 22.2247V10.1954L13.8949 2.90652H6.41104Z",
"M14.3656 0.633789C14.9932 0.633789 15.502 1.14256 15.502 1.77015V8.58833H22.3202C22.9478 8.58833 23.4565 9.0971 23.4565 9.7247C23.4565 10.3523 22.9478 10.8611 22.3202 10.8611H14.3656C13.738 10.8611 13.2292 10.3523 13.2292 9.7247V1.77015C13.2292 1.14256 13.738 0.633789 14.3656 0.633789Z",
],
list: [
"M0 10.25C0 9.55964 0.559644 9 1.25 9H18.75C19.4404 9 20 9.55964 20 10.25C20 10.9404 19.4404 11.5 18.75 11.5H1.25C0.559644 11.5 0 10.9404 0 10.25Z",
"M0 5.25C0 4.55964 0.559644 4 1.25 4H23.75C24.4404 4 25 4.55964 25 5.25C25 5.94036 24.4404 6.5 23.75 6.5H1.25C0.559644 6.5 0 5.94036 0 5.25Z",
"M0 15.25C0 14.5596 0.559644 14 1.25 14H23.75C24.4404 14 25 14.5596 25 15.25C25 15.9404 24.4404 16.5 23.75 16.5H1.25C0.559644 16.5 0 15.9404 0 15.25Z",
"M0 20.25C0 19.5596 0.559644 19 1.25 19H18.75C19.4404 19 20 19.5596 20 20.25C20 20.9404 19.4404 21.5 18.75 21.5H1.25C0.559644 21.5 0 20.9404 0 20.25Z",
],
money: [
"M13.2917 0C13.867 0 14.3333 0.46637 14.3333 1.04167V23.9583C14.3333 24.5336 13.867 25 13.2917 25C12.7164 25 12.25 24.5336 12.25 23.9583V1.04167C12.25 0.46637 12.7164 0 13.2917 0Z",
"M7.37294 5.53956C8.25201 4.66049 9.4443 4.16663 10.6875 4.16663H18.5C19.0753 4.16663 19.5417 4.633 19.5417 5.20829C19.5417 5.78359 19.0753 6.24996 18.5 6.24996H10.6875C9.99683 6.24996 9.33445 6.52433 8.84608 7.0127C8.3577 7.50108 8.08333 8.16346 8.08333 8.85413C8.08333 9.54479 8.3577 10.2072 8.84608 10.6955C9.33445 11.1839 9.99683 11.4583 10.6875 11.4583H15.8958C17.139 11.4583 18.3313 11.9522 19.2104 12.8312C20.0895 13.7103 20.5833 14.9026 20.5833 16.1458C20.5833 17.389 20.0895 18.5813 19.2104 19.4604C18.3313 20.3394 17.139 20.8333 15.8958 20.8333H7.04167C6.46637 20.8333 6 20.3669 6 19.7916C6 19.2163 6.46637 18.75 7.04167 18.75H15.8958C16.5865 18.75 17.2489 18.4756 17.7373 17.9872C18.2256 17.4988 18.5 16.8365 18.5 16.1458C18.5 15.4551 18.2256 14.7927 17.7373 14.3044C17.2489 13.816 16.5865 13.5416 15.8958 13.5416H10.6875C9.4443 13.5416 8.25201 13.0478 7.37294 12.1687C6.49386 11.2896 6 10.0973 6 8.85413C6 7.61092 6.49386 6.41864 7.37294 5.53956Z",
],
paperclip: [
"M17.5359 2.82806C16.6555 2.82806 15.8112 3.17779 15.1886 3.80031L5.02747 13.9615C3.99 14.999 3.40716 16.4061 3.40716 17.8733C3.40716 19.3405 3.99 20.7476 5.02747 21.785C6.06493 22.8225 7.47204 23.4053 8.93924 23.4053C10.4064 23.4053 11.8135 22.8225 12.851 21.785L23.0122 11.6239C23.444 11.1921 24.1441 11.1921 24.5759 11.6239C25.0076 12.0556 25.0076 12.7557 24.5759 13.1875L14.4147 23.3487C12.9625 24.8009 10.9929 25.6167 8.93924 25.6167C6.88555 25.6167 4.91598 24.8009 3.4638 23.3487C2.01162 21.8965 1.1958 19.9269 1.1958 17.8733C1.1958 15.8196 2.01162 13.85 3.4638 12.3978L13.625 2.23665C14.6622 1.19941 16.069 0.616699 17.5359 0.616699C19.0028 0.616699 20.4095 1.19941 21.4468 2.23665C22.484 3.27388 23.0667 4.68068 23.0667 6.14755C23.0667 7.61442 22.484 9.02121 21.4468 10.0584L11.2745 20.2196C10.6523 20.8419 9.80824 21.1915 8.92818 21.1915C8.04812 21.1915 7.20411 20.8419 6.58181 20.2196C5.95952 19.5973 5.60992 18.7533 5.60992 17.8733C5.60992 16.9932 5.95952 16.1492 6.58181 15.5269L15.9695 6.15029C16.4015 5.71875 17.1016 5.71916 17.5331 6.15121C17.9647 6.58326 17.9643 7.28333 17.5322 7.71487L8.14548 17.0906C7.93818 17.2981 7.82127 17.5799 7.82127 17.8733C7.82127 18.1668 7.93789 18.4484 8.14548 18.656C8.35306 18.8636 8.63461 18.9802 8.92818 18.9802C9.22175 18.9802 9.50329 18.8636 9.71088 18.656L19.8831 8.49479C20.5054 7.8723 20.8554 7.02773 20.8554 6.14755C20.8554 5.26716 20.5056 4.42284 19.8831 3.80031C19.2606 3.17779 18.4163 2.82806 17.5359 2.82806Z",
],
person: [
"M3.04927 16.6449C4.23321 15.4462 5.83898 14.7727 7.51333 14.7727H17.6143C19.2887 14.7727 20.8945 15.4462 22.0784 16.6449C23.2623 17.8436 23.9275 19.4695 23.9275 21.1648V23.7216C23.9275 24.4276 23.3622 25 22.6648 25C21.9675 25 21.4022 24.4276 21.4022 23.7216V21.1648C21.4022 20.1476 21.0031 19.1721 20.2928 18.4528C19.5824 17.7336 18.6189 17.3295 17.6143 17.3295H7.51333C6.50872 17.3295 5.54526 17.7336 4.83489 18.4528C4.12453 19.1721 3.72545 20.1476 3.72545 21.1648V23.7216C3.72545 24.4276 3.16015 25 2.46282 25C1.76549 25 1.2002 24.4276 1.2002 23.7216V21.1648C1.2002 19.4695 1.86533 17.8436 3.04927 16.6449Z",
"M11.9956 2.5C9.92454 2.5 8.24561 4.17893 8.24561 6.25C8.24561 8.32107 9.92454 10 11.9956 10C14.0667 10 15.7456 8.32107 15.7456 6.25C15.7456 4.17893 14.0667 2.5 11.9956 2.5ZM5.74561 6.25C5.74561 2.79822 8.54383 0 11.9956 0C15.4474 0 18.2456 2.79822 18.2456 6.25C18.2456 9.70178 15.4474 12.5 11.9956 12.5C8.54383 12.5 5.74561 9.70178 5.74561 6.25Z",
],
refresh: [
"M6.97937 3.40529C8.70577 2.45337 10.6948 2.08834 12.6467 2.36521C14.5986 2.64207 16.4076 3.54582 17.8012 4.94028C17.8093 4.94841 17.8176 4.95642 17.8259 4.9643L21.0026 7.95574H17.048C16.4203 7.95574 15.9115 8.4646 15.9115 9.09231C15.9115 9.72002 16.4203 10.2289 17.048 10.2289H23.8643H23.8675C24.0269 10.2289 24.1787 10.196 24.3165 10.1367C24.4544 10.0775 24.5828 9.98985 24.6925 9.874C24.7014 9.86462 24.7102 9.85508 24.7187 9.8454C24.9095 9.62998 25.0041 9.36088 25.004 9.09232C25.004 9.09121 25.004 9.09009 25.004 9.08898V2.27288C25.004 1.64517 24.4952 1.13631 23.8675 1.13631C23.2397 1.13631 22.7309 1.64517 22.7309 2.27288V6.46082L19.3966 3.32095C17.6563 1.58497 15.4 0.45984 12.9659 0.11459C10.526 -0.231487 8.03977 0.224801 5.88178 1.4147C3.72379 2.60459 2.01099 4.46363 1.00148 6.71166C-0.00803095 8.9597 -0.259554 11.4749 0.284811 13.8784C0.829176 16.2818 2.13994 18.4432 4.01957 20.0368C5.89921 21.6305 8.24589 22.5701 10.706 22.714C13.1661 22.8579 15.6063 22.1984 17.6589 20.8347C19.7116 19.4711 21.2654 17.4773 22.0863 15.1538C22.2954 14.5619 21.9851 13.9126 21.3933 13.7035C20.8014 13.4944 20.1521 13.8047 19.943 14.3966C19.2863 16.2554 18.0432 17.8504 16.4011 18.9413C14.759 20.0322 12.8068 20.5599 10.8387 20.4447C8.87066 20.3296 6.99332 19.5779 5.48961 18.303C3.9859 17.0281 2.93729 15.299 2.5018 13.3762C2.06631 11.4535 2.26753 9.44129 3.07513 7.64286C3.88274 5.84443 5.25298 4.35721 6.97937 3.40529Z",
],
swoop: [
"M17.6488 0.406796C17.1064 -0.135599 16.227 -0.135599 15.6846 0.406796C15.1422 0.949191 15.1422 1.82859 15.6846 2.37098L20.258 6.94444H6.94444C5.10266 6.94444 3.33632 7.67609 2.03398 8.97843C0.731644 10.2808 0 12.0471 0 13.8889V23.6111C0 24.3782 0.621827 25 1.38889 25C2.15595 25 2.77778 24.3782 2.77778 23.6111V13.8889C2.77778 12.7838 3.21676 11.724 3.99817 10.9426C4.77957 10.1612 5.83938 9.72222 6.94444 9.72222H20.258L15.6846 14.2957C15.1422 14.8381 15.1422 15.7175 15.6846 16.2599C16.227 16.8023 17.1064 16.8023 17.6488 16.2599L24.5932 9.31543C24.8611 9.04749 24.9967 8.69732 24.9999 8.34616C25 8.34189 25 8.33761 25 8.33333C25 8.32906 25 8.32478 24.9999 8.32051C24.9983 8.13686 24.961 7.96173 24.8946 7.80169C24.8268 7.63788 24.7264 7.4844 24.5932 7.35124L17.6488 0.406796Z",
],
twitter: [
"M21.351 2.5026C20.4177 1.5026 19.0844 0.835938 17.6177 0.835938C14.8177 0.835938 12.4844 3.16927 12.4844 6.1026C12.4844 6.5026 12.551 6.9026 12.6177 7.3026C8.35104 7.1026 4.61771 4.96927 2.08438 1.83594C1.61771 2.63594 1.41771 3.5026 1.41771 4.5026C1.41771 6.3026 2.35104 7.9026 3.68437 8.9026C2.81771 8.9026 2.08437 8.63594 1.35104 8.23594V8.3026C1.35104 10.8359 3.08438 12.9693 5.48438 13.4359C5.08438 13.5693 4.61771 13.6359 4.15104 13.6359C3.81771 13.6359 3.48438 13.6359 3.21771 13.5693C3.88438 15.6359 5.75104 17.1693 8.01771 17.2359C6.28438 18.6359 4.08438 19.5026 1.68438 19.5026C1.28438 19.5026 0.884375 19.5026 0.484375 19.4359C2.75104 20.9026 5.41771 21.7693 8.35104 21.7693C17.751 21.7693 22.951 13.7693 22.951 6.83594V6.16927C23.951 5.43594 24.8177 4.5026 25.4844 3.43594C24.551 3.83594 23.551 4.16927 22.551 4.23594C23.4177 3.5026 24.2177 2.5026 24.6177 1.23594C23.6177 1.83594 22.551 2.3026 21.351 2.5026Z",
],
add: [
"M12.5 2.27273C6.85163 2.27273 2.27273 6.85163 2.27273 12.5C2.27273 18.1484 6.85163 22.7273 12.5 22.7273C18.1484 22.7273 22.7273 18.1484 22.7273 12.5C22.7273 6.85163 18.1484 2.27273 12.5 2.27273ZM0 12.5C0 5.59644 5.59644 0 12.5 0C19.4036 0 25 5.59644 25 12.5C25 19.4036 19.4036 25 12.5 25C5.59644 25 0 19.4036 0 12.5Z",
"M12.5 7.29167C13.0753 7.29167 13.5417 7.75804 13.5417 8.33333V16.6667C13.5417 17.242 13.0753 17.7083 12.5 17.7083C11.9247 17.7083 11.4583 17.242 11.4583 16.6667V8.33333C11.4583 7.75804 11.9247 7.29167 12.5 7.29167Z",
"M7.29167 12.5C7.29167 11.9247 7.75804 11.4583 8.33333 11.4583H16.6667C17.242 11.4583 17.7083 11.9247 17.7083 12.5C17.7083 13.0753 17.242 13.5417 16.6667 13.5417H8.33333C7.75804 13.5417 7.29167 13.0753 7.29167 12.5Z",
],
addcolumn: [
"M10 3c.552 0 1 .448 1 1v16c0 .552-.448 1-1 1H4c-.552 0-1-.448-1-1V4c0-.552.448-1 1-1h6zM9 5H5v14h4V5zm9 2c2.761 0 5 2.239 5 5s-2.239 5-5 5-5-2.239-5-5 2.239-5 5-5zm1 2h-2v1.999L15 11v2l2-.001V15h2v-2.001L21 13v-2l-2-.001V9z",
],
addrow: [
"M12 13c2.761 0 5 2.239 5 5s-2.239 5-5 5-5-2.239-5-5 2.239-5 5-5zm1 2h-2v1.999L9 17v2l2-.001V21h2v-2.001L15 19v-2l-2-.001V15zm7-12c.552 0 1 .448 1 1v6c0 .552-.448 1-1 1H4c-.552 0-1-.448-1-1V4c0-.552.448-1 1-1h16zM5 5v4h14V5H5z",
],
view: [
"M12 3c5.392 0 9.878 3.88 10.819 9-.94 5.12-5.427 9-10.819 9-5.392 0-9.878-3.88-10.819-9C2.121 6.88 6.608 3 12 3zm0 16a9.005 9.005 0 0 0 8.777-7 9.005 9.005 0 0 0-17.554 0A9.005 9.005 0 0 0 12 19zm0-2.5a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9zm0-2a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z",
],
table: [
"M4 8h16V5H4v3zm10 11v-9h-4v9h4zm2 0h4v-9h-4v9zm-8 0v-9H4v9h4zM3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z",
],
edit: [
"M15.728 9.686l-1.414-1.414L5 17.586V19h1.414l9.314-9.314zm1.414-1.414l1.414-1.414-1.414-1.414-1.414 1.414 1.414 1.414zM7.242 21H3v-4.243L16.435 3.322a1 1 0 0 1 1.414 0l2.829 2.829a1 1 0 0 1 0 1.414L7.243 21z",
],
delete: [
"M17 6h5v2h-2v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V8H2V6h5V3a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v3zm1 2H6v12h12V8zM9 4v2h6V4H9z",
],
close: [
"M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-11.414L9.172 7.757 7.757 9.172 10.586 12l-2.829 2.828 1.415 1.415L12 13.414l2.828 2.829 1.415-1.415L13.414 12l2.829-2.828-1.415-1.415L12 10.586z",
],
arrowup: ["M12 8l6 6H6z"],
arrowdown: ["M12 16l-6-6h12z"],
arrowleft: ["M8 12l6-6v12z"],
arrowright: ["M16 12l-6 6V6z"],
search: [
"M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15z",
],
settings: [
"M2.132 13.63a9.942 9.942 0 0 1 0-3.26c1.102.026 2.092-.502 2.477-1.431.385-.93.058-2.004-.74-2.763a9.942 9.942 0 0 1 2.306-2.307c.76.798 1.834 1.125 2.764.74.93-.385 1.457-1.376 1.43-2.477a9.942 9.942 0 0 1 3.262 0c-.027 1.102.501 2.092 1.43 2.477.93.385 2.004.058 2.763-.74a9.942 9.942 0 0 1 2.307 2.306c-.798.76-1.125 1.834-.74 2.764.385.93 1.376 1.457 2.477 1.43a9.942 9.942 0 0 1 0 3.262c-1.102-.027-2.092.501-2.477 1.43-.385.93-.058 2.004.74 2.763a9.942 9.942 0 0 1-2.306 2.307c-.76-.798-1.834-1.125-2.764-.74-.93.385-1.457 1.376-1.43 2.477a9.942 9.942 0 0 1-3.262 0c.027-1.102-.501-2.092-1.43-2.477-.93-.385-2.004-.058-2.763.74a9.942 9.942 0 0 1-2.307-2.306c.798-.76 1.125-1.834.74-2.764-.385-.93-1.376-1.457-2.477-1.43zM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z",
],
preview: [
"M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM10.622 8.415a.4.4 0 0 0-.622.332v6.506a.4.4 0 0 0 .622.332l4.879-3.252a.4.4 0 0 0 0-.666l-4.88-3.252z",
],
adduser: [
"M14 14.252V22H4a8 8 0 0 1 10-7.748zM12 13c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm6 4v-3h2v3h3v2h-3v3h-2v-3h-3v-2h3z",
],
plugin: [
"M13 18v2h6v2h-6a2 2 0 0 1-2-2v-2H8a4 4 0 0 1-4-4v-4h16v4a4 4 0 0 1-4 4h-3zm3-12h3a1 1 0 0 1 1 1v2H4V7a1 1 0 0 1 1-1h3V2h2v4h4V2h2v4zm-4 8.5a1 1 0 1 0 0-2 1 1 0 0 0 0 2z",
],
help: [
"M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm2-1.645A3.502 3.502 0 0 0 12 6.5a3.501 3.501 0 0 0-3.433 2.813l1.962.393A1.5 1.5 0 1 1 12 11.5a1 1 0 0 0-1 1V14h2v-.645z",
],
sortdescending: [
"M19 3l4 5h-3v12h-2V8h-3l4-5zm-5 15v2H3v-2h11zm0-7v2H3v-2h11zm-2-7v2H3V4h9z",
],
sortascending: [
"M20 4v12h3l-4 5-4-5h3V4h2zm-8 14v2H3v-2h9zm2-7v2H3v-2h11zm0-7v2H3V4h11z",
],
calculate: [
"M4 2h16a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1zm1 2v16h14V4H5zm2 2h10v4H7V6zm0 6h2v2H7v-2zm0 4h2v2H7v-2zm4-4h2v2h-2v-2zm0 4h2v2h-2v-2zm4-4h2v6h-2v-6z",
],
filter: [
"M21 4v2h-1l-5 7.5V22H9v-8.5L4 6H3V4h18zM6.404 6L11 12.894V20h2v-7.106L17.596 6H6.404z",
],
addfill: [
"M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11H7v2h4v4h2v-4h4v-2h-4V7h-2v4z",
],
group: [
"M11 4h10v2H11V4zm0 4h6v2h-6V8zm0 6h10v2H11v-2zm0 4h6v2h-6v-2zM3 4h6v6H3V4zm2 2v2h2V6H5zm-2 8h6v6H3v-6zm2 2v2h2v-2H5z",
],
download: [
"M13 10h5l-6 6-6-6h5V3h2v7zm-9 9h16v-7h2v8a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-8h2v7z",
],
lightning: [
"M13 9h8L11 24v-9H4l9-15v9zm-2 2V7.22L7.532 13H13v4.394L17.263 11H11z",
],
closeline: [
"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z",
],
}

View File

@ -0,0 +1,50 @@
<script>
export let href,
icon,
title,
active = false
</script>
<a {href} target="_blank" class="nav-item" class:active>
{#if icon}
<span class="nav-item-icon">
<svelte:component this={icon} />
</span>
{/if}
{#if title}
<div class="nav-item-title">{title}</div>
{/if}
</a>
<style>
.nav-item {
cursor: pointer;
padding: 12px;
height: 2rem;
display: flex;
flex-direction: row;
align-items: center;
box-sizing: border-box;
border-radius: var(--border-radius-s);
}
.nav-item:hover {
background-color: var(--grey-1);
}
.nav-item-title {
font-family: var(--font-sans);
font-size: var(--font-size-xs);
color: var(--ink);
font-weight: 400;
margin-left: 12px;
}
.nav-item-icon {
color: var(--grey-7);
}
.active {
background-color: var(--grey-2);
}
</style>

View File

@ -0,0 +1,19 @@
<script>
import { View } from "svench";
import Home from "./Home.svelte";
import Contribution from "../Icons/Contribution.svelte";
</script>
<View name="default">
<Home
href="https://github.com/Budibase/budibase/blob/master/CONTRIBUTING.md"
title="Contribute"
icon={Contribution} />
</View>
<View name="active">
<Home
active
href="https://github.com/Budibase/budibase/blob/master/CONTRIBUTING.md"
title="Contribute"
icon={Contribution} />
</View>

View File

@ -0,0 +1,85 @@
<script>
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
export let thin = false
export let name,
show = false
const capitalize = name => name[0].toUpperCase() + name.slice(1)
const onHeaderClick = () => {
show = !show
if (show) {
dispatch("open")
}
}
</script>
<div class="property-group-container" class:thin>
<div class="property-group-name" on:click={onHeaderClick}>
<div class:thin class="name">{capitalize(name)}</div>
<div class="icon">
<i class={show ? 'ri-arrow-down-s-fill' : 'ri-arrow-left-s-fill'} />
</div>
</div>
<div class="property-panel" class:show>
<slot />
</div>
</div>
<style>
.property-group-container {
display: flex;
flex-direction: column;
height: auto;
justify-content: center;
border-radius: var(--border-radius-m);
font-family: var(--font-sans);
}
.property-group-name {
cursor: pointer;
display: flex;
flex-flow: row nowrap;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.name {
text-align: left;
font-size: 14px;
font-weight: 500;
letter-spacing: 0.14px;
color: var(--ink);
flex: 1 1 auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.name.thin {
font-size: var(--font-size-xs);
}
.icon {
flex: 0 0 20px;
text-align: center;
}
.property-panel {
/* height: 0px;
overflow: hidden; */
display: none;
}
.show {
/* overflow: auto;
height: auto; */
display: flex;
flex-direction: column;
flex: 1;
margin-top: var(--spacing-m);
}
</style>

View File

@ -0,0 +1,53 @@
<script>
import { View } from "svench";
import DetailSummary from "./DetailSummary.svelte";
</script>
<svelte:head>
<link href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css" rel="stylesheet">
</svelte:head>
<style>
div {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-m);
width: 120px;
}
</style>
<View name="default">
<div>
<DetailSummary name="Category 1">
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
</DetailSummary>
<DetailSummary name="Category 2">
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
</DetailSummary>
</div>
</View>
<View name="thin">
<div>
<DetailSummary thin name="Category 1">
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
</DetailSummary>
<DetailSummary thin name="Category 2">
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
</DetailSummary>
</div>
</View>

View File

@ -0,0 +1,33 @@
<script>
import Input from "../Form/Input.svelte"
export let categories = [
{
name: "Customers List - Data Row",
items: [
{ name: "Name", id: "chjaHICHc82h2" },
{ name: "Created", id: "chjaHICgr56Hc82h2" },
{ name: "Status", id: "chjaHICHc8646462h2" },
],
},
{
name: "Home Screen Components",
items: [{ name: "Title", id: "chjaHICHc82h2" }],
},
]
</script>
<div class="container">
<Input thin placeholder="Search" />
{#each categories as { name, items }}
<div class="title">{name}</div>
<ul>
{#each items as { name, id }}
<li>{name}</li>
{/each}
</ul>
{/each}
</div>
<style>
</style>

View File

@ -0,0 +1,8 @@
<script>
import Search from "./Search.svelte";
import { View } from "svench";
</script>
<View name="Name">
<Search />
</View>

View File

@ -0,0 +1,17 @@
<script>
import Input from "../Form/Input.svelte"
let value = ""
</script>
<Input label="Your Name" bind:value />
<style>
p {
font-size: var(--font-size-m);
margin: 0;
font-family: var(--font-sans);
font-weight: 400;
line-height: 1.5;
color: var(--grey-7);
}
</style>

View File

@ -0,0 +1,38 @@
<script>
import { getContext } from "svelte"
import Context from "../context"
const { hide } = getContext(Context.Modal)
let count = 0
const clicks = 5
$: if (count === clicks) hide()
$: remaining = clicks - count
function increment() {
count++
}
</script>
<div on:click={increment}>
Click me
{remaining}
more time{remaining === 1 ? '' : 's'}
to close this modal!
</div>
<style>
div {
padding: 40px;
background-color: var(--purple);
color: white;
font-family: var(--font-sans);
font-weight: bold;
text-align: center;
user-select: none;
font-size: 20px;
}
div:hover {
cursor: pointer;
}
</style>

View File

@ -0,0 +1,116 @@
<script>
import { createEventDispatcher, setContext } from "svelte"
import { fade, fly } from "svelte/transition"
import Portal from "svelte-portal"
import Context from "../context"
const dispatch = createEventDispatcher()
export let padding = true
export let width = undefined
export let border = true
let visible = false
$: dispatch(visible ? "show" : "hide")
export function show() {
if (visible) {
return
}
visible = true
}
export function hide() {
if (!visible) {
return
}
visible = false
}
function handleKey(e) {
if (visible && e.key === "Escape") {
hide()
}
}
setContext(Context.Modal, { show, hide })
</script>
<svelte:window on:keydown={handleKey} />
{#if visible}
<Portal target="body">
<div
class="overlay"
on:click|self={hide}
transition:fade={{ duration: 200 }}>
<div
class="scroll-wrapper"
on:click|self={hide}
transition:fly={{ y: 30, duration: 200 }}>
<div class="content-wrapper" on:click|self={hide}>
<div
class="modal"
class:padding
class:border
style={width ? `flex: 0 0 ${width}` : ''}>
<slot />
</div>
</div>
</div>
</div>
</Portal>
{/if}
<style>
.overlay {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow-x: hidden;
overflow-y: auto;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.25);
z-index: 999;
}
.scroll-wrapper {
flex: 1 1 auto;
display: flex;
flex-direction: row;
justify-content: center;
align-items: flex-start;
max-height: 100%;
}
.content-wrapper {
flex: 1 1 auto;
display: flex;
flex-direction: row;
justify-content: center;
align-items: flex-start;
width: 0;
}
.modal {
background-color: var(--background);
display: grid;
align-items: stretch;
box-shadow: 0 0 4rem 1.5rem rgba(0, 0, 0, 0.15);
position: relative;
flex: 0 0 400px;
margin: 2rem 0;
border-radius: var(--border-radius-m);
gap: var(--spacing-xl);
}
.modal.padding {
padding: var(--spacing-xl);
}
.modal.border {
border: var(--border-dark);
}
</style>

View File

@ -0,0 +1,117 @@
<script>
import { View } from "svench";
import Modal from "./Modal.svelte";
import ModalContent from "./ModalContent.svelte";
import Button from "../Button/Button.svelte";
import Content from "./Content.svelte";
import QuizModal from "./QuizModal.svelte";
import CustomContent from "./CustomContent.svelte";
let modal1
let modal2
let modal3
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
async function longTask() {
await sleep(3000)
}
</script>
<style>
p, span {
font-family: var(--font-sans);
font-size: var(--font-size-s);
}
code {
display: inline-block;
box-sizing: border-box;
padding: var(--spacing-xs) var(--spacing-s);
background-color: var(--grey-2);
color: var(--red-dark);
border-radius: var(--spacing-xs);
}
</style>
<h3>Modals</h3>
<p>
Modals provide a means to render content in front of everything else on a page.
</p>
<p>
The modal module in BBUI exposes two
separate components to provide this functionality; a <code>Modal</code> component to control visibility of content,
and a <code>ModalContent</code> component to quickly construct the typical content - although this is optional.
</p>
<p>
One of the common problems with modals and popups is stale state reappearing after hiding and showing the content
again, since the state hasn't been garbage collected if a component controls its own visibility. This is handled for
you when using the <code>Modal</code> component as it will fully unmount child components, properly resetting state
every time it appears.
</p>
<br/>
<p>Use ModalContent to render typical modal content.</p>
<View name="Simple Confirmation Modal">
<Button primary on:click={modal1.show}>Delete Record</Button>
<Modal bind:this={modal1}>
<ModalContent title="Confirm Deletion" confirmText="Delete">
<span>Are you sure you want to delete this record?</span>
</ModalContent>
</Modal>
</View>
<br/>
<p>
Width can be specified as a prop to a <code>Modal</code>. Any additional <code>ModalContent</code> props provided
will be passed to the confirmation button.
</p>
<View name="Different Buttons and Width">
<Button primary on:click={modal3.show}>Open Modal</Button>
<Modal bind:this={modal3} width="250px">
<ModalContent
title="Confirmation Required"
showCancelButton={false}
showCloseIcon={false}
confirmText="I'm sure!"
green
large
wide
>
<span>Are you sure you want to do that?</span>
</ModalContent>
</Modal>
</View>
<br/>
<p>Any content can be rendered inside a <code>Modal</code>. Use context to close the modal from your own components.</p>
<View name="Custom Content">
<Button primary on:click={modal1.show}>Open Modal</Button>
<Modal bind:this={modal1} padding={false} border={false}>
<CustomContent/>
</Modal>
</View>
<br/>
<p>Async functions passed in as the onConfirm prop will make the modal wait until the callback is completed.</p>
<View name="Async Callbacks">
<Button primary on:click={modal2.show}>Long Task</Button>
<Modal bind:this={modal2}>
<ModalContent
title="Perform Long Task"
confirmText="Submit"
onConfirm={longTask}
>
<span>Pressing submit will wait 3 seconds before finishing and disable the confirm button until it's done.</span>
</ModalContent>
</Modal>
</View>
<br/>
<p>Returning false from a onConfirm callback will prevent the modal being closed.</p>
<View name="Callback Failure Handling">
<Button primary on:click={modal3.show}>Open Quiz</Button>
<Modal bind:this={modal3}>
<QuizModal />
</Modal>
</View>

View File

@ -0,0 +1,132 @@
<script>
import { createEventDispatcher, getContext } from "svelte"
import Button from "../Button/Button.svelte"
import Icon from "../Icons/Icon.svelte"
import Context from "../context"
const dispatch = createEventDispatcher()
export let title = undefined
export let cancelText = "Cancel"
export let confirmText = "Confirm"
export let showCancelButton = true
export let showConfirmButton = true
export let showCloseIcon = true
export let onConfirm = undefined
export let disabled = false
const { hide } = getContext(Context.Modal)
let loading = false
$: confirmDisabled = disabled || loading
async function confirm() {
loading = true
if (!onConfirm || (await onConfirm()) !== false) {
hide()
}
loading = false
}
</script>
<div class="modal-content">
{#if title}
<header>
<h5>{title}</h5>
<div class="header-content">
<slot name="header" />
</div>
</header>
{/if}
<slot />
{#if showCancelButton || showConfirmButton}
<footer>
<div class="footer-content">
<slot name="footer" />
</div>
<div class="buttons">
{#if showCancelButton}
<Button secondary on:click={hide}>{cancelText}</Button>
{/if}
{#if showConfirmButton}
<Button
primary
{...$$restProps}
disabled={confirmDisabled}
on:click={confirm}>
{confirmText}
</Button>
{/if}
</div>
</footer>
{/if}
{#if showCloseIcon}
<div class="close-icon" on:click={hide}>
<Icon name="closeline" />
</div>
{/if}
</div>
<style>
.modal-content {
display: grid;
position: relative;
gap: var(--spacing-xl);
color: var(--ink);
}
header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-right: 40px;
}
header h5 {
margin: 0;
font-weight: 500;
}
.header-content {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
.close-icon {
position: absolute;
top: 0;
right: 0;
color: var(--ink);
font-size: var(--font-size-m);
}
.close-icon:hover {
color: var(--grey-6);
cursor: pointer;
}
.close-icon :global(svg) {
margin-right: 0;
}
footer {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: var(--spacing-m);
}
.footer-content {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.buttons {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
gap: var(--spacing-m);
}
</style>

View File

@ -0,0 +1,54 @@
<script>
import { onMount } from "svelte"
import ModalContent from "./ModalContent.svelte"
import Input from "../Form/Input.svelte"
let modal
let answer
let error
export function show() {
modal.show()
}
export function hide() {
modal.hide
}
function resetState() {
answer = undefined
error = undefined
}
async function answerQuiz() {
const correct = answer === "8"
error = !correct
return correct
}
</script>
<ModalContent
title="Quick Maths"
bind:this={modal}
confirmText="Submit"
onConfirm={answerQuiz}
on:show={resetState}>
{#if error}
<p class="error">Wrong answer! Try again.</p>
{/if}
<p>What is 4 + 4?</p>
<Input label="Answer" bind:value={answer} />
</ModalContent>
<style>
p {
margin: 0;
font-family: var(--font-sans);
font-size: var(--font-size-s);
}
p.error {
color: #e26d69;
background-color: #ffe6e6;
padding: 8px;
border-radius: 4px;
}
</style>

View File

@ -0,0 +1,68 @@
<script>
import Portal from "svelte-portal"
import { createEventDispatcher } from "svelte"
import positionDropdown from "../Actions/position_dropdown"
import clickOutside from "../Actions/click_outside"
const dispatch = createEventDispatcher()
export let anchor
export let align = "right"
let open = null
export const show = () => {
open = true
dispatch("show")
}
export const hide = () => {
open = false
dispatch("hide")
}
function handleEscape(e) {
if (open && e.key === "Escape") {
hide()
}
}
</script>
{#if open}
<Portal>
<div
tabindex="0"
class:open
use:positionDropdown={{ anchor, align }}
use:clickOutside={hide}
on:keydown={handleEscape}
class="menu-container">
<slot />
</div>
</Portal>
{/if}
<style>
.menu-container {
position: fixed;
margin-top: var(--spacing-xs);
padding: var(--spacing-xl);
outline: none;
box-sizing: border-box;
opacity: 0;
min-width: 400px;
z-index: 2;
color: var(--ink);
height: fit-content !important;
border: var(--border-dark);
border-radius: var(--border-radius-m);
transform: scale(0);
transition: opacity 0.13s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1);
overflow-y: auto;
background-color: var(--background);
box-shadow: 0 5px 12px rgba(0, 0, 0, 0.15);
}
.open {
transform: scale(1);
opacity: 1;
}
</style>

View File

@ -0,0 +1,121 @@
<script>
import { View } from "svench";
import Popover from "./Popover.svelte";
import Button from "../Button/Button.svelte";
import TextButton from "../Button/TextButton.svelte";
import Icon from "../Icons/Icon.svelte";
import Input from "../Form/Input.svelte";
import Select from "../Form/Select.svelte";
let anchorRight;
let anchorLeft;
let dropdownRight;
let dropdownLeft;
const options = ["Column 1", "Column 2", "Super cool column"];
const option1s = ["Is", "Is not", "Contains" , "Does not contain"];
</script>
<style>
.button-group {
margin-top: var(--spacing-l);
display: flex;
justify-content: flex-end;
gap: var(--spacing-s);
}
h6 {
font-size: var(--font-size-m);
margin: 0 0 var(--spacing-l) 0;
font-weight: 600;
font-family: var(--font-sans);
}
.input-group-column {
display: flex;
flex-direction: column;
gap: var(--spacing-s);
}
.input-group-row {
display: grid;
grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];
gap: var(--spacing-s);
margin-bottom: var(--spacing-l);
align-items: center;
}
p {
margin:0;
font-size: var(--font-size-xs);
}
</style>
<View name="Simple popover">
<div bind:this={anchorLeft}>
<Button text on:click={dropdownLeft.show}>
<Icon name="view" />
Add View
</Button>
</div>
<Popover bind:this={dropdownLeft} anchor={anchorLeft} align="left">
<h6>Add New View</h6>
<Input thin placeholder="Enter your name" />
<div class="button-group">
<Button secondary on:click={() => alert('Clicked!')}>Cancel</Button>
<Button primary on:click={() => alert('Clicked!')}>Add New View</Button>
</div>
</Popover>
</View>
<View name="Stacked columns">
<div bind:this={anchorRight}>
<Button text on:click={dropdownRight.show}>
<Icon name="addrow" />
Add Row
</Button>
</div>
<Popover bind:this={dropdownRight} anchor={anchorRight}>
<h6>Add New Row</h6>
<div class="input-group-column">
<Input thin placeholder="Enter your string" />
<Input thin placeholder="Enter your string" />
<Input thin placeholder="Enter your string" />
</div>
<div class="button-group">
<Button secondary on:click={() => alert('Clicked!')}>Cancel</Button>
<Button primary on:click={() => alert('Clicked!')}>Add New Row</Button>
</div>
</Popover>
</View>
<View name="Multiple inputs in a row">
<div bind:this={anchorLeft}>
<Button text on:click={dropdownLeft.show}>
<Icon name="filter" />
Add Filter
</Button>
</div>
<Popover bind:this={dropdownLeft} anchor={anchorLeft} align="left">
<h6>Add New Filter</h6>
<div class="input-group-row">
<p>Where</p>
<Select secondary thin name="Test">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
<Select secondary thin name="Test">
{#each option1s as option1}
<option value={option1}>{option1}</option>
{/each}
</Select>
<Input thin placeholder="Enter your name" />
<Button text on:click={() => alert('Clicked!')}>
<Icon name="close" />
</Button>
</div>
<Button text on:click={() => alert('Clicked!')}>Add Filter</Button>
</Popover>
</View>

View File

@ -0,0 +1,49 @@
<script>
export let extraSmall = false
export let small = false
export let medium = false
export let large = false
export let extraLarge = false
</script>
<spacer
class:extraSmall
class:small
class:medium
class:large
class:extraLarge />
<style>
spacer {
display: block;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.extraSmall {
height: var(--spacing-xs);
width: var(--spacing-xs);
}
.small {
height: var(--spacing-s);
width: var(--spacing-s);
}
.medium {
height: var(--spacing-m);
width: var(--spacing-m);
}
.large {
height: var(--spacing-l);
width: var(--spacing-l);
}
.extraLarge {
height: var(--spacing-xl);
width: var(--spacing-xl);
}
</style>

View File

@ -0,0 +1,51 @@
<script>
import { View } from "svench";
import Spacer from "./Spacer.svelte";
</script>
<style>
.block {
background: black;
height: 100px;
width: 100px;
border-radius: var(--border-radius-s);
}
.horizontal {
display: flex;
}
</style>
<View name="extraSmall">
<div class="block"></div>
<Spacer extraSmall />
<div class="block"></div>
</View>
<View name="Small">
<div class="block"></div>
<Spacer small />
<div class="block"></div>
</View>
<View name="Medium">
<div class="block"></div>
<Spacer medium />
<div class="block"></div>
</View>
<View name="Large">
<div class="horizontal">
<div class="block"></div>
<Spacer large />
<div class="block"></div>
</div>
</View>
<View name="extraLarge">
<div class="horizontal">
<div class="block"></div>
<Spacer extraLarge />
<div class="block"></div>
</div>
</View>

View File

@ -0,0 +1,78 @@
<script>
import { flip } from 'svelte/animate';
import { fly } from "svelte/transition"
import { View } from "svench";
import { notifications } from "./notifications";
export let themes = {
danger: "#E26D69",
success: "#84C991",
warning: "#f0ad4e",
info: "#5bc0de",
default: "#aaaaaa",
}
</script>
## Notification Store
This custom can be used to display toast messages. It has 5 different methods: `send`, `danger`, `warning`, `success`, `info`.
<View name="danger">
<button on:click={() => notifications.danger('This is a danger!')}>Danger</button>
</View>
<View name="warning">
<button on:click={() => notifications.warning('This is a warning!')}>Warning</button>
</View>
<View name="success">
<button on:click={() => notifications.success('This is a success!')}>Success</button>
</View>
<View name="info">
<button on:click={() => notifications.info('This is an info toast!')}>Info</button>
</View>
<div class="notifications">
{#each $notifications as notification (notification.id)}
<div
animate:flip
class="toast"
style="background: {themes[notification.type]};"
transition:fly={{ y: -30 }}>
<div class="content">{notification.message}</div>
{#if notification.icon}<i class={notification.icon} />{/if}
</div>
{/each}
</div>
<style>
.notifications {
position: fixed;
top: 10px;
left: 0;
right: 0;
margin: 0 auto;
padding: 0;
z-index: 9999;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
pointer-events: none;
}
.toast {
flex: 0 0 auto;
margin-bottom: 10px;
border-radius: var(--border-radius-s);
/* The toasts now support being auto sized, so this static width could be removed */
width: 40vw;
}
.content {
padding: 10px;
display: block;
color: white;
font-weight: 500;
}
</style>

View File

@ -0,0 +1,50 @@
import { writable, derived } from "svelte/store"
const NOTIFICATION_TIMEOUT = 3000
const createNotificationStore = () => {
const _notifications = writable([])
const send = (message, type = "default") => {
_notifications.update(state => {
return [...state, { id: id(), type, message }]
})
}
const notifications = derived(_notifications, ($_notifications, set) => {
set($_notifications)
if ($_notifications.length > 0) {
const timeout = setTimeout(() => {
_notifications.update(state => {
state.shift()
return state
})
set($_notifications)
}, NOTIFICATION_TIMEOUT)
return () => {
clearTimeout(timeout)
}
}
})
const { subscribe } = notifications
return {
subscribe,
send,
danger: msg => send(msg, "danger"),
warning: msg => send(msg, "warning"),
info: msg => send(msg, "info"),
success: msg => send(msg, "success"),
}
}
function id() {
return (
"_" +
Math.random()
.toString(36)
.substr(2, 9)
)
}
export const notifications = createNotificationStore()

View File

@ -0,0 +1,72 @@
<script>
export let extraSmall = false,
small = false,
medium = false,
large = false,
extraLarge = false,
white = false,
grey = false,
black = false,
lh = false
</script>
<p
class="bb-body"
class:extraSmall
class:small
class:medium
class:large
class:extraLarge
class:white
class:grey
class:black
class:lh>
<slot />
</p>
<style>
.bb-body {
font-family: var(--font-sans);
font-weight: 400;
text-rendering: var(--text-render);
color: var(--ink);
font-size: var(--font-size-m);
line-height: 0;
}
.extraSmall {
font-size: var(--font-size-xs);
}
.small {
font-size: var(--font-size-s);
}
.medium {
font-size: var(--font-size-m);
}
.large {
font-size: var(--font-size-l);
}
.extraLarge {
font-size: var(--font-size-xl);
}
.white {
color: white;
}
.grey {
color: var(--grey-6);
}
.black {
color: var(--ink);
}
.lh {
line-height: 1.75;
}
</style>

View File

@ -0,0 +1,175 @@
<script>
import { View } from "svench";
</script>
<h1>Borders</h1>
<p>Budibase has 2 border variables, light and dark.</p>
<p><strong>Light</strong> is for layouts.</p>
<p><strong>Dark</strong> is for components.</p>
<code>border: var(--border-light);</code>
<div class="border-light"></div>
<code>border: var(--border-dark);</code>
<div class="border-dark"></div>
<hr>
<h2>Border Radius</h2>
<p>Budibase has 5 border-radius variables:</p>
<table>
<tr>
<th>Name</th>
<th>Var</th>
<th>rem</th>
<th>px</th>
<th>Visual</th>
</tr>
<tr>
<td>Extra Small</td>
<td>var(--border-radius-xs)</td>
<td>0.125</td>
<td>2</td>
<td><div class="border-radius-xs">Extra small</div></td>
</tr>
<tr>
<td>Small</td>
<td>var(--border-radius-s)</td>
<td>0.35</td>
<td>5.6px</td>
<td><div class="border-radius-s">Small</div></td>
</tr>
<tr>
<td>Medium</td>
<td>var(--border-radius-m)</td>
<td>0.5</td>
<td>8</td>
<td><div class="border-radius-m">Medium</div></td>
</tr>
<tr>
<td>Large</td>
<td>var(--border-radius-l)</td>
<td>1</td>
<td>16</td>
<td><div class="border-radius-l">Large</div></td>
</tr>
<tr>
<td>Extra Large</td>
<td>var(--border-radius-xl)</td>
<td>100</td>
<td>1600</td>
<td><div class="border-radius-xl">Extra large</div></td>
</tr>
</table>
<style>
table {
font-family: var(--font-sans);
border-collapse: collapse;
width: 100%;
}
code {
display: inline-block;
box-sizing: border-box;
padding: var(--spacing-s) var(--spacing-l);
margin: var(--spacing-xl) 0 var(--spacing-s) 0;
background-color: var(--grey-2);
color: var(--red-dark);
border-radius: var(--spacing-xs);
}
td, th {
border: 1px solid var(--grey-4);
text-align: left;
padding: var(--spacing-m);
width: 20%;
}
tr:nth-child(even) {
background-color: var(--grey-3);
}
hr {
margin: var(--layout-xl) 0px;
}
.border-light {
width: 40rem;
height: 10rem;
background-color: white;
border: var(--border-light);
}
.border-dark {
width: 40rem;
height: 10rem;
background-color: white;
border: var(--border-dark);
}
.border-radius-xs {
display: flex;
align-items: center;
justify-content: center;
color: white;
font-family: var(--font-sans);
width: 8rem;
height: 3rem;
background-color: black;
border-radius: var(--border-radius-xs);
margin-right: var(--spacing-l);
}
.border-radius-s {
display: flex;
align-items: center;
justify-content: center;
color: white;
font-family: var(--font-sans);
width: 8rem;
height: 3rem;
background-color: black;
border-radius: var(--border-radius-s);
margin-right: var(--spacing-l);
}
.border-radius-m {
display: flex;
align-items: center;
justify-content: center;
color: white;
font-family: var(--font-sans);
width: 8rem;
height: 3rem;
background-color: black;
border-radius: var(--border-radius-m);
margin-right: var(--spacing-l);
}
.border-radius-l {
display: flex;
align-items: center;
justify-content: center;
color: white;
font-family: var(--font-sans);
width: 8rem;
height: 3rem;
background-color: black;
border-radius: var(--border-radius-l);
margin-right: var(--spacing-l);
}
.border-radius-xl {
display: flex;
align-items: center;
justify-content: center;
color: white;
font-family: var(--font-sans);
width: 8rem;
height: 3rem;
background-color: black;
border-radius: var(--border-radius-xl);
margin-right: var(--spacing-l);
}
</style>

View File

@ -0,0 +1,68 @@
<svg
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 115 40"
style="enable-background:new 0 0 115 40;"
xml:space="preserve">
<path
class="st0"
d="M111.16,40H3.91c-2.15,0-3.89-1.74-3.89-3.89V4.04c0-2.15,1.74-3.89,3.89-3.89h107.25
c2.15,0,3.89,1.74,3.89,3.89v32.07C115.05,38.26,113.31,40,111.16,40z" />
<path
class="st1"
d="M10.37,10.03v8.57c0.93-1.26,2.33-1.67,3.61-1.67c1.58,0,3.01,0.59,4.02,1.54c1.12,1.05,1.82,2.62,1.82,4.53
c0,1.78-0.62,3.42-1.82,4.61c-1.01,1.03-2.26,1.57-3.97,1.57c-2.05,0-3.09-0.95-3.66-1.78v1.39H6.63V10.03H10.37z
M10.97,20.98
c-0.44,0.46-0.82,1.13-0.82,2.11c0,0.95,0.41,1.64,0.85,2.05c0.59,0.57,1.41,0.85,2.11,0.85c0.64,0,1.36-0.26,1.93-0.8
c0.54-0.51,0.9-1.26,0.9-2.11c0-0.92-0.36-1.67-0.9-2.18c-0.59-0.57-1.23-0.77-1.98-0.77C12.26,20.14,11.56,20.37,10.97,20.98z" />
<path
class="st1"
d="M25.08,17.35v6.32c0,0.51,0.05,1.31,0.64,1.85c0.26,0.23,0.72,0.54,1.54,0.54c0.69,0,1.23-0.23,1.56-0.54
c0.54-0.51,0.62-1.28,0.62-1.85v-6.32h3.74v6.68c0,1.31-0.13,2.54-1.28,3.67c-1.31,1.28-3.23,1.49-4.59,1.49
c-1.41,0-3.31-0.21-4.62-1.49c-1.05-1.03-1.26-2.18-1.26-3.44v-6.91H25.08z" />
<path
class="st1"
d="M47.88,28.79h-3.74V27.4c-0.57,0.82-1.61,1.78-3.66,1.78c-1.71,0-2.96-0.54-3.97-1.57
c-1.19-1.18-1.82-2.83-1.82-4.61c0-1.9,0.7-3.47,1.82-4.53c1.01-0.95,2.44-1.54,4.02-1.54c1.27,0,2.67,0.41,3.61,1.67v-8.57h3.74
V28.79z
M39.53,20.91c-0.54,0.51-0.9,1.26-0.9,2.18c0,0.85,0.36,1.59,0.9,2.11c0.57,0.54,1.28,0.8,1.93,0.8
c0.69,0,1.52-0.28,2.11-0.85c0.44-0.41,0.85-1.1,0.85-2.05c0-0.98-0.39-1.64-0.82-2.11c-0.59-0.62-1.28-0.85-2.08-0.85
C40.77,20.14,40.12,20.34,39.53,20.91z" />
<path
class="st1"
d="M52.32,10.3c1.21,0,2.16,0.95,2.16,2.16c0,1.21-0.95,2.16-2.16,2.16c-1.21,0-2.16-0.95-2.16-2.16
C50.17,11.25,51.12,10.3,52.32,10.3z M54.19,17.35v11.44h-3.74V17.35H54.19z" />
<path
class="st1"
d="M60.49,10.03v8.57c0.93-1.26,2.33-1.67,3.61-1.67c1.58,0,3.01,0.59,4.02,1.54c1.12,1.05,1.82,2.62,1.82,4.53
c0,1.78-0.62,3.42-1.82,4.61c-1.01,1.03-2.26,1.57-3.97,1.57c-2.05,0-3.09-0.95-3.66-1.78v1.39h-3.74V10.03H60.49z
M61.06,20.98
c-0.44,0.46-0.82,1.13-0.82,2.11c0,0.95,0.41,1.64,0.85,2.05c0.59,0.57,1.41,0.85,2.11,0.85c0.64,0,1.36-0.26,1.93-0.8
c0.54-0.51,0.9-1.26,0.9-2.11c0-0.92-0.36-1.67-0.9-2.18c-0.59-0.57-1.23-0.77-1.98-0.77C62.34,20.14,61.65,20.37,61.06,20.98z" />
<path
class="st1"
d="M80.26,17.35H84v11.44h-3.74v-1.39c-1.01,1.54-2.46,1.77-3.42,1.77c-1.66,0-3.06-0.41-4.33-1.74
c-1.22-1.28-1.69-2.77-1.69-4.28c0-1.92,0.73-3.57,1.79-4.62c1.01-1,2.41-1.56,4.02-1.56c0.99,0,2.57,0.23,3.63,1.67V17.35z
M75.57,20.96c-0.39,0.39-0.85,1.05-0.85,2.08c0,1.03,0.44,1.7,0.77,2.05c0.51,0.54,1.31,0.9,2.18,0.9c0.74,0,1.44-0.31,1.93-0.8
c0.49-0.46,0.9-1.18,0.9-2.16c0-0.82-0.31-1.59-0.85-2.11c-0.57-0.54-1.39-0.8-2.05-0.8C76.8,20.14,76.06,20.47,75.57,20.96z" />
<path
class="st1"
d="M93.21,20.26c-0.57-0.33-1.31-0.64-2.03-0.64c-0.39,0-0.82,0.1-1.05,0.33c-0.13,0.13-0.23,0.33-0.23,0.51
c0,0.26,0.18,0.41,0.36,0.51c0.26,0.15,0.64,0.23,1.1,0.39l0.98,0.31c0.64,0.21,1.31,0.46,1.9,1c0.67,0.62,0.9,1.31,0.9,2.18
c0,1.52-0.67,2.49-1.18,3.01c-1.13,1.13-2.52,1.31-3.72,1.31c-1.54,0-3.21-0.33-4.7-1.64l1.57-2.49c0.36,0.31,0.87,0.67,1.26,0.85
c0.51,0.26,1.05,0.36,1.54,0.36c0.23,0,0.82,0,1.16-0.26c0.23-0.18,0.39-0.46,0.39-0.74c0-0.21-0.08-0.46-0.41-0.67
c-0.26-0.15-0.59-0.26-1.13-0.41l-0.92-0.28c-0.67-0.21-1.36-0.57-1.85-1.05c-0.54-0.57-0.82-1.21-0.82-2.08
c0-1.1,0.44-2.03,1.1-2.65c1.03-0.95,2.41-1.16,3.47-1.16c1.7,0,2.88,0.44,3.8,0.98L93.21,20.26z" />
<path
class="st1"
d="M108.43,23.73h-8.55c0,0.62,0.23,1.44,0.69,1.95c0.57,0.62,1.34,0.72,1.9,0.72c0.54,0,1.1-0.1,1.49-0.33
c0.05-0.03,0.49-0.31,0.8-0.95l3.49,0.36c-0.51,1.62-1.54,2.47-2.21,2.88c-1.1,0.67-2.34,0.85-3.62,0.85
c-1.72,0-3.24-0.31-4.57-1.64c-1-1-1.72-2.52-1.72-4.42c0-1.64,0.59-3.34,1.75-4.52c1.39-1.39,3.11-1.64,4.39-1.64
c1.28,0,3.13,0.23,4.55,1.72c1.36,1.44,1.62,3.24,1.62,4.65V23.73z
M105.03,21.48c-0.03-0.1-0.21-0.82-0.74-1.34
c-0.41-0.39-1-0.64-1.75-0.64c-0.95,0-1.52,0.39-1.87,0.74c-0.28,0.31-0.54,0.72-0.64,1.23H105.03z" />
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,201 @@
<script>
import { View } from "svench";
</script>
<View name="Base">
<div class="white">white</div>
<div class="grey1">var(--grey-1)</div>
<div class="grey2">var(--grey-2)</div>
<div class="grey3">var(--grey-3)</div>
<div class="grey4">var(--grey-4)</div>
<div class="grey5">var(--grey-5)</div>
<div class="grey6">var(--grey-6)</div>
<div class="grey7">var(--grey-7)</div>
<div class="grey8">var(--grey-8)</div>
<div class="grey9">var(--grey-9)</div>
<div class="ink">var(--ink)</div>
</View>
<View name="Blue">
<div class="blue-light">var(--blue-light)</div>
<div class="blue">var(--blue)</div>
<div class="blue-dark">var(--blue-dark)</div>
</View>
<View name="Yellow">
<div class="yellow-light">var(--yellow-light)</div>
<div class="yellow">var(--yellow)</div>
<div class="yellow-dark">var(--yellow-dark)</div>
</View>
<View name="Red">
<div class="red-light">var(--red-light)</div>
<div class="red">var(--red)</div>
<div class="red-dark">var(--red-dark)</div>
</View>
<View name="Orange">
<div class="orange-light">var(--orange-light)</div>
<div class="orange">var(--orange)</div>
<div class="orange-dark">var(--orange-dark)</div>
</View>
<View name="Green">
<div class="green-light">var(--green-light)</div>
<div class="green">var(--green)</div>
<div class="green-dark">var(--green-dark)</div>
</View>
<View name="Purple">
<div class="purple-light">var(--purple-light)</div>
<div class="purple">var(--purple)</div>
<div class="purple-dark">var(--purple-dark)</div>
</View>
<style>
div {
height: 100px;
width: 800px;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-sans);
}
.white {
background-color: white;
}
.grey1 {
background-color: var(--grey-1);
}
.grey1 {
background-color: var(--grey-1);
}
.grey2 {
background-color: var(--grey-2);
}
.grey3 {
background-color: var(--grey-3);
}
.grey4 {
background-color: var(--grey-4);
}
.grey5 {
background-color: var(--grey-5);
}
.grey6 {
background-color: var(--grey-6);
}
.grey7 {
background-color: var(--grey-7);
color: white;
}
.grey8 {
background-color: var(--grey-8);
color: white;
}
.grey9 {
background-color: var(--grey-9);
color: white;
}
.ink {
background-color: var(--ink);
color: white;
}
.blue {
background-color: var(--blue);
color: white;
}
.blue-light {
background-color: var(--blue-light);
}
.blue-dark {
background-color: var(--blue-dark);
color: white;
}
.yellow {
background-color: var(--yellow);
color: white;
}
.yellow-light {
background-color: var(--yellow-light);
}
.yellow-dark {
background-color: var(--yellow-dark);
color: white;
}
.red {
background-color: var(--red);
color: white;
}
.red-light {
background-color: var(--red-light);
}
.red-dark {
background-color: var(--red-dark);
color: white;
}
.orange {
background-color: var(--orange);
color: white;
}
.orange-light {
background-color: var(--orange-light);
}
.orange-dark {
background-color: var(--orange-dark);
color: white;
}
.green {
background-color: var(--green);
color: white;
}
.green-light {
background-color: var(--green-light);
}
.green-dark {
background-color: var(--green-dark);
color: white;
}
.purple {
background-color: var(--purple);
color: white;
}
.purple-light {
background-color: var(--purple-light);
}
.purple-dark {
background-color: var(--purple-dark);
color: white;
}
</style>

View File

@ -0,0 +1,74 @@
<script>
export let extraSmall = false,
small = false,
medium = false,
large = false,
extraLarge = false,
white = false,
grey = false,
black = false,
lh = false
</script>
<h1
class="bb-heading"
class:extraSmall
class:small
class:medium
class:large
class:extraLarge
class:white
class:grey
class:black
class:lh>
<slot />
</h1>
<style>
.bb-heading {
font-family: var(--font-sans);
font-weight: 600;
text-rendering: var(--text-render);
color: var(--ink);
font-size: var(--heading-font-size-m);
line-height: 0;
}
.extraSmall {
font-size: var(--heading-font-size-xs);
}
.small {
font-size: var(--heading-font-size-s);
}
.medium {
font-size: var(--heading-font-size-m);
}
.large {
font-size: var(--heading-font-size-l);
font-weight: 700;
}
.extraLarge {
font-size: var(--heading-font-size-xl);
font-weight: 700;
}
.white {
color: white;
}
.grey {
color: var(--grey-6);
}
.black {
color: var(--ink);
}
.lh {
line-height: 1.3;
}
</style>

View File

@ -0,0 +1,69 @@
<script>
export let forAttr = "",
extraSmall = false,
small = false,
medium = false,
large = false,
extraLarge = false,
white = false,
grey = false,
black = false
</script>
<label
class="bb-label"
class:extraSmall
class:small
class:medium
class:large
class:extraLarge
class:white
class:grey
class:black
for={forAttr}>
<slot />
</label>
<style>
.bb-label {
font-family: var(--font-sans);
font-weight: 500;
text-rendering: var(--text-render);
color: var(--ink);
font-size: var(--font-size-s);
margin-bottom: var(--spacing-s);
display: block;
}
.extraSmall {
font-size: var(--font-size-xs);
}
.small {
font-size: var(--font-size-s);
}
.medium {
font-size: var(--font-size-m);
}
.large {
font-size: var(--font-size-l);
}
.extraLarge {
font-size: var(--font-size-xl);
}
.white {
color: white;
}
.grey {
color: var(--grey-6);
}
.black {
color: var(--ink);
}
</style>

View File

@ -0,0 +1,16 @@
<script>
import BudibaseLogo from "./Budibase-logo.svelte"
</script>
<div class="home-logo">
<BudibaseLogo />
</div>
<style>
.home-logo {
cursor: pointer;
height: 40px;
width: 100px;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,8 @@
<script>
import { View } from "svench";
import Logo from "./Logo.svelte";
</script>
<View name="default">
<Logo />
</View>

View File

@ -0,0 +1,191 @@
<script>
import { View } from "svench";
</script>
<h1>2 Scales, 1 Spatial System</h1>
<p>Budibase has 2 scales, spacing and layout, and follows a 4pt grid system.</p>
<p><strong>Spacing</strong> is used for smaller, more refined spacing needs, specifically within the context of a component.</p>
<p><strong>Layout</strong> is used for laying out your page and positing components within that page.</p>
<p>Spacing and layout will mostly exist alongside margin and padding.</p>
<p><code>margin: var(--spacing-s);</code></p>
<code>padding: var(--spacing-s) var(--spacing-l);</code>
<hr>
<h2>Spacing</h2>
<table>
<tr>
<th>Var</th>
<th>rem</th>
<th>px</th>
<th>Visual</th>
</tr>
<tr>
<td>var(--spacing-xs)</td>
<td>0.25</td>
<td>4</td>
<td><div class="xs"></div></td>
</tr>
<tr>
<td>var(--spacing-s)</td>
<td>0.5</td>
<td>8</td>
<td><div class="s"></div></td>
</tr>
<tr>
<td>var(--spacing-m)</td>
<td>.75</td>
<td>12</td>
<td><div class="m"></div></td>
</tr>
<tr>
<td>var(--spacing-l)</td>
<td>1</td>
<td>16</td>
<td><div class="l"></div></td>
</tr>
<tr>
<td>var(--spacing-xl)</td>
<td>1.25</td>
<td>20</td>
<td><div class="xl"></div></td>
</tr>
</table>
<hr>
<h2>Layout</h2>
<table>
<tr>
<th>Var</th>
<th>rem</th>
<th>px</th>
<th>Visual</th>
</tr>
<tr>
<td>var(--layout-xs)</td>
<td>1.25</td>
<td>20</td>
<td><div class="layout-xs"></div></td>
</tr>
<tr>
<td>var(--layout-s)</td>
<td>1.5</td>
<td>24</td>
<td><div class="layout-s"></div></td>
</tr>
<tr>
<td>var(--layout-m)</td>
<td>2</td>
<td>32</td>
<td><div class="layout-m"></div></td>
</tr>
<tr>
<td>var(--layout-l)</td>
<td>3</td>
<td>48</td>
<td><div class="layout-l"></div></td>
</tr>
<tr>
<td>var(--layout-xl)</td>
<td>4</td>
<td>64</td>
<td><div class="layout-xl"></div></td>
</tr>
</table>
<style>
table {
font-family: var(--font-sans);
border-collapse: collapse;
width: 100%;
}
code {
display: inline-block;
box-sizing: border-box;
padding: var(--spacing-s) var(--spacing-l);
margin: var(--spacing-s) 0;
background-color: var(--grey-2);
color: var(--red-dark);
border-radius: var(--spacing-xs);
}
td, th {
border: 1px solid var(--grey-4);
text-align: left;
padding: var(--spacing-m);
width: 25%;
}
tr:nth-child(even) {
background-color: var(--grey-3);
}
hr {
margin: var(--layout-xl) 0px;
}
.xs {
height: var(--spacing-xs);
width: var(--spacing-xs);
background-color: var(--purple);
}
.s {
height: var(--spacing-s);
width: var(--spacing-s);
background-color: var(--purple);
}
.m {
height: var(--spacing-m);
width: var(--spacing-m);
background-color: var(--purple);
}
.l {
height: var(--spacing-l);
width: var(--spacing-l);
background-color: var(--purple);
}
.xl {
height: var(--spacing-xl);
width: var(--spacing-xl);
background-color: var(--purple);
}
.layout-xs {
height: var(--layout-xs);
width: var(--layout-xs);
background-color: var(--blue);
}
.layout-s {
height: var(--layout-s);
width: var(--layout-s);
background-color: var(--blue);
}
.layout-m {
height: var(--layout-m);
width: var(--layout-m);
background-color: var(--blue);
}
.layout-l {
height: var(--layout-l);
width: var(--layout-l);
background-color: var(--blue);
}
.layout-xl {
height: var(--layout-xl);
width: var(--layout-xl);
background-color: var(--blue);
}
</style>

View File

@ -0,0 +1,227 @@
<script>
import { View } from "svench";
import Heading from "./Heading.svelte";
import Body from "./Body.svelte";
import Label from "./Label.svelte";
const username = "bbuserexample";
</script>
<style>
.typography-container {
display: grid;
grid-template-columns: 1fr 1fr;
}
.typography-column {
padding: var(--spacing-l);
}
.typography-column--grey {
background-color: var(--grey-5);
}
.label-wrapper {
padding: var(--spacing-m);
}
</style>
<h1 class="bb-heading bb-heading--large bb-heading--heavy">Typography</h1>
<p>
Budibase uses Inter as its typeface and three types can be used,
<strong>heading</strong>
,
<strong>body</strong>
and
<strong>label</strong>
</p>
<p>Each type has 5 sizes, xs, s, m l and xl.</p>
<p>Each type is available in the colors white, grey or black.</p>
<View name="Heading">
<div class="typography-container">
<div class="typography-column">
<Heading extraLarge black>Heading Extra Large Black</Heading>
<Heading large black>Heading Large Black</Heading>
<Heading medium black>Heading Medium Black</Heading>
<Heading small black>Heading Small Black</Heading>
<Heading extraSmall black>Heading Extra Small Black</Heading>
</div>
<div class="typography-column">
<Heading extraLarge grey>Heading Extra Large Grey</Heading>
<Heading large grey>Heading Large Grey</Heading>
<Heading medium grey>Heading Medium Grey</Heading>
<Heading small grey>Heading Small Grey</Heading>
<Heading extraSmall grey>Heading Extra Small Grey</Heading>
</div>
<div class="typography-column typography-column--grey">
<Heading extraLarge white>Heading Extra Large White</Heading>
<Heading large white>Heading Large White</Heading>
<Heading medium white>Heading Medium White</Heading>
<Heading small white>Heading Small White</Heading>
<Heading extraSmall white>Heading Extra Small White</Heading>
</div>
<div class="typography-column">
<Heading extraLarge black lh>Heading Extra Large Black Line Height</Heading>
<Heading large black lh>Heading Large Black Line Height</Heading>
<Heading medium black lh>Heading Medium Black Line Height</Heading>
<Heading small black lh>Heading Small Black Line Height</Heading>
<Heading extraSmall black lh>Heading Extra Small Black Line Height</Heading>
</div>
</div>
</View>
<View name="Body">
<div class="typography-container">
<div class="typography-column">
<Body extraLarge black>Body Extra Large Black</Body>
<Body large black>Body Large Black</Body>
<Body medium black>Body Medium Black</Body>
<Body small black>Body Small Black</Body>
<Body extraSmall black>Body Extra Small Black</Body>
</div>
<div class="typography-column">
<Body extraLarge lh black>Body Extra Large Black Line-height</Body>
<Body large lh black>Body Large Black Line-height</Body>
<Body medium lh black>Body Medium Black Line-height</Body>
<Body small lh black>Body Small Black Line-height</Body>
<Body extraSmall lh black>Body Extra Small Black Line-height</Body>
</div>
<div class="typography-column">
<Body extraLarge grey>Body Extra Large Grey</Body>
<Body large grey>Body Large Grey</Body>
<Body medium grey>Body Medium Grey</Body>
<Body small grey>Body Small Grey</Body>
<Body extraSmall grey>Body Extra Small Grey</Body>
</div>
<div class="typography-column typography-column--grey">
<Body extraLarge white>Body Extra Large White</Body>
<Body large white>Body Large White</Body>
<Body medium white>Body Medium White</Body>
<Body small white>Body Small White</Body>
<Body extraSmall white>Body Extra Small White</Body>
</div>
</div>
</View>
<View name="Label">
<div class="typography-container">
<div class="label-wrapper">
<Label extraLarge black forAttr={username}>Label Extra Large Black</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label large black forAttr={username}>Label Large Black</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label medium black forAttr={username}>Label Medium Black</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label small black forAttr={username}>Label Small Black</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label extraSmall black forAttr={username}>Label Extra Small Black</Label>
<input type="text" />
</div>
</div>
<div class="typography-container">
<div class="label-wrapper">
<Label extraLarge grey forAttr={username}>Label Extra Large Grey</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label large grey forAttr={username}>Label Large Grey</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label medium grey forAttr={username}>Label Medium Grey</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label small grey forAttr={username}>Label Small Grey</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label extraSmall grey forAttr={username}>Label Extra Small Grey</Label>
<input type="text" />
</div>
</div>
<div class="typography-container typography-column--grey">
<div class="label-wrapper">
<Label extraLarge white forAttr={username}>Label Extra Large White</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label large white forAttr={username}>Label Large White</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label medium white forAttr={username}>Label Medium White</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label small white forAttr={username}>Label Small White</Label>
<input type="text" />
</div>
<div class="label-wrapper">
<Label extraSmall white forAttr={username}>Label Extra Small White</Label>
<input type="text" />
</div>
</div>
</View>

View File

@ -0,0 +1,47 @@
<script>
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
export let headings
export let value
</script>
<section>
<header>
{#each headings as heading}
<span
class:selected={value === heading.key}
on:click={() => {
value = heading.key
dispatch('change', { heading })
}}>
{heading.title}
</span>
{/each}
</header>
<slot />
</section>
<style>
header {
font-family: var(--font-sans);
display: flex;
margin-bottom: var(--spacing-l);
}
span {
margin-right: var(--spacing-xl);
font-size: var(--font-size-m);
font-weight: 500;
color: var(--grey-5);
}
span:hover {
cursor: pointer;
}
.selected {
color: var(--ink);
}
</style>

View File

@ -0,0 +1,27 @@
<script>
import { View } from "svench";
import Switcher from "./Switcher.svelte";
const items = [
{
title: "Apps",
key: "APPS"
},
{
title: "Deploy",
key: "DEPLOY"
},
{
title: "Settings",
key: "SETTINGS"
}
]
let value = "APPS"
</script>
<View name="default">
<Switcher headings={items} bind:value={value}>
Content
</Switcher>
</View>

193
packages/bbui/src/bbui.css Normal file
View File

@ -0,0 +1,193 @@
.svench-content {
color: #666;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, "Roboto",
Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
h1.svench-content {
font-size: 2em;
}
h2.svench-content {
font-size: 1.66em;
}
h3.svench-content {
font-size: 1.33em;
}
h4.svench-content {
font-size: 1.33em;
font-weight: normal;
}
h5.svench-content {
font-size: 1em;
font-weight: bold;
}
h6.svench-content {
font-size: 1em;
font-weight: normal;
font-style: italic;
}
:root {
--background: #ffffff;
--ink: #000000;
--grey-1: #fafafa;
--grey-2: #f5f5f5;
--grey-3: #eeeeee;
--grey-4: #e0e0e0;
--grey-5: #bdbdbd;
--grey-6: #9e9e9e;
--grey-7: #757575;
--grey-8: #616161;
--grey-9: #424242;
--blue-light: #f1f4fc;
--blue: #4285f4;
--blue-dark: #2f4c9b;
--red-light: #ffe6e6;
--red: #e26d69;
--red-dark: #800400;
--yellow-light: #fff7e6;
--yellow: #ffd26a;
--yellow-dark: #805900;
--orange-light: #fff0e6;
--orange: #f0955a;
--orange-dark: #803300;
--green-light: #e6ffeb;
--green: #84c991;
--green-dark: #008017;
--purple-light: #e9e6ff;
--purple: #806fde;
--purple-dark: #130080;
--rounded-small: 4px;
--rounded-medium: 8px;
--rounded-large: 16px;
--font-normal: "Inter";
--font-black: "Inter Black";
--font-bold: "Inter Bold";
--font-medium: "Inter Medium";
--font-light: "Inter Light";
--font-sans: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, "Inter",
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-serif: "Georgia", Cambria, Times New Roman, Times, serif;
--font-mono: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
font-size: 16px;
--font-size-xs: 0.75rem;
--font-size-s: 0.875rem;
--font-size-m: 1rem;
--font-size-l: 1.15rem;
--font-size-xl: 1.3rem;
--heading-font-size-xs: 0.875rem;
--heading-font-size-s: 1.12rem;
--heading-font-size-m: 1.5rem;
--heading-font-size-l: 2.6rem;
--heading-font-size-xl: 3rem;
--font-render: optimizeLegibility;
--font-smooth: antialiased;
--spacing-xs: 0.25rem;
--spacing-s: 0.5rem;
--spacing-m: 0.75rem;
--spacing-l: 1rem;
--spacing-xl: 1.25rem;
--layout-xs: 1.25rem;
--layout-s: 1.5rem;
--layout-m: 2rem;
--layout-l: 3rem;
--layout-xl: 4rem;
--border-radius-xs: 0.125rem;
--border-radius-s: 0.35rem;
--border-radius-m: 0.5rem;
--border-radius-l: 1rem;
--border-radius-xl: 100rem;
--border-black: 2px var(--ink) solid;
--border-dark: 2px var(--grey-7) solid;
--border-grey: 1px var(--grey-4) solid;
--border-grey-2: 2px var(--grey-4) solid;
--border-light: 1px var(--grey-3) solid;
--border-light-2: 2px var(--grey-3) solid;
--border-blue: 2px var(--blue) solid;
--border-transparent: 2px transparent solid;
}
h1 {
font-size: 3rem;
font-family: var(--font-sans);
font-weight: 700;
text-rendering: var(--text-render);
-webkit-font-smoothing: var(--text-smooth);
color: var(--ink);
}
h2 {
font-size: 2.25rem;
font-family: var(--font-sans);
font-weight: 600;
text-rendering: var(--text-render);
-webkit-font-smoothing: var(--text-smooth);
color: var(--ink);
}
h3 {
font-size: 1.7rem;
font-family: var(--font-sans);
font-weight: 600;
text-rendering: var(--text-render);
-webkit-font-smoothing: var(--text-smooth);
color: var(--ink);
}
h4 {
font-size: var(--font-size-xl);
font-family: var(--font-sans);
font-weight: 500;
text-rendering: var(--text-render);
-webkit-font-smoothing: var(--text-smooth);
color: var(--ink);
}
h5 {
font-size: var(--font-size-l);
font-family: var(--font-sans);
font-weight: 500;
text-rendering: var(--text-render);
-webkit-font-smoothing: var(--text-smooth);
color: var(--ink);
}
h6 {
font-size: var(--font-size-m);
font-family: var(--font-sans);
font-weight: 500;
text-rendering: var(--text-render);
-webkit-font-smoothing: var(--text-smooth);
color: var(--ink);
}
p {
font-size: var(--font-size);
font-family: var(--font-sans);
font-weight: 400;
text-rendering: var(--text-render);
-webkit-font-smoothing: var(--text-smooth);
color: var(--ink);
}
a {
text-decoration: none;
}

View File

@ -0,0 +1,3 @@
export default {
Modal: "bbui-modal",
}

View File

@ -0,0 +1,40 @@
import "./bbui.css"
// Components
export { default as Input } from "./Form/Input.svelte"
export { default as TextArea } from "./Form/TextArea.svelte"
export { default as RichText } from "./Form/RichText.svelte"
export { default as Select } from "./Form/Select.svelte"
export { default as DataList } from "./Form/DataList.svelte"
export { default as Dropzone } from "./Dropzone/Dropzone.svelte"
export { default as Drawer } from "./Drawer/Drawer.svelte"
export { default as Button } from "./Button/Button.svelte"
export { default as Icon, iconOptions, directions } from "./Icons/Icon.svelte"
export { default as TextButton } from "./Button/TextButton.svelte"
export { default as Toggle } from "./Form/Toggle.svelte"
export { default as Radio } from "./Form/Radio.svelte"
export { default as Checkbox } from "./Form/Checkbox.svelte"
export { default as Home } from "./Links/Home.svelte"
export { default as DetailSummary } from "./List/Items/DetailSummary.svelte"
export { default as Switcher } from "./Switcher/Switcher.svelte"
export { default as DropdownMenu } from "./DropdownMenu/DropdownMenu.svelte"
export { default as Popover } from "./Popover/Popover.svelte"
export { default as Body } from "./Styleguide/Body.svelte"
export { default as Heading } from "./Styleguide/Heading.svelte"
export { default as Label } from "./Styleguide/Label.svelte"
export { default as Close } from "./Button/Close.svelte"
export { default as Modal } from "./Modal/Modal.svelte"
export { default as ModalContent } from "./Modal/ModalContent.svelte"
export { default as Spacer } from "./Spacer/Spacer.svelte"
export { default as DatePicker } from "./DatePicker/DatePicker.svelte"
export { default as Multiselect } from "./Form/Multiselect.svelte"
export { default as Slider } from "./Form/Slider.svelte"
export { default as Context } from "./context"
// Actions
export { default as autoResizeTextArea } from "./Actions/autoresize_textarea"
export { default as positionDropdown } from "./Actions/position_dropdown"
export { default as clickOutside } from "./Actions/click_outside"
// Stores
export { notifications } from "./Stores/notifications"

View File

@ -0,0 +1,14 @@
export default function buildStyle(styles) {
const convertCamel = str => {
return str.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)
}
let str = ""
for (let s in styles) {
if (styles[s]) {
let key = convertCamel(s)
str += `${key}: ${styles[s]}; `
}
}
return str
}

View File

@ -0,0 +1,22 @@
import svelte from "@sveltejs/vite-plugin-svelte"
export default ({ mode }) => {
const isProduction = mode === "production"
return {
build: {
lib: {
entry: "src/index.js",
name: "bbui",
formats: ["es"],
},
minify: isProduction,
},
plugins: [svelte()],
resolve: {
dedupe: ["svelte", "svelte/internal"],
},
rollupOptions: {
external: ["svelte", "svelte/internal"],
},
}
}

6613
packages/bbui/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,3 +7,4 @@ dist/
routify routify
cypress/videos cypress/videos
cypress/screenshots cypress/screenshots
.routify/

View File

@ -1,6 +1,6 @@
## Get Started ## Get Started
`npm install` `yarn install`
Webpack dev server (port 8080): `npm run dev` Vite dev server (port 3000): `yarn run dev`
Build `npm run build` Build `yarn run build`

View File

@ -1,9 +1,9 @@
{ {
"baseUrl": "http://localhost:4001/_builder/", "baseUrl": "http://localhost:10000/builder/",
"video": true, "video": true,
"projectId": "bmbemn", "projectId": "bmbemn",
"env": { "env": {
"PORT": "4001", "PORT": "10000",
"JWT_SECRET": "test" "JWT_SECRET": "test"
} }
} }

View File

@ -1,7 +1,7 @@
context("Create an Application", () => { context("Create an Application", () => {
it("should create a new application", () => { it("should create a new application", () => {
cy.createTestApp() cy.createTestApp()
cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
cy.contains("Cypress Tests").should("exist") cy.contains("Cypress Tests").should("exist")
}) })
}) })

View File

@ -20,7 +20,7 @@ context("Create Bindings", () => {
cy.get("[data-cy=setting-text] input") cy.get("[data-cy=setting-text] input")
.type("{{}{{}{{} Current User._id {}}{}}") .type("{{}{{}{{} Current User._id {}}{}}")
.blur() .blur()
cy.getComponent(componentId).should("have.text", "{{{ user._id }}") cy.getComponent(componentId).should("have.text", "{{{ [user].[_id] }}")
}) })
}) })

View File

@ -43,7 +43,7 @@ context("Create Components", () => {
it("should create a form and reset to match schema", () => { it("should create a form and reset to match schema", () => {
cy.addComponent("Form", "Form").then(() => { cy.addComponent("Form", "Form").then(() => {
cy.get("[data-cy=Settings]").click() cy.get("[data-cy=Settings]").click()
cy.get("[data-cy=setting-datasource]") cy.get("[data-cy=setting-dataSource]")
.contains("Choose option") .contains("Choose option")
.click() .click()
cy.get(".dropdown") cy.get(".dropdown")

View File

@ -70,7 +70,7 @@ context("Create a View", () => {
.eq(1) .eq(1)
.select("age") .select("age")
cy.contains("Save").click() cy.contains("Save").click()
cy.wait(100) cy.wait(1000)
cy.get(".ag-center-cols-viewport").scrollTo("100%") cy.get(".ag-center-cols-viewport").scrollTo("100%")
cy.get("[data-cy=table-header]").then($headers => { cy.get("[data-cy=table-header]").then($headers => {
expect($headers).to.have.length(7) expect($headers).to.have.length(7)
@ -100,6 +100,7 @@ context("Create a View", () => {
cy.contains("Group By").click() cy.contains("Group By").click()
cy.get("select").select("group") cy.get("select").select("group")
cy.contains("Save").click() cy.contains("Save").click()
cy.wait(1000)
cy.get(".ag-center-cols-viewport").scrollTo("100%") cy.get(".ag-center-cols-viewport").scrollTo("100%")
cy.contains("Students").should("be.visible") cy.contains("Students").should("be.visible")
cy.contains("Teachers").should("be.visible") cy.contains("Teachers").should("be.visible")
@ -108,7 +109,8 @@ context("Create a View", () => {
.find(".ag-cell") .find(".ag-cell")
.then($values => { .then($values => {
const values = Array.from($values).map(value => value.textContent) const values = Array.from($values).map(value => value.textContent)
expect(values).to.deep.eq([ expect(values.sort()).to.deep.eq(
[
"Students", "Students",
"23.333333333333332", "23.333333333333332",
"1650", "1650",
@ -116,7 +118,8 @@ context("Create a View", () => {
"25", "25",
"20", "20",
"70", "70",
]) ].sort()
)
}) })
}) })
@ -129,6 +132,7 @@ context("Create a View", () => {
cy.get("input").type(" Updated") cy.get("input").type(" Updated")
cy.contains("Save").click() cy.contains("Save").click()
}) })
cy.wait(1000)
cy.contains("Test View Updated").should("be.visible") cy.contains("Test View Updated").should("be.visible")
}) })
@ -138,6 +142,7 @@ context("Create a View", () => {
.click({ force: true }) .click({ force: true })
cy.get("[data-cy=delete-view]").click() cy.get("[data-cy=delete-view]").click()
cy.contains("Delete View").click() cy.contains("Delete View").click()
cy.wait(1000)
cy.contains("TestView Updated").should("not.be.visible") cy.contains("TestView Updated").should("not.be.visible")
}) })
}) })

View File

@ -25,7 +25,9 @@
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
Cypress.Commands.add("createApp", name => { Cypress.Commands.add("createApp", name => {
cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
// wait for init API calls on visit
cy.wait(100)
cy.contains("Create New Web App").click() cy.contains("Create New Web App").click()
cy.get("body") cy.get("body")
.then($body => { .then($body => {
@ -56,10 +58,10 @@ Cypress.Commands.add("createApp", name => {
}) })
Cypress.Commands.add("deleteApp", name => { Cypress.Commands.add("deleteApp", name => {
cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
cy.get("body").then($body => { cy.get(".apps").then($apps => {
cy.wait(1000) cy.wait(1000)
if ($body.find(`[data-cy="app-${name}"]`).length) { if ($apps.find(`[data-cy="app-${name}"]`).length) {
cy.get(`[data-cy="app-${name}"] a`).click() cy.get(`[data-cy="app-${name}"] a`).click()
cy.get("[data-cy=settings-icon]").click() cy.get("[data-cy=settings-icon]").click()
cy.get(".modal-content").within(() => { cy.get(".modal-content").within(() => {
@ -156,7 +158,7 @@ Cypress.Commands.add("addComponent", (category, component) => {
cy.get(`[data-cy="category-${category}"]`).click() cy.get(`[data-cy="category-${category}"]`).click()
} }
cy.get(`[data-cy="component-${component}"]`).click() cy.get(`[data-cy="component-${component}"]`).click()
cy.wait(500) cy.wait(1000)
cy.location().then(loc => { cy.location().then(loc => {
const params = loc.pathname.split("/") const params = loc.pathname.split("/")
const componentId = params[params.length - 1] const componentId = params[params.length - 1]

View File

@ -0,0 +1,12 @@
<!doctype html>
<html>
<head>
<meta charset='utf8'>
<meta name='viewport' content='width=device-width'>
<title>Budibase Builder</title>
<link rel='icon' type='image/png' href='./src/favicon.png'>
</head>
<body id="app">
<script type="module" src='/src/main.js'></script>
</body>
</html>

View File

@ -4,18 +4,19 @@
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "routify --routify-dir routify -b && rollup -c", "build": "routify -b && vite build --emptyOutDir",
"start": "routify --routify-dir routify -c rollup", "start": "routify -c rollup",
"test": "jest", "test": "jest",
"test:watch": "jest --watchAll", "test:watch": "jest --watchAll",
"dev:builder": "routify --routify-dir routify -c rollup", "dev:builder": "routify -c dev:vite",
"dev:vite": "vite",
"rollup": "rollup -c -w", "rollup": "rollup -c -w",
"cy:setup": "node ./cypress/setup.js", "cy:setup": "node ./cypress/setup.js",
"cy:run": "cypress run", "cy:run": "cypress run",
"cy:open": "cypress open", "cy:open": "cypress open",
"cy:run:ci": "cypress run --browser electron --record --key f308590b-6070-41af-b970-794a3823d451", "cy:run:ci": "cypress run --browser electron --record --key f308590b-6070-41af-b970-794a3823d451",
"cy:test": "start-server-and-test cy:setup http://localhost:4001/_builder cy:run", "cy:test": "start-server-and-test cy:setup http://localhost:10000/builder cy:run",
"cy:ci": "start-server-and-test cy:setup http://localhost:4001/_builder cy:run:ci" "cy:ci": "start-server-and-test cy:setup http://localhost:10000/builder cy:run:ci"
}, },
"jest": { "jest": {
"globals": { "globals": {
@ -28,7 +29,9 @@
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/internals/mocks/fileMock.js", "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/internals/mocks/fileMock.js",
"\\.(css|less|sass|scss)$": "identity-obj-proxy", "\\.(css|less|sass|scss)$": "identity-obj-proxy",
"components(.*)$": "<rootDir>/src/components$1", "components(.*)$": "<rootDir>/src/components$1",
"builderStore(.*)$": "<rootDir>/src/builderStore$1" "builderStore(.*)$": "<rootDir>/src/builderStore$1",
"stores(.*)$": "<rootDir>/src/stores$1",
"analytics(.*)$": "<rootDir>/src/analytics$1"
}, },
"moduleFileExtensions": [ "moduleFileExtensions": [
"js", "js",
@ -42,16 +45,14 @@
"^.+.svelte$": "svelte-jester" "^.+.svelte$": "svelte-jester"
}, },
"transformIgnorePatterns": [ "transformIgnorePatterns": [
"/node_modules/(?!svelte).+\\.js$" "/node_modules/(?!svelte).+\\.js$",
".*string-templates.*"
], ],
"modulePathIgnorePatterns": [ "modulePathIgnorePatterns": [
"<rootDir>/cypress/" "<rootDir>/cypress/"
], ],
"setupFilesAfterEnv": [ "setupFilesAfterEnv": [
"@testing-library/jest-dom/extend-expect" "@testing-library/jest-dom/extend-expect"
],
"setupFiles": [
"./scripts/jestSetup.js"
] ]
}, },
"eslintConfig": { "eslintConfig": {
@ -71,10 +72,7 @@
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@svelteschool/svelte-forms": "0.7.0", "@svelteschool/svelte-forms": "0.7.0",
"codemirror": "^5.59.0", "codemirror": "^5.59.0",
"d3-selection": "^1.4.1",
"deepmerge": "^4.2.2",
"downloadjs": "1.4.7", "downloadjs": "1.4.7",
"fast-sort": "^2.2.0",
"lodash": "4.17.13", "lodash": "4.17.13",
"posthog-js": "1.4.5", "posthog-js": "1.4.5",
"remixicon": "2.5.0", "remixicon": "2.5.0",
@ -86,39 +84,30 @@
"yup": "0.29.2" "yup": "0.29.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.5.5", "@babel/core": "^7.12.14",
"@babel/plugin-transform-runtime": "^7.5.5", "@babel/plugin-transform-runtime": "^7.13.10",
"@babel/preset-env": "^7.5.5", "@babel/preset-env": "^7.13.12",
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.13.10",
"@rollup/plugin-alias": "^3.0.1", "@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-commonjs": "^16.0.0", "@roxi/routify": "2.15.1",
"@rollup/plugin-json": "^4.0.3", "@sveltejs/vite-plugin-svelte": "^1.0.0-next.5",
"@sveltech/routify": "1.7.11", "@testing-library/jest-dom": "^5.11.10",
"@testing-library/jest-dom": "^5.11.0",
"@testing-library/svelte": "^3.0.0", "@testing-library/svelte": "^3.0.0",
"babel-jest": "^24.8.0", "babel-jest": "^26.6.3",
"cypress": "^5.1.0", "cypress": "^5.1.0",
"cypress-terminal-report": "^1.4.1", "cypress-terminal-report": "^1.4.1",
"eslint-plugin-cypress": "^2.11.1", "eslint": "^7.23.0",
"eslint-plugin-cypress": "^2.11.2",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"jest": "^24.8.0", "jest": "^26.6.3",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup": "^2.11.2", "rollup": "^2.44.0",
"rollup-plugin-alias": "^1.5.2", "rollup-plugin-copy": "^3.4.0",
"rollup-plugin-copy": "^3.0.0", "start-server-and-test": "^1.12.1",
"rollup-plugin-css-only": "^2.1.0", "svelte": "^3.36.0",
"rollup-plugin-html": "^0.2.1", "svelte-jester": "^1.3.2",
"rollup-plugin-livereload": "^1.2.0", "vite": "^2.1.5"
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-svelte": "^6.1.1",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-url": "^2.2.2",
"start-server-and-test": "^1.11.0",
"svelte": "^3.30.0",
"svelte-jester": "^1.0.6"
}, },
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072" "gitHead": "115189f72a850bfb52b65ec61d932531bf327072"
} }

Some files were not shown because too many files have changed in this diff Show More