Merge branch 'next' of github.com:Budibase/budibase into lab-day-search
This commit is contained in:
commit
e661fe8cf2
|
@ -92,6 +92,16 @@ then `cd ` into your local copy.
|
|||
|
||||
### 3. Install and Build
|
||||
|
||||
To develop the Budibase platform you'll need [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) installed.
|
||||
|
||||
#### Quick method
|
||||
|
||||
`yarn setup` will check that all necessary components are installed and setup the repo for usage.
|
||||
|
||||
#### Manual method
|
||||
|
||||
The following commands can be executed to manually get Budibase up and running (assuming Docker/Docker Compose has been installed).
|
||||
|
||||
`yarn` to install project dependencies
|
||||
|
||||
`yarn bootstrap` will install all budibase modules and symlink them together using lerna.
|
||||
|
@ -112,10 +122,17 @@ To run the budibase server and builder in dev mode (i.e. with live reloading):
|
|||
|
||||
1. Open a new console
|
||||
2. `yarn dev` (from root)
|
||||
3. Access the builder on http://localhost:4001/_builder/
|
||||
3. Access the builder on http://localhost:10000/builder
|
||||
|
||||
This will enable watch mode for both the builder app, server, client library and any component libraries.
|
||||
|
||||
### 5. Cleanup
|
||||
|
||||
If you wish to delete all the apps created in development and reset the environment then run the following:
|
||||
|
||||
1. `yarn nuke:docker` will wipe all the Budibase services
|
||||
2. `yarn dev` will restart all the services
|
||||
|
||||
## Data Storage
|
||||
|
||||
When you are running locally, budibase stores data on disk using [PouchDB](https://pouchdb.com/), as well as some JSON on local files. After setting up budibase, you can find all of this data in the `~/.budibase` directory.
|
||||
|
|
|
@ -15,6 +15,22 @@
|
|||
"created_at": "2021-04-14T16:20:04Z",
|
||||
"repoId": 190729906,
|
||||
"pullRequestNo": 1383
|
||||
},
|
||||
{
|
||||
"name": "aptkingston",
|
||||
"id": 9075550,
|
||||
"comment_id": 830252031,
|
||||
"created_at": "2021-04-30T17:37:37Z",
|
||||
"repoId": 190729906,
|
||||
"pullRequestNo": 1431
|
||||
},
|
||||
{
|
||||
"name": "kevmodrome",
|
||||
"id": 534488,
|
||||
"comment_id": 830545461,
|
||||
"created_at": "2021-05-01T05:27:53Z",
|
||||
"repoId": 190729906,
|
||||
"pullRequestNo": 1431
|
||||
}
|
||||
]
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"trailingComma": "es5",
|
||||
"arrowParens": "avoid",
|
||||
"jsxBracketSameLine": false,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"svelteSortOrder" : "scripts-markup-styles"
|
||||
"svelteSortOrder" : "options-scripts-markup-styles"
|
||||
}
|
|
@ -59,6 +59,7 @@ services:
|
|||
container_name: budi-redis-dev
|
||||
restart: always
|
||||
image: redis
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD}
|
||||
ports:
|
||||
- "${REDIS_PORT}:6379"
|
||||
volumes:
|
||||
|
|
|
@ -23,6 +23,8 @@ services:
|
|||
LOG_LEVEL: info
|
||||
SENTRY_DSN: https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131
|
||||
ENABLE_ANALYTICS: "true"
|
||||
REDIS_URL: redis-service:6379
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||
depends_on:
|
||||
- worker-service
|
||||
|
||||
|
@ -43,6 +45,8 @@ services:
|
|||
COUCH_DB_PASSWORD: ${COUCH_DB_PASSWORD}
|
||||
COUCH_DB_URL: http://${COUCH_DB_USER}:${COUCH_DB_PASSWORD}@couchdb-service:5984
|
||||
SELF_HOST_KEY: ${HOSTING_KEY}
|
||||
REDIS_URL: redis-service:6379
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||
depends_on:
|
||||
- minio-service
|
||||
- couch-init
|
||||
|
@ -100,8 +104,7 @@ services:
|
|||
redis-service:
|
||||
restart: always
|
||||
image: redis
|
||||
ports:
|
||||
- "${REDIS_PORT}:6379"
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
|
||||
|
|
|
@ -16,16 +16,16 @@ static_resources:
|
|||
- name: local_services
|
||||
domains: ["*"]
|
||||
routes:
|
||||
# special case to redirect specifically the route path
|
||||
# to the builder, if this were a prefix then it would break minio
|
||||
- match: { path: "/" }
|
||||
redirect: { path_redirect: "/builder/" }
|
||||
|
||||
- match: { prefix: "/db/" }
|
||||
route:
|
||||
cluster: couchdb-service
|
||||
prefix_rewrite: "/"
|
||||
|
||||
- match: { prefix: "/cache/" }
|
||||
route:
|
||||
cluster: redis-service
|
||||
prefix_rewrite: "/"
|
||||
|
||||
- match: { prefix: "/api/admin/" }
|
||||
route:
|
||||
cluster: worker-dev
|
||||
|
@ -38,6 +38,13 @@ static_resources:
|
|||
route:
|
||||
cluster: server-dev
|
||||
|
||||
# the below three cases are needed to make sure
|
||||
# all traffic prefixed for the builder is passed through
|
||||
# correctly.
|
||||
- match: { path: "/" }
|
||||
route:
|
||||
cluster: builder-dev
|
||||
|
||||
- match: { prefix: "/builder/" }
|
||||
route:
|
||||
cluster: builder-dev
|
||||
|
@ -47,10 +54,6 @@ static_resources:
|
|||
cluster: builder-dev
|
||||
prefix_rewrite: "/builder/"
|
||||
|
||||
# special case in dev to redirect no path to builder
|
||||
- match: { path: "/" }
|
||||
redirect: { path_redirect: "/builder/" }
|
||||
|
||||
# minio is on the default route because this works
|
||||
# best, minio + AWS SDK doesn't handle path proxy
|
||||
- match: { prefix: "/" }
|
||||
|
@ -89,20 +92,6 @@ static_resources:
|
|||
address: couchdb-service
|
||||
port_value: 5984
|
||||
|
||||
- name: redis-service
|
||||
connect_timeout: 0.25s
|
||||
type: strict_dns
|
||||
lb_policy: round_robin
|
||||
load_assignment:
|
||||
cluster_name: redis-service
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: redis-service
|
||||
port_value: 6379
|
||||
|
||||
- name: server-dev
|
||||
connect_timeout: 0.25s
|
||||
type: strict_dns
|
||||
|
|
|
@ -21,7 +21,6 @@ static_resources:
|
|||
cluster: app-service
|
||||
prefix_rewrite: "/"
|
||||
|
||||
# special case for presenting our static self hosting page
|
||||
- match: { path: "/" }
|
||||
route:
|
||||
cluster: app-service
|
||||
|
@ -41,11 +40,6 @@ static_resources:
|
|||
cluster: worker-service
|
||||
prefix_rewrite: "/"
|
||||
|
||||
- match: { prefix: "/cache/" }
|
||||
route:
|
||||
cluster: redis-service
|
||||
prefix_rewrite: "/"
|
||||
|
||||
- match: { prefix: "/db/" }
|
||||
route:
|
||||
cluster: couchdb-service
|
||||
|
@ -117,18 +111,3 @@ static_resources:
|
|||
address: couchdb-service
|
||||
port_value: 5984
|
||||
|
||||
- name: redis-service
|
||||
connect_timeout: 0.25s
|
||||
type: strict_dns
|
||||
lb_policy: round_robin
|
||||
load_assignment:
|
||||
cluster_name: redis-service
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: redis-service
|
||||
port_value: 6379
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ MINIO_ACCESS_KEY=budibase
|
|||
MINIO_SECRET_KEY=budibase
|
||||
COUCH_DB_PASSWORD=budibase
|
||||
COUCH_DB_USER=budibase
|
||||
REDIS_PASSWORD=budibase
|
||||
|
||||
# This section contains variables that do not need to be altered under normal circumstances
|
||||
APP_PORT=4002
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const os = require("os")
|
||||
const exec = require("child_process").exec
|
||||
const fs = require("fs")
|
||||
const platform = os.platform()
|
||||
|
||||
const windows = platform === "win32"
|
||||
const mac = platform === "darwin"
|
||||
const linux = platform === "linux"
|
||||
|
||||
function execute(command) {
|
||||
return new Promise(resolve => {
|
||||
exec(command, (err, stdout) => resolve(linux ? !!stdout : true))
|
||||
})
|
||||
}
|
||||
|
||||
async function commandExistsUnix(command) {
|
||||
const unixCmd = `command -v ${command} 2>/dev/null && { echo >&1 ${command}; exit 0; }`
|
||||
return execute(command)
|
||||
}
|
||||
|
||||
async function commandExistsWindows(command) {
|
||||
if (/[\x00-\x1f<>:"|?*]/.test(command)) {
|
||||
return false
|
||||
}
|
||||
return execute(`where ${command}`)
|
||||
}
|
||||
|
||||
function commandExists(command) {
|
||||
return windows ? commandExistsWindows(command) : commandExistsUnix(command)
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const docker = commandExists("docker")
|
||||
const dockerCompose = commandExists("docker-compose")
|
||||
if (docker && dockerCompose) {
|
||||
console.log("Docker installed - continuing.")
|
||||
return
|
||||
}
|
||||
if (mac) {
|
||||
console.log(
|
||||
"Please install docker by visiting: https://docs.docker.com/docker-for-mac/install/"
|
||||
)
|
||||
} else if (windows) {
|
||||
console.log(
|
||||
"Please install docker by visiting: https://docs.docker.com/docker-for-windows/install/"
|
||||
)
|
||||
} else if (linux) {
|
||||
console.log("Beginning automated linux installation.")
|
||||
await execute(`./hosting/scripts/linux/get-docker.sh`)
|
||||
await execute(`./hosting/scripts/linux/get-docker-compose.sh`)
|
||||
} else {
|
||||
console.error(
|
||||
"Platform unknown - please look online for information about installing docker for our OS."
|
||||
)
|
||||
}
|
||||
console.log("Once installation complete please re-run the setup script.")
|
||||
process.exit(-1)
|
||||
}
|
||||
init()
|
|
@ -14,9 +14,10 @@
|
|||
"prettier-plugin-svelte": "^2.2.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"svelte": "^3.30.0"
|
||||
"svelte": "^3.38.2"
|
||||
},
|
||||
"scripts": {
|
||||
"setup": "./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev",
|
||||
"bootstrap": "lerna link && lerna bootstrap",
|
||||
"build": "lerna run build",
|
||||
"initialise": "lerna run initialise",
|
||||
|
|
|
@ -1,18 +1,27 @@
|
|||
{
|
||||
"name": "@budibase/auth",
|
||||
"version": "0.0.1",
|
||||
"version": "0.18.6",
|
||||
"description": "Authentication middlewares for budibase builder and apps",
|
||||
"main": "src/index.js",
|
||||
"author": "Budibase",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"aws-sdk": "^2.901.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"ioredis": "^4.27.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"koa-passport": "^4.1.4",
|
||||
"node-fetch": "^2.6.1",
|
||||
"passport-google-auth": "^1.0.2",
|
||||
"passport-google-oauth": "^2.0.0",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"uuid": "^8.3.2"
|
||||
"sanitize-s3-objectkey": "^0.0.1",
|
||||
"tar-fs": "^2.1.1",
|
||||
"uuid": "^8.3.2",
|
||||
"zlib": "^1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ioredis-mock": "^5.5.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ const getConfigParams = ({ type, group, user }, otherProps = {}) => {
|
|||
* @param {Object} scopes - the type, group and userID scopes of the configuration.
|
||||
* @returns The most granular configuration document based on the scope.
|
||||
*/
|
||||
const determineScopedConfig = async function(db, { type, user, group }) {
|
||||
const getScopedFullConfig = async function (db, { type, user, group }) {
|
||||
const response = await db.allDocs(
|
||||
getConfigParams(
|
||||
{ type, user, group },
|
||||
|
@ -132,31 +132,40 @@ const determineScopedConfig = async function(db, { type, user, group }) {
|
|||
}
|
||||
)
|
||||
)
|
||||
const configs = response.rows.map(row => {
|
||||
|
||||
function determineScore(row) {
|
||||
const config = row.doc
|
||||
|
||||
// Config is specific to a user and a group
|
||||
if (config._id.includes(generateConfigID({ type, user, group }))) {
|
||||
config.score = 4
|
||||
return 4
|
||||
} else if (config._id.includes(generateConfigID({ type, user }))) {
|
||||
// Config is specific to a user only
|
||||
config.score = 3
|
||||
return 3
|
||||
} else if (config._id.includes(generateConfigID({ type, group }))) {
|
||||
// Config is specific to a group only
|
||||
config.score = 2
|
||||
return 2
|
||||
} else if (config._id.includes(generateConfigID({ type }))) {
|
||||
// Config is specific to a type only
|
||||
config.score = 1
|
||||
return 1
|
||||
}
|
||||
return config
|
||||
})
|
||||
return 0
|
||||
}
|
||||
|
||||
// Find the config with the most granular scope based on context
|
||||
const scopedConfig = configs.sort((a, b) => b.score - a.score)[0]
|
||||
const scopedConfig = response.rows.sort(
|
||||
(a, b) => determineScore(a) - determineScore(b)
|
||||
)[0]
|
||||
|
||||
return scopedConfig
|
||||
return scopedConfig && scopedConfig.doc
|
||||
}
|
||||
|
||||
async function getScopedConfig(db, params) {
|
||||
const configDoc = await getScopedFullConfig(db, params)
|
||||
return configDoc && configDoc.config ? configDoc.config : configDoc
|
||||
}
|
||||
|
||||
exports.getScopedConfig = getScopedConfig
|
||||
exports.generateConfigID = generateConfigID
|
||||
exports.getConfigParams = getConfigParams
|
||||
exports.determineScopedConfig = determineScopedConfig
|
||||
exports.getScopedFullConfig = getScopedFullConfig
|
||||
|
|
|
@ -1,5 +1,19 @@
|
|||
function isTest() {
|
||||
return (
|
||||
process.env.NODE_ENV === "jest" ||
|
||||
process.env.NODE_ENV === "cypress" ||
|
||||
process.env.JEST_WORKER_ID != null
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
COUCH_DB_URL: process.env.COUCH_DB_URL,
|
||||
SALT_ROUNDS: process.env.SALT_ROUNDS,
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
|
||||
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
|
||||
MINIO_URL: process.env.MINIO_URL,
|
||||
isTest,
|
||||
}
|
||||
|
|
|
@ -13,6 +13,6 @@ exports.compare = async (data, encrypted) => {
|
|||
return bcrypt.compare(data, encrypted)
|
||||
}
|
||||
|
||||
exports.newid = function() {
|
||||
exports.newid = function () {
|
||||
return v4().replace(/-/g, "")
|
||||
}
|
||||
|
|
|
@ -28,6 +28,14 @@ module.exports = {
|
|||
setDB(pouch)
|
||||
},
|
||||
db: require("./db/utils"),
|
||||
redis: {
|
||||
Client: require("./redis"),
|
||||
utils: require("./redis/utils"),
|
||||
},
|
||||
objectStore: {
|
||||
...require("./objectStore"),
|
||||
...require("./objectStore/utils"),
|
||||
},
|
||||
utils: {
|
||||
...require("./utils"),
|
||||
...require("./hashing"),
|
||||
|
|
|
@ -3,11 +3,35 @@ const database = require("../db")
|
|||
const { getCookie, clearCookie } = require("../utils")
|
||||
const { StaticDatabases } = require("../db/utils")
|
||||
|
||||
module.exports = (noAuthPatterns = []) => {
|
||||
const regex = new RegExp(noAuthPatterns.join("|"))
|
||||
const PARAM_REGEX = /\/:(.*?)\//g
|
||||
|
||||
function buildNoAuthRegex(patterns) {
|
||||
return patterns.map(pattern => {
|
||||
const isObj = typeof pattern === "object" && pattern.route
|
||||
const method = isObj ? pattern.method : "GET"
|
||||
let route = isObj ? pattern.route : pattern
|
||||
|
||||
const matches = route.match(PARAM_REGEX)
|
||||
if (matches) {
|
||||
for (let match of matches) {
|
||||
route = route.replace(match, "/.*/")
|
||||
}
|
||||
}
|
||||
return { regex: new RegExp(route), method }
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = (noAuthPatterns = [], opts) => {
|
||||
const noAuthOptions = noAuthPatterns ? buildNoAuthRegex(noAuthPatterns) : []
|
||||
return async (ctx, next) => {
|
||||
// the path is not authenticated
|
||||
if (regex.test(ctx.request.url)) {
|
||||
const found = noAuthOptions.find(({ regex, method }) => {
|
||||
return (
|
||||
regex.test(ctx.request.url) &&
|
||||
ctx.request.method.toLowerCase() === method.toLowerCase()
|
||||
)
|
||||
})
|
||||
if (found != null) {
|
||||
return next()
|
||||
}
|
||||
try {
|
||||
|
@ -30,10 +54,14 @@ module.exports = (noAuthPatterns = []) => {
|
|||
if (ctx.isAuthenticated !== true) {
|
||||
ctx.isAuthenticated = false
|
||||
}
|
||||
|
||||
return next()
|
||||
} catch (err) {
|
||||
ctx.throw(err.status || 403, err)
|
||||
// allow configuring for public access
|
||||
if (opts && opts.publicAllowed) {
|
||||
ctx.isAuthenticated = false
|
||||
} else {
|
||||
ctx.throw(err.status || 403, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ async function authenticate(token, tokenSecret, profile, done) {
|
|||
* from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport.
|
||||
* @returns Dynamically configured Passport Google Strategy
|
||||
*/
|
||||
exports.strategyFactory = async function(config) {
|
||||
exports.strategyFactory = async function (config) {
|
||||
try {
|
||||
const { clientID, clientSecret, callbackURL } = config
|
||||
|
||||
|
|
|
@ -3,12 +3,12 @@ const env = require("../../environment")
|
|||
|
||||
exports.options = {
|
||||
secretOrKey: env.JWT_SECRET,
|
||||
jwtFromRequest: function(ctx) {
|
||||
jwtFromRequest: function (ctx) {
|
||||
return ctx.cookies.get(Cookies.Auth)
|
||||
},
|
||||
}
|
||||
|
||||
exports.authenticate = async function(jwt, done) {
|
||||
exports.authenticate = async function (jwt, done) {
|
||||
try {
|
||||
return done(null, jwt)
|
||||
} catch (err) {
|
||||
|
|
|
@ -15,7 +15,7 @@ exports.options = {}
|
|||
* @param {*} done - callback from passport to return user information and errors
|
||||
* @returns The authenticated user, or errors if they occur
|
||||
*/
|
||||
exports.authenticate = async function(email, password, done) {
|
||||
exports.authenticate = async function (email, password, done) {
|
||||
if (!email) return done(null, false, "Email Required.")
|
||||
if (!password) return done(null, false, "Password Required.")
|
||||
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
const sanitize = require("sanitize-s3-objectkey")
|
||||
const AWS = require("aws-sdk")
|
||||
const stream = require("stream")
|
||||
const fetch = require("node-fetch")
|
||||
const tar = require("tar-fs")
|
||||
const zlib = require("zlib")
|
||||
const { promisify } = require("util")
|
||||
const { join } = require("path")
|
||||
const fs = require("fs")
|
||||
const env = require("../environment")
|
||||
const { budibaseTempDir, ObjectStoreBuckets } = require("./utils")
|
||||
const { v4 } = require("uuid")
|
||||
|
||||
const streamPipeline = promisify(stream.pipeline)
|
||||
// use this as a temporary store of buckets that are being created
|
||||
const STATE = {
|
||||
bucketCreationPromises: {},
|
||||
}
|
||||
|
||||
const CONTENT_TYPE_MAP = {
|
||||
html: "text/html",
|
||||
css: "text/css",
|
||||
js: "application/javascript",
|
||||
}
|
||||
const STRING_CONTENT_TYPES = [
|
||||
CONTENT_TYPE_MAP.html,
|
||||
CONTENT_TYPE_MAP.css,
|
||||
CONTENT_TYPE_MAP.js,
|
||||
]
|
||||
|
||||
function publicPolicy(bucketName) {
|
||||
return {
|
||||
Version: "2012-10-17",
|
||||
Statement: [
|
||||
{
|
||||
Effect: "Allow",
|
||||
Principal: {
|
||||
AWS: ["*"],
|
||||
},
|
||||
Action: "s3:GetObject",
|
||||
Resource: [`arn:aws:s3:::${bucketName}/*`],
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
const PUBLIC_BUCKETS = [ObjectStoreBuckets.APPS, ObjectStoreBuckets.GLOBAL]
|
||||
|
||||
/**
|
||||
* Gets a connection to the object store using the S3 SDK.
|
||||
* @param {string} bucket the name of the bucket which blobs will be uploaded/retrieved from.
|
||||
* @return {Object} an S3 object store object, check S3 Nodejs SDK for usage.
|
||||
* @constructor
|
||||
*/
|
||||
exports.ObjectStore = bucket => {
|
||||
AWS.config.update({
|
||||
accessKeyId: env.MINIO_ACCESS_KEY,
|
||||
secretAccessKey: env.MINIO_SECRET_KEY,
|
||||
})
|
||||
const config = {
|
||||
s3ForcePathStyle: true,
|
||||
signatureVersion: "v4",
|
||||
params: {
|
||||
Bucket: bucket,
|
||||
},
|
||||
}
|
||||
if (env.MINIO_URL) {
|
||||
config.endpoint = env.MINIO_URL
|
||||
}
|
||||
return new AWS.S3(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an object store and a bucket name this will make sure the bucket exists,
|
||||
* if it does not exist then it will create it.
|
||||
*/
|
||||
exports.makeSureBucketExists = async (client, bucketName) => {
|
||||
try {
|
||||
await client
|
||||
.headBucket({
|
||||
Bucket: bucketName,
|
||||
})
|
||||
.promise()
|
||||
} catch (err) {
|
||||
const promises = STATE.bucketCreationPromises
|
||||
if (promises[bucketName]) {
|
||||
await promises[bucketName]
|
||||
} else if (err.statusCode === 404) {
|
||||
// bucket doesn't exist create it
|
||||
promises[bucketName] = client
|
||||
.createBucket({
|
||||
Bucket: bucketName,
|
||||
})
|
||||
.promise()
|
||||
await promises[bucketName]
|
||||
delete promises[bucketName]
|
||||
// public buckets are quite hidden in the system, make sure
|
||||
// no bucket is set accidentally
|
||||
if (PUBLIC_BUCKETS.includes(bucketName)) {
|
||||
await client
|
||||
.putBucketPolicy({
|
||||
Bucket: bucketName,
|
||||
Policy: JSON.stringify(publicPolicy(bucketName)),
|
||||
})
|
||||
.promise()
|
||||
}
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the contents of a file given the required parameters, useful when
|
||||
* temp files in use (for example file uploaded as an attachment).
|
||||
*/
|
||||
exports.upload = async ({ bucket, filename, path, type, metadata }) => {
|
||||
const extension = [...filename.split(".")].pop()
|
||||
const fileBytes = fs.readFileSync(path)
|
||||
|
||||
const objectStore = exports.ObjectStore(bucket)
|
||||
await exports.makeSureBucketExists(objectStore, bucket)
|
||||
|
||||
const config = {
|
||||
// windows file paths need to be converted to forward slashes for s3
|
||||
Key: sanitize(filename).replace(/\\/g, "/"),
|
||||
Body: fileBytes,
|
||||
ContentType: type || CONTENT_TYPE_MAP[extension.toLowerCase()],
|
||||
}
|
||||
if (metadata) {
|
||||
config.Metadata = metadata
|
||||
}
|
||||
return objectStore.upload(config).promise()
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the upload function but can be used to send a file stream
|
||||
* through to the object store.
|
||||
*/
|
||||
exports.streamUpload = async (bucket, filename, stream) => {
|
||||
const objectStore = exports.ObjectStore(bucket)
|
||||
await exports.makeSureBucketExists(objectStore, bucket)
|
||||
|
||||
const params = {
|
||||
Bucket: bucket,
|
||||
Key: sanitize(filename).replace(/\\/g, "/"),
|
||||
Body: stream,
|
||||
}
|
||||
return objectStore.upload(params).promise()
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieves the contents of a file from the object store, if it is a known content type it
|
||||
* will be converted, otherwise it will be returned as a buffer stream.
|
||||
*/
|
||||
exports.retrieve = async (bucket, filepath) => {
|
||||
const objectStore = exports.ObjectStore(bucket)
|
||||
const params = {
|
||||
Bucket: bucket,
|
||||
Key: sanitize(filepath).replace(/\\/g, "/"),
|
||||
}
|
||||
const response = await objectStore.getObject(params).promise()
|
||||
// currently these are all strings
|
||||
if (STRING_CONTENT_TYPES.includes(response.ContentType)) {
|
||||
return response.Body.toString("utf8")
|
||||
} else {
|
||||
return response.Body
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as retrieval function but puts to a temporary file.
|
||||
*/
|
||||
exports.retrieveToTmp = async (bucket, filepath) => {
|
||||
const data = await exports.retrieve(bucket, filepath)
|
||||
const outputPath = join(budibaseTempDir(), v4())
|
||||
fs.writeFileSync(outputPath, data)
|
||||
return outputPath
|
||||
}
|
||||
|
||||
exports.deleteFolder = async (bucket, folder) => {
|
||||
const client = exports.ObjectStore(bucket)
|
||||
const listParams = {
|
||||
Bucket: bucket,
|
||||
Prefix: folder,
|
||||
}
|
||||
|
||||
let response = await client.listObjects(listParams).promise()
|
||||
if (response.Contents.length === 0) {
|
||||
return
|
||||
}
|
||||
const deleteParams = {
|
||||
Bucket: bucket,
|
||||
Delete: {
|
||||
Objects: [],
|
||||
},
|
||||
}
|
||||
|
||||
response.Contents.forEach(content => {
|
||||
deleteParams.Delete.Objects.push({ Key: content.Key })
|
||||
})
|
||||
|
||||
response = await client.deleteObjects(deleteParams).promise()
|
||||
// can only empty 1000 items at once
|
||||
if (response.Deleted.length === 1000) {
|
||||
return exports.deleteFolder(bucket, folder)
|
||||
}
|
||||
}
|
||||
|
||||
exports.uploadDirectory = async (bucket, localPath, bucketPath) => {
|
||||
let uploads = []
|
||||
const files = fs.readdirSync(localPath, { withFileTypes: true })
|
||||
for (let file of files) {
|
||||
const path = join(bucketPath, file.name)
|
||||
const local = join(localPath, file.name)
|
||||
if (file.isDirectory()) {
|
||||
uploads.push(exports.uploadDirectory(bucket, local, path))
|
||||
} else {
|
||||
uploads.push(
|
||||
exports.streamUpload(bucket, path, fs.createReadStream(local))
|
||||
)
|
||||
}
|
||||
}
|
||||
await Promise.all(uploads)
|
||||
}
|
||||
|
||||
exports.downloadTarball = async (url, bucket, path) => {
|
||||
const response = await fetch(url)
|
||||
if (!response.ok) {
|
||||
throw new Error(`unexpected response ${response.statusText}`)
|
||||
}
|
||||
|
||||
const tmpPath = join(budibaseTempDir(), path)
|
||||
await streamPipeline(response.body, zlib.Unzip(), tar.extract(tmpPath))
|
||||
if (!env.isTest()) {
|
||||
await exports.uploadDirectory(bucket, tmpPath, path)
|
||||
}
|
||||
// return the temporary path incase there is a use for it
|
||||
return tmpPath
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
const { join } = require("path")
|
||||
const { tmpdir } = require("os")
|
||||
|
||||
exports.ObjectStoreBuckets = {
|
||||
BACKUPS: "backups",
|
||||
APPS: "prod-budi-app-assets",
|
||||
TEMPLATES: "templates",
|
||||
GLOBAL: "global",
|
||||
}
|
||||
|
||||
exports.budibaseTempDir = function () {
|
||||
return join(tmpdir(), ".budibase")
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
const env = require("../environment")
|
||||
// ioredis mock is all in memory
|
||||
const Redis = env.isTest() ? require("ioredis-mock") : require("ioredis")
|
||||
const { addDbPrefix, removeDbPrefix, getRedisOptions } = require("./utils")
|
||||
|
||||
const CLUSTERED = false
|
||||
|
||||
// for testing just generate the client once
|
||||
let CLIENT = env.isTest() ? new Redis(getRedisOptions()) : null
|
||||
|
||||
/**
|
||||
* Inits the system, will error if unable to connect to redis cluster (may take up to 10 seconds) otherwise
|
||||
* will return the ioredis client which will be ready to use.
|
||||
* @return {Promise<object>} The ioredis client.
|
||||
*/
|
||||
function init() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// testing uses a single in memory client
|
||||
if (env.isTest()) {
|
||||
return resolve(CLIENT)
|
||||
}
|
||||
// if a connection existed, close it and re-create it
|
||||
if (CLIENT) {
|
||||
CLIENT.disconnect()
|
||||
CLIENT = null
|
||||
}
|
||||
const { opts, host, port } = getRedisOptions(CLUSTERED)
|
||||
if (CLUSTERED) {
|
||||
CLIENT = new Redis.Cluster([{ host, port }], opts)
|
||||
} else {
|
||||
CLIENT = new Redis(opts)
|
||||
}
|
||||
CLIENT.on("end", err => {
|
||||
reject(err)
|
||||
})
|
||||
CLIENT.on("error", err => {
|
||||
reject(err)
|
||||
})
|
||||
CLIENT.on("connect", () => {
|
||||
resolve(CLIENT)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function, takes a redis stream and converts it to a promisified response -
|
||||
* this can only be done with redis streams because they will have an end.
|
||||
* @param stream A redis stream, specifically as this type of stream will have an end.
|
||||
* @return {Promise<object>} The final output of the stream
|
||||
*/
|
||||
function promisifyStream(stream) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const outputKeys = new Set()
|
||||
stream.on("data", keys => {
|
||||
keys.forEach(key => {
|
||||
outputKeys.add(key)
|
||||
})
|
||||
})
|
||||
stream.on("error", err => {
|
||||
reject(err)
|
||||
})
|
||||
stream.on("end", async () => {
|
||||
const keysArray = Array.from(outputKeys)
|
||||
try {
|
||||
let getPromises = []
|
||||
for (let key of keysArray) {
|
||||
getPromises.push(CLIENT.get(key))
|
||||
}
|
||||
const jsonArray = await Promise.all(getPromises)
|
||||
resolve(
|
||||
keysArray.map(key => ({
|
||||
key: removeDbPrefix(key),
|
||||
value: JSON.parse(jsonArray.shift()),
|
||||
}))
|
||||
)
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
class RedisWrapper {
|
||||
constructor(db) {
|
||||
this._db = db
|
||||
}
|
||||
|
||||
async init() {
|
||||
this._client = await init()
|
||||
return this
|
||||
}
|
||||
|
||||
async finish() {
|
||||
this._client.disconnect()
|
||||
}
|
||||
|
||||
async scan() {
|
||||
const db = this._db,
|
||||
client = this._client
|
||||
let stream
|
||||
if (CLUSTERED) {
|
||||
let node = client.nodes("master")
|
||||
stream = node[0].scanStream({ match: db + "-*", count: 100 })
|
||||
} else {
|
||||
stream = client.scanStream({ match: db + "-*", count: 100 })
|
||||
}
|
||||
return promisifyStream(stream)
|
||||
}
|
||||
|
||||
async get(key) {
|
||||
const db = this._db,
|
||||
client = this._client
|
||||
let response = await client.get(addDbPrefix(db, key))
|
||||
// overwrite the prefixed key
|
||||
if (response != null && response.key) {
|
||||
response.key = key
|
||||
}
|
||||
// if its not an object just return the response
|
||||
try {
|
||||
return JSON.parse(response)
|
||||
} catch (err) {
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
async store(key, value, expirySeconds = null) {
|
||||
const db = this._db,
|
||||
client = this._client
|
||||
if (typeof value === "object") {
|
||||
value = JSON.stringify(value)
|
||||
}
|
||||
const prefixedKey = addDbPrefix(db, key)
|
||||
await client.set(prefixedKey, value)
|
||||
if (expirySeconds) {
|
||||
await client.expire(prefixedKey, expirySeconds)
|
||||
}
|
||||
}
|
||||
|
||||
async delete(key) {
|
||||
const db = this._db,
|
||||
client = this._client
|
||||
await client.del(addDbPrefix(db, key))
|
||||
}
|
||||
|
||||
async clear() {
|
||||
const db = this._db
|
||||
let items = await this.scan(db)
|
||||
await Promise.all(items.map(obj => this.delete(db, obj.key)))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RedisWrapper
|
|
@ -0,0 +1,46 @@
|
|||
const env = require("../environment")
|
||||
|
||||
const SLOT_REFRESH_MS = 2000
|
||||
const CONNECT_TIMEOUT_MS = 10000
|
||||
const SEPARATOR = "-"
|
||||
const REDIS_URL = !env.REDIS_URL ? "localhost:6379" : env.REDIS_URL
|
||||
const REDIS_PASSWORD = !env.REDIS_PASSWORD ? "budibase" : env.REDIS_PASSWORD
|
||||
|
||||
exports.Databases = {
|
||||
PW_RESETS: "pwReset",
|
||||
INVITATIONS: "invitation",
|
||||
}
|
||||
|
||||
exports.getRedisOptions = (clustered = false) => {
|
||||
const [host, port] = REDIS_URL.split(":")
|
||||
const opts = {
|
||||
connectTimeout: CONNECT_TIMEOUT_MS,
|
||||
}
|
||||
if (clustered) {
|
||||
opts.redisOptions = {}
|
||||
opts.redisOptions.tls = {}
|
||||
opts.redisOptions.password = REDIS_PASSWORD
|
||||
opts.slotsRefreshTimeout = SLOT_REFRESH_MS
|
||||
opts.dnsLookup = (address, callback) => callback(null, address)
|
||||
} else {
|
||||
opts.host = host
|
||||
opts.port = port
|
||||
opts.password = REDIS_PASSWORD
|
||||
}
|
||||
return { opts, host, port }
|
||||
}
|
||||
|
||||
exports.addDbPrefix = (db, key) => {
|
||||
return `${db}${SEPARATOR}${key}`
|
||||
}
|
||||
|
||||
exports.removeDbPrefix = key => {
|
||||
let parts = key.split(SEPARATOR)
|
||||
if (parts.length >= 2) {
|
||||
parts.shift()
|
||||
return parts.join(SEPARATOR)
|
||||
} else {
|
||||
// return the only part
|
||||
return parts[0]
|
||||
}
|
||||
}
|
|
@ -105,6 +105,12 @@ exports.isClient = ctx => {
|
|||
return ctx.headers["x-budibase-type"] === "client"
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an email address this will use a view to search through
|
||||
* all the users to find one with this email address.
|
||||
* @param {string} email the email to lookup the user by.
|
||||
* @return {Promise<object|null>}
|
||||
*/
|
||||
exports.getGlobalUserByEmail = async email => {
|
||||
const db = getDB(StaticDatabases.GLOBAL.name)
|
||||
try {
|
||||
|
|
|
@ -36,6 +36,21 @@ asynckit@^0.4.0:
|
|||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
|
||||
|
||||
aws-sdk@^2.901.0:
|
||||
version "2.901.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.901.0.tgz#96b387778cf2b3537383fba04994e815f1fab4d4"
|
||||
integrity sha512-prmUjg4mjguamnwaXMdm/g1xtnT9515cjSaV/MhBsMUVzhe66EX7dLwiA7Jo8qUlwFMyCVIcp/2T+6KwJ9sQgQ==
|
||||
dependencies:
|
||||
buffer "4.9.2"
|
||||
events "1.1.1"
|
||||
ieee754 "1.1.13"
|
||||
jmespath "0.15.0"
|
||||
querystring "0.2.0"
|
||||
sax "1.2.1"
|
||||
url "0.10.3"
|
||||
uuid "3.3.2"
|
||||
xml2js "0.4.19"
|
||||
|
||||
aws-sign2@~0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
||||
|
@ -46,6 +61,16 @@ aws4@^1.8.0:
|
|||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
|
||||
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-js@^1.0.2, base64-js@^1.3.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
base64url@3.x.x:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d"
|
||||
|
@ -63,16 +88,60 @@ bcryptjs@^2.4.3:
|
|||
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
|
||||
integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
|
||||
|
||||
bl@^4.0.3:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
|
||||
integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
|
||||
dependencies:
|
||||
buffer "^5.5.0"
|
||||
inherits "^2.0.4"
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||
|
||||
buffer@4.9.2:
|
||||
version "4.9.2"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
|
||||
integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
|
||||
dependencies:
|
||||
base64-js "^1.0.2"
|
||||
ieee754 "^1.1.4"
|
||||
isarray "^1.0.0"
|
||||
|
||||
buffer@^5.5.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.1.13"
|
||||
|
||||
caseless@~0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
|
||||
|
||||
chownr@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
||||
|
||||
cluster-key-slot@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d"
|
||||
integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==
|
||||
|
||||
combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
|
@ -80,6 +149,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
|
|||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
core-util-is@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
|
@ -92,11 +166,23 @@ dashdash@^1.12.0:
|
|||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
debug@^4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
||||
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
denque@^1.1.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.0.tgz#773de0686ff2d8ec2ff92914316a47b73b1c73de"
|
||||
integrity sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==
|
||||
|
||||
ecc-jsbn@~0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
|
||||
|
@ -112,6 +198,18 @@ ecdsa-sig-formatter@1.0.11:
|
|||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
events@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
|
||||
integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=
|
||||
|
||||
extend@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||
|
@ -137,6 +235,20 @@ fast-json-stable-stringify@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
||||
|
||||
fengari-interop@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/fengari-interop/-/fengari-interop-0.1.2.tgz#f7731dcdd2ff4449073fb7ac3c451a8841ce1e87"
|
||||
integrity sha512-8iTvaByZVoi+lQJhHH9vC+c/Yaok9CwOqNQZN6JrVpjmWwW4dDkeblBXhnHC+BoI6eF4Cy5NKW3z6ICEjvgywQ==
|
||||
|
||||
fengari@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/fengari/-/fengari-0.1.4.tgz#72416693cd9e43bd7d809d7829ddc0578b78b0bb"
|
||||
integrity sha512-6ujqUuiIYmcgkGz8MGAdERU57EIluGGPSUgGPTsco657EHa+srq0S3/YUl/r9kx1+D+d4rGfYObd+m8K22gB1g==
|
||||
dependencies:
|
||||
readline-sync "^1.4.9"
|
||||
sprintf-js "^1.1.1"
|
||||
tmp "^0.0.33"
|
||||
|
||||
forever-agent@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||
|
@ -151,6 +263,11 @@ form-data@~2.3.2:
|
|||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
fs-constants@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||
|
||||
getpass@^0.1.1:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
|
||||
|
@ -216,16 +333,68 @@ http-signature@~1.2.0:
|
|||
jsprim "^1.2.2"
|
||||
sshpk "^1.7.0"
|
||||
|
||||
ieee754@1.1.13:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
|
||||
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
|
||||
|
||||
ieee754@^1.1.13, ieee754@^1.1.4:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
inherits@^2.0.3, inherits@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
ioredis-mock@^5.5.5:
|
||||
version "5.5.5"
|
||||
resolved "https://registry.yarnpkg.com/ioredis-mock/-/ioredis-mock-5.5.5.tgz#dec9fedd238c6ab9f56c026fc366533144f8a256"
|
||||
integrity sha512-7SxCAwNtDLC8IFDptqIhOC7ajp3fciVtCrXOEOkpyjPboAGRQkJbnpNPy1NYORoWi+0/iOtUPUQckSKtSQj4DA==
|
||||
dependencies:
|
||||
fengari "^0.1.4"
|
||||
fengari-interop "^0.1.2"
|
||||
lodash "^4.17.21"
|
||||
minimatch "^3.0.4"
|
||||
standard-as-callback "^2.1.0"
|
||||
|
||||
ioredis@^4.27.1:
|
||||
version "4.27.1"
|
||||
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.27.1.tgz#4ef947b455a1b995baa4b0d7e2c4e4f75f746421"
|
||||
integrity sha512-PaFNFeBbOcEYHXAdrJuy7uesJcyvzStTM1aYMchTuky+VgKqDbXhnTJHaDsjAwcTwPx8Asatx+l2DW8zZ2xlsQ==
|
||||
dependencies:
|
||||
cluster-key-slot "^1.1.0"
|
||||
debug "^4.3.1"
|
||||
denque "^1.1.0"
|
||||
lodash.defaults "^4.2.0"
|
||||
lodash.flatten "^4.4.0"
|
||||
p-map "^2.1.0"
|
||||
redis-commands "1.7.0"
|
||||
redis-errors "^1.2.0"
|
||||
redis-parser "^3.0.0"
|
||||
standard-as-callback "^2.1.0"
|
||||
|
||||
is-typedarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||
|
||||
isarray@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||
|
||||
isstream@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
|
||||
|
||||
jmespath@0.15.0:
|
||||
version "0.15.0"
|
||||
resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217"
|
||||
integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=
|
||||
|
||||
jsbn@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
|
@ -296,6 +465,16 @@ koa-passport@^4.1.4:
|
|||
dependencies:
|
||||
passport "^0.4.0"
|
||||
|
||||
lodash.defaults@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
|
||||
integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=
|
||||
|
||||
lodash.flatten@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
|
||||
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
|
||||
|
||||
lodash.includes@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
|
@ -336,7 +515,7 @@ lodash.once@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||
|
||||
lodash@^4.14.0:
|
||||
lodash@^4.14.0, lodash@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
@ -358,11 +537,33 @@ mime@^1.4.1:
|
|||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
mkdirp-classic@^0.5.2:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
||||
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
node-forge@^0.7.1:
|
||||
version "0.7.6"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
|
||||
|
@ -378,6 +579,23 @@ oauth@0.9.x:
|
|||
resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
|
||||
integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE=
|
||||
|
||||
once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
os-tmpdir@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
|
||||
|
||||
p-map@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
|
||||
integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
|
||||
|
||||
passport-google-auth@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/passport-google-auth/-/passport-google-auth-1.0.2.tgz#8b300b5aa442ef433de1d832ed3112877d0b2938"
|
||||
|
@ -471,6 +689,19 @@ psl@^1.1.28:
|
|||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
|
||||
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
|
||||
|
||||
pump@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
|
||||
dependencies:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
punycode@1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
|
||||
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
|
||||
|
||||
punycode@^2.1.0, punycode@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
|
@ -481,6 +712,42 @@ qs@~6.5.2:
|
|||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||
|
||||
querystring@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
|
||||
|
||||
readable-stream@^3.1.1, readable-stream@^3.4.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
string_decoder "^1.1.1"
|
||||
util-deprecate "^1.0.1"
|
||||
|
||||
readline-sync@^1.4.9:
|
||||
version "1.4.10"
|
||||
resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b"
|
||||
integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==
|
||||
|
||||
redis-commands@1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89"
|
||||
integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==
|
||||
|
||||
redis-errors@^1.0.0, redis-errors@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad"
|
||||
integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=
|
||||
|
||||
redis-parser@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4"
|
||||
integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=
|
||||
dependencies:
|
||||
redis-errors "^1.0.0"
|
||||
|
||||
request@^2.72.0, request@^2.74.0:
|
||||
version "2.88.2"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
|
||||
|
@ -507,7 +774,7 @@ request@^2.72.0, request@^2.74.0:
|
|||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
@ -517,11 +784,31 @@ safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sanitize-s3-objectkey@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sanitize-s3-objectkey/-/sanitize-s3-objectkey-0.0.1.tgz#efa9887cd45275b40234fb4bb12fc5754fe64e7e"
|
||||
integrity sha512-ZTk7aqLxy4sD40GWcYWoLfbe05XLmkKvh6vGKe13ADlei24xlezcvjgKy1qRArlaIbIMYaqK7PCalvZtulZlaQ==
|
||||
|
||||
sax@1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
|
||||
integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o=
|
||||
|
||||
sax@>=0.6.0:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
sprintf-js@^1.1.1:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
|
||||
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
|
||||
|
||||
sshpk@^1.7.0:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
||||
|
@ -537,11 +824,51 @@ sshpk@^1.7.0:
|
|||
safer-buffer "^2.0.2"
|
||||
tweetnacl "~0.14.0"
|
||||
|
||||
standard-as-callback@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45"
|
||||
integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==
|
||||
|
||||
string-template@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96"
|
||||
integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y=
|
||||
|
||||
string_decoder@^1.1.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
tar-fs@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
|
||||
integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
|
||||
dependencies:
|
||||
chownr "^1.1.1"
|
||||
mkdirp-classic "^0.5.2"
|
||||
pump "^3.0.0"
|
||||
tar-stream "^2.1.4"
|
||||
|
||||
tar-stream@^2.1.4:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
||||
dependencies:
|
||||
bl "^4.0.3"
|
||||
end-of-stream "^1.4.1"
|
||||
fs-constants "^1.0.0"
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^3.1.1"
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
|
||||
dependencies:
|
||||
os-tmpdir "~1.0.2"
|
||||
|
||||
tough-cookie@~2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
||||
|
@ -574,11 +901,29 @@ uri-js@^4.2.2:
|
|||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
url@0.10.3:
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"
|
||||
integrity sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=
|
||||
dependencies:
|
||||
punycode "1.3.2"
|
||||
querystring "0.2.0"
|
||||
|
||||
util-deprecate@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
utils-merge@1.x.x:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||
|
||||
uuid@3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
|
||||
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
|
||||
|
||||
uuid@^3.3.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
|
@ -597,3 +942,26 @@ verror@1.10.0:
|
|||
assert-plus "^1.0.0"
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
xml2js@0.4.19:
|
||||
version "0.4.19"
|
||||
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
|
||||
integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==
|
||||
dependencies:
|
||||
sax ">=0.6.0"
|
||||
xmlbuilder "~9.0.1"
|
||||
|
||||
xmlbuilder@~9.0.1:
|
||||
version "9.0.7"
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
|
||||
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
|
||||
|
||||
zlib@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/zlib/-/zlib-1.0.5.tgz#6e7c972fc371c645a6afb03ab14769def114fcc0"
|
||||
integrity sha1-bnyXL8NxxkWmr7A6sUdp3vEU/MA=
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"rollup-plugin-postcss": "^4.0.0",
|
||||
"rollup-plugin-svelte": "^7.1.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"svelte": "^3.37.0"
|
||||
"svelte": "^3.38.2"
|
||||
},
|
||||
"keywords": [
|
||||
"svelte"
|
||||
|
|
|
@ -42,12 +42,14 @@
|
|||
class="spectrum-ActionButton spectrum-ActionButton--size{size}"
|
||||
{disabled}
|
||||
on:longPress
|
||||
on:click|preventDefault>
|
||||
on:click|preventDefault
|
||||
>
|
||||
{#if longPressable}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-CornerTriangle100 spectrum-ActionButton-hold"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-CornerTriangle100" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -56,7 +58,8 @@
|
|||
class="spectrum-Icon spectrum-Icon--size{size}"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label={icon}>
|
||||
aria-label={icon}
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// Attaches a spectrum-ActionGroup-item class to buttons inside the div
|
||||
function group(element) {
|
||||
const buttons = Array.from(element.getElementsByTagName("button"))
|
||||
buttons.forEach((button) => {
|
||||
buttons.forEach(button => {
|
||||
button.classList.add("spectrum-ActionGroup-item")
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import Menu from "../Menu/Menu.svelte"
|
||||
|
||||
export let disabled = false
|
||||
export let align = "left"
|
||||
|
||||
let anchor
|
||||
let dropdown
|
||||
|
@ -31,7 +32,7 @@
|
|||
<div use:getAnchor on:click={openMenu}>
|
||||
<slot name="control" />
|
||||
</div>
|
||||
<Popover bind:this={dropdown} {anchor} align="left">
|
||||
<Popover bind:this={dropdown} {anchor} {align}>
|
||||
<Menu>
|
||||
<slot />
|
||||
</Menu>
|
||||
|
|
|
@ -1,12 +1,53 @@
|
|||
<script>
|
||||
import "@spectrum-css/avatar/dist/index-vars.css"
|
||||
let sizes = new Map([
|
||||
["XXS", "--spectrum-alias-avatar-size-50"],
|
||||
["XS", "--spectrum-alias-avatar-size-75"],
|
||||
["S", "--spectrum-alias-avatar-size-200"],
|
||||
["M", "--spectrum-alias-avatar-size-300"],
|
||||
["L", "--spectrum-alias-avatar-size-500"],
|
||||
["XL", "--spectrum-alias-avatar-size-600"],
|
||||
["XXL", "--spectrum-alias-avatar-size-700"],
|
||||
])
|
||||
export let size = "M"
|
||||
export let url = ""
|
||||
export let disabled = false
|
||||
export let name = "John Doe"
|
||||
|
||||
function getInitials(name) {
|
||||
let parts = name.split(" ")
|
||||
return parts.map(name => name[0]).join("")
|
||||
}
|
||||
</script>
|
||||
|
||||
<img
|
||||
class:is-disabled={disabled}
|
||||
class="spectrum-Avatar"
|
||||
src={url}
|
||||
alt="Avatar"
|
||||
/>
|
||||
{#if url}
|
||||
<img
|
||||
class:is-disabled={disabled}
|
||||
class="spectrum-Avatar"
|
||||
src={url}
|
||||
alt="Avatar"
|
||||
style="width: var({sizes.get(size)}); height: var({sizes.get(size)});"
|
||||
/>
|
||||
{:else}
|
||||
<div
|
||||
class:is-disabled={disabled}
|
||||
style="width: var({sizes.get(size)}); height: var({sizes.get(
|
||||
size
|
||||
)}); font-size: calc(var({sizes.get(size)}) / 2)"
|
||||
>
|
||||
{getInitials(name)}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
div {
|
||||
color: white;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-weight: 500;
|
||||
background: #3aab87;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -23,13 +23,15 @@
|
|||
class:active
|
||||
class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
|
||||
{disabled}
|
||||
on:click|preventDefault>
|
||||
on:click|preventDefault
|
||||
>
|
||||
{#if icon}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label={icon}>
|
||||
aria-label={icon}
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
<script>
|
||||
import "@spectrum-css/buttongroup/dist/index-vars.css"
|
||||
export let vertical = false
|
||||
import "@spectrum-css/buttongroup/dist/index-vars.css"
|
||||
export let vertical = false
|
||||
|
||||
function group(element) {
|
||||
const buttons = Array.from(element.getElementsByTagName('button'))
|
||||
buttons.forEach(button => {
|
||||
button.classList.add('spectrum-ButtonGroup-item')
|
||||
})
|
||||
}
|
||||
function group(element) {
|
||||
const buttons = Array.from(element.getElementsByTagName("button"))
|
||||
buttons.forEach(button => {
|
||||
button.classList.add("spectrum-ButtonGroup-item")
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div use:group class="spectrum-ButtonGroup" class:spectrum-ButtonGroup--vertical={vertical}>
|
||||
<slot />
|
||||
<div
|
||||
use:group
|
||||
class="spectrum-ButtonGroup"
|
||||
class:spectrum-ButtonGroup--vertical={vertical}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
|
@ -37,8 +37,10 @@
|
|||
<section class="drawer" transition:slide>
|
||||
<header>
|
||||
<div class="text">
|
||||
<Heading xs>{title}</Heading>
|
||||
<Body xxs><slot name="description" /></Body>
|
||||
<Heading size="XS">{title}</Heading>
|
||||
<Body size="XXS">
|
||||
<slot name="description" />
|
||||
</Body>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<slot name="buttons" />
|
||||
|
|
|
@ -28,11 +28,9 @@
|
|||
border-right: var(--border-light);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
font-family: var(--font-sans);
|
||||
}
|
||||
|
|
|
@ -35,5 +35,6 @@
|
|||
{placeholder}
|
||||
{getOptionLabel}
|
||||
{getOptionValue}
|
||||
on:change={onChange} />
|
||||
on:change={onChange}
|
||||
/>
|
||||
</Field>
|
||||
|
|
|
@ -17,27 +17,31 @@
|
|||
|
||||
<label
|
||||
class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-Checkbox--emphasized"
|
||||
class:is-invalid={!!error}>
|
||||
class:is-invalid={!!error}
|
||||
>
|
||||
<input
|
||||
checked={value}
|
||||
{disabled}
|
||||
on:change={onChange}
|
||||
type="checkbox"
|
||||
class="spectrum-Checkbox-input"
|
||||
{id} />
|
||||
{id}
|
||||
/>
|
||||
<span class="spectrum-Checkbox-box">
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Checkbox-checkmark"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
||||
</svg>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Dash100 spectrum-Checkbox-partialCheckmark"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Dash100" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="spectrum-Checkbox-label">{text || ''}</span>
|
||||
<span class="spectrum-Checkbox-label">{text || ""}</span>
|
||||
</label>
|
||||
|
|
|
@ -51,12 +51,14 @@
|
|||
class="spectrum-InputGroup"
|
||||
class:is-focused={open || focus}
|
||||
class:is-invalid={!!error}
|
||||
class:is-disabled={disabled}>
|
||||
class:is-disabled={disabled}
|
||||
>
|
||||
<div
|
||||
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
||||
class:is-invalid={!!error}
|
||||
class:is-disabled={disabled}
|
||||
class:is-focused={open || focus}>
|
||||
class:is-focused={open || focus}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
on:focus={() => (focus = true)}
|
||||
|
@ -65,18 +67,21 @@
|
|||
{value}
|
||||
{disabled}
|
||||
{placeholder}
|
||||
class="spectrum-Textfield-input spectrum-InputGroup-input" />
|
||||
class="spectrum-Textfield-input spectrum-InputGroup-input"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="spectrum-Picker spectrum-Picker--sizeM spectrum-InputGroup-button"
|
||||
tabindex="-1"
|
||||
aria-haspopup="true"
|
||||
{disabled}
|
||||
on:click={() => (open = true)}>
|
||||
on:click={() => (open = true)}
|
||||
>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon spectrum-InputGroup-icon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
||||
</svg>
|
||||
</button>
|
||||
|
@ -84,7 +89,8 @@
|
|||
<div class="overlay" on:mousedown|self={() => (open = false)} />
|
||||
<div
|
||||
transition:fly={{ y: -20, duration: 200 }}
|
||||
class="spectrum-Popover spectrum-Popover--bottom is-open">
|
||||
class="spectrum-Popover spectrum-Popover--bottom is-open"
|
||||
>
|
||||
<ul class="spectrum-Menu" role="listbox">
|
||||
{#if options && Array.isArray(options)}
|
||||
{#each options as option}
|
||||
|
@ -94,13 +100,16 @@
|
|||
role="option"
|
||||
aria-selected="true"
|
||||
tabindex="0"
|
||||
on:click={() => selectOption(getOptionValue(option))}>
|
||||
<span
|
||||
class="spectrum-Menu-itemLabel">{getOptionLabel(option)}</span>
|
||||
on:click={() => selectOption(getOptionValue(option))}
|
||||
>
|
||||
<span class="spectrum-Menu-itemLabel"
|
||||
>{getOptionLabel(option)}</span
|
||||
>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
||||
</svg>
|
||||
</li>
|
||||
|
|
|
@ -64,7 +64,8 @@
|
|||
on:close={onClose}
|
||||
options={flatpickrOptions}
|
||||
on:change={handleChange}
|
||||
element={`#${flatpickrId}`}>
|
||||
element={`#${flatpickrId}`}
|
||||
>
|
||||
<div
|
||||
id={flatpickrId}
|
||||
class:is-disabled={disabled}
|
||||
|
@ -73,17 +74,20 @@
|
|||
class:is-focused={open}
|
||||
aria-readonly="false"
|
||||
aria-required="false"
|
||||
aria-haspopup="true">
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<div
|
||||
on:click={flatpickr?.open}
|
||||
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
||||
class:is-disabled={disabled}
|
||||
class:is-invalid={!!error}>
|
||||
class:is-invalid={!!error}
|
||||
>
|
||||
{#if !!error}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-Alert" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -94,7 +98,8 @@
|
|||
class="spectrum-Textfield-input spectrum-InputGroup-input"
|
||||
{placeholder}
|
||||
{id}
|
||||
{value} />
|
||||
{value}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
|
@ -102,12 +107,14 @@
|
|||
tabindex="-1"
|
||||
{disabled}
|
||||
class:is-invalid={!!error}
|
||||
on:click={flatpickr?.open}>
|
||||
on:click={flatpickr?.open}
|
||||
>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label="Calendar">
|
||||
aria-label="Calendar"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-Calendar" />
|
||||
</svg>
|
||||
</button>
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
export let fileSizeLimit = BYTES_IN_MB * 20
|
||||
export let processFiles = null
|
||||
export let handleFileTooLarge = null
|
||||
export let gallery = true
|
||||
export let error = null
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const imageExtensions = [
|
||||
|
@ -52,6 +54,8 @@
|
|||
const newValue = [...value, ...processedFiles]
|
||||
dispatch("change", newValue)
|
||||
selectedImageIdx = newValue.length - 1
|
||||
} else {
|
||||
dispatch("change", fileList)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,45 +98,68 @@
|
|||
|
||||
<div class="container">
|
||||
{#if selectedImage}
|
||||
<div class="gallery">
|
||||
<div class="title">
|
||||
<div class="filename">{selectedImage.name}</div>
|
||||
<div class="filesize">
|
||||
{#if selectedImage.size <= BYTES_IN_MB}
|
||||
{`${selectedImage.size / BYTES_IN_KB} KB`}
|
||||
{:else}{`${selectedImage.size / BYTES_IN_MB} MB`}{/if}
|
||||
{#if gallery}
|
||||
<div class="gallery">
|
||||
<div class="title">
|
||||
<div class="filename">{selectedImage.name}</div>
|
||||
<div class="filesize">
|
||||
{#if selectedImage.size <= BYTES_IN_MB}
|
||||
{`${selectedImage.size / BYTES_IN_KB} KB`}
|
||||
{:else}{`${selectedImage.size / BYTES_IN_MB} MB`}{/if}
|
||||
</div>
|
||||
{#if !disabled}
|
||||
<div class="delete-button" on:click={removeFile}>
|
||||
<Icon name="Close" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if !disabled}
|
||||
<div class="delete-button" on:click={removeFile}>
|
||||
<Icon name="Close" />
|
||||
{#if isImage}
|
||||
<img alt="preview" src={selectedImage.url} />
|
||||
{:else}
|
||||
<div class="placeholder">
|
||||
<div class="extension">{selectedImage.extension}</div>
|
||||
<div>Preview not supported</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if isImage}
|
||||
<img alt="preview" src={selectedImage.url} />
|
||||
{:else}
|
||||
<div class="placeholder">
|
||||
<div class="extension">{selectedImage.extension}</div>
|
||||
<div>Preview not supported</div>
|
||||
<div
|
||||
class="nav left"
|
||||
class:visible={selectedImageIdx > 0}
|
||||
on:click={navigateLeft}
|
||||
>
|
||||
<Icon name="ChevronLeft" />
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class="nav left"
|
||||
class:visible={selectedImageIdx > 0}
|
||||
on:click={navigateLeft}>
|
||||
<Icon name="ChevronLeft" />
|
||||
<div
|
||||
class="nav right"
|
||||
class:visible={selectedImageIdx < fileCount - 1}
|
||||
on:click={navigateRight}
|
||||
>
|
||||
<Icon name="ChevronRight" />
|
||||
</div>
|
||||
<div class="footer">File {selectedImageIdx + 1} of {fileCount}</div>
|
||||
</div>
|
||||
<div
|
||||
class="nav right"
|
||||
class:visible={selectedImageIdx < fileCount - 1}
|
||||
on:click={navigateRight}>
|
||||
<Icon name="ChevronRight" />
|
||||
</div>
|
||||
<div class="footer">File {selectedImageIdx + 1} of {fileCount}</div>
|
||||
</div>
|
||||
{:else if value?.length}
|
||||
{#each value as file}
|
||||
<div class="gallery">
|
||||
<div class="title">
|
||||
<div class="filename">{file.name}</div>
|
||||
<div class="filesize">
|
||||
{#if file.size <= BYTES_IN_MB}
|
||||
{`${file.size / BYTES_IN_KB} KB`}
|
||||
{:else}{`${file.size / BYTES_IN_MB} MB`}{/if}
|
||||
</div>
|
||||
{#if !disabled}
|
||||
<div class="delete-button" on:click={removeFile}>
|
||||
<Icon name="Close" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
<div
|
||||
class="spectrum-Dropzone"
|
||||
class:is-invalid={!!error}
|
||||
class:disabled
|
||||
role="region"
|
||||
tabindex="0"
|
||||
|
@ -140,19 +167,22 @@
|
|||
on:dragleave={handleDragLeave}
|
||||
on:dragenter={handleDragOver}
|
||||
on:drop={handleDrop}
|
||||
class:is-dragged={fileDragged}>
|
||||
class:is-dragged={fileDragged}
|
||||
>
|
||||
<div class="spectrum-IllustratedMessage spectrum-IllustratedMessage--cta">
|
||||
<input
|
||||
id={fieldId}
|
||||
{disabled}
|
||||
type="file"
|
||||
multiple
|
||||
on:change={handleFile} />
|
||||
on:change={handleFile}
|
||||
/>
|
||||
<svg
|
||||
class="spectrum-IllustratedMessage-illustration"
|
||||
width="125"
|
||||
height="60"
|
||||
viewBox="0 0 199 97.7"><defs>
|
||||
viewBox="0 0 199 97.7"
|
||||
><defs>
|
||||
<style>
|
||||
.cls-1,
|
||||
.cls-2 {
|
||||
|
@ -170,25 +200,31 @@
|
|||
</defs>
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M110.53,85.66,100.26,95.89a1.09,1.09,0,0,1-1.52,0L88.47,85.66" />
|
||||
d="M110.53,85.66,100.26,95.89a1.09,1.09,0,0,1-1.52,0L88.47,85.66"
|
||||
/>
|
||||
<line class="cls-1" x1="99.5" y1="95.5" x2="99.5" y2="58.5" />
|
||||
<path class="cls-1" d="M105.5,73.5h19a2,2,0,0,0,2-2v-43" />
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M126.5,22.5h-19a2,2,0,0,1-2-2V1.5h-31a2,2,0,0,0-2,2v68a2,2,0,0,0,2,2h19" />
|
||||
d="M126.5,22.5h-19a2,2,0,0,1-2-2V1.5h-31a2,2,0,0,0-2,2v68a2,2,0,0,0,2,2h19"
|
||||
/>
|
||||
<line class="cls-1" x1="105.5" y1="1.5" x2="126.5" y2="22.5" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M47.93,50.49a5,5,0,1,0-4.83-5A4.93,4.93,0,0,0,47.93,50.49Z" />
|
||||
d="M47.93,50.49a5,5,0,1,0-4.83-5A4.93,4.93,0,0,0,47.93,50.49Z"
|
||||
/>
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M36.6,65.93,42.05,60A2.06,2.06,0,0,1,45,60l12.68,13.2" />
|
||||
d="M36.6,65.93,42.05,60A2.06,2.06,0,0,1,45,60l12.68,13.2"
|
||||
/>
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M3.14,73.23,22.42,53.76a1.65,1.65,0,0,1,2.38,0l19.05,19.7" />
|
||||
d="M3.14,73.23,22.42,53.76a1.65,1.65,0,0,1,2.38,0l19.05,19.7"
|
||||
/>
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M139.5,36.5H196A1.49,1.49,0,0,1,197.5,38V72A1.49,1.49,0,0,1,196,73.5H141A1.49,1.49,0,0,1,139.5,72V32A1.49,1.49,0,0,1,141,30.5H154a2.43,2.43,0,0,1,1.67.66l6,5.66" />
|
||||
d="M139.5,36.5H196A1.49,1.49,0,0,1,197.5,38V72A1.49,1.49,0,0,1,196,73.5H141A1.49,1.49,0,0,1,139.5,72V32A1.49,1.49,0,0,1,141,30.5H154a2.43,2.43,0,0,1,1.67.66l6,5.66"
|
||||
/>
|
||||
<rect
|
||||
class="cls-1"
|
||||
x="1.5"
|
||||
|
@ -196,16 +232,21 @@
|
|||
width="58"
|
||||
height="39"
|
||||
rx="2"
|
||||
ry="2" />
|
||||
ry="2"
|
||||
/>
|
||||
</svg>
|
||||
<h2
|
||||
class="spectrum-Heading spectrum-Heading--sizeL spectrum-Heading--light spectrum-IllustratedMessage-heading">
|
||||
class="spectrum-Heading spectrum-Heading--sizeL spectrum-Heading--light spectrum-IllustratedMessage-heading"
|
||||
>
|
||||
Drag and drop your file
|
||||
</h2>
|
||||
{#if !disabled}
|
||||
<p
|
||||
class="spectrum-Body spectrum-Body--sizeS spectrum-IllustratedMessage-description">
|
||||
<label for={fieldId} class="spectrum-Link">Select a file to upload</label>
|
||||
class="spectrum-Body spectrum-Body--sizeS spectrum-IllustratedMessage-description"
|
||||
>
|
||||
<label for={fieldId} class="spectrum-Link"
|
||||
>Select a file to upload</label
|
||||
>
|
||||
<br />
|
||||
from your computer
|
||||
</p>
|
||||
|
@ -229,6 +270,9 @@
|
|||
.spectrum-Dropzone {
|
||||
user-select: none;
|
||||
}
|
||||
.spectrum-Dropzone.is-invalid {
|
||||
border-color: var(--spectrum-global-color-red-400);
|
||||
}
|
||||
input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
|
@ -260,7 +304,7 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
align-items: center;
|
||||
}
|
||||
.filename {
|
||||
flex: 1 1 auto;
|
||||
|
@ -315,6 +359,7 @@
|
|||
.delete-button {
|
||||
transition: all 0.3s;
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
}
|
||||
.delete-button i {
|
||||
font-size: 2em;
|
||||
|
|
|
@ -78,4 +78,5 @@
|
|||
{isOptionSelected}
|
||||
{getOptionLabel}
|
||||
{getOptionValue}
|
||||
onSelectOption={toggleOption} />
|
||||
onSelectOption={toggleOption}
|
||||
/>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
export let getOptionValue = option => option
|
||||
export let open = false
|
||||
export let readonly = false
|
||||
export let quiet = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const onClick = e => {
|
||||
|
@ -33,11 +34,13 @@
|
|||
<button
|
||||
{id}
|
||||
class="spectrum-Picker spectrum-Picker--sizeM"
|
||||
class:spectrum-Picker--quiet={quiet}
|
||||
{disabled}
|
||||
class:is-invalid={!!error}
|
||||
class:is-open={open}
|
||||
aria-haspopup="listbox"
|
||||
on:mousedown={onClick}>
|
||||
on:mousedown={onClick}
|
||||
>
|
||||
<span class="spectrum-Picker-label" class:is-placeholder={isPlaceholder}>
|
||||
{fieldText}
|
||||
</span>
|
||||
|
@ -46,14 +49,16 @@
|
|||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Picker-validationIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label="Folder">
|
||||
aria-label="Folder"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-Alert" />
|
||||
</svg>
|
||||
{/if}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
||||
</svg>
|
||||
</button>
|
||||
|
@ -61,7 +66,8 @@
|
|||
<div
|
||||
use:clickOutside={() => (open = false)}
|
||||
transition:fly={{ y: -20, duration: 200 }}
|
||||
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open">
|
||||
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
|
||||
>
|
||||
<ul class="spectrum-Menu" role="listbox">
|
||||
{#if placeholderOption}
|
||||
<li
|
||||
|
@ -70,12 +76,14 @@
|
|||
role="option"
|
||||
aria-selected="true"
|
||||
tabindex="0"
|
||||
on:click={() => onSelectOption(null)}>
|
||||
on:click={() => onSelectOption(null)}
|
||||
>
|
||||
<span class="spectrum-Menu-itemLabel">{placeholderOption}</span>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
||||
</svg>
|
||||
</li>
|
||||
|
@ -88,13 +96,16 @@
|
|||
role="option"
|
||||
aria-selected="true"
|
||||
tabindex="0"
|
||||
on:click={() => onSelectOption(getOptionValue(option, idx))}>
|
||||
<span
|
||||
class="spectrum-Menu-itemLabel">{getOptionLabel(option, idx)}</span>
|
||||
on:click={() => onSelectOption(getOptionValue(option, idx))}
|
||||
>
|
||||
<span class="spectrum-Menu-itemLabel"
|
||||
>{getOptionLabel(option, idx)}</span
|
||||
>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
||||
</svg>
|
||||
</li>
|
||||
|
|
|
@ -21,14 +21,16 @@
|
|||
<div
|
||||
title={getOptionLabel(option)}
|
||||
class="spectrum-Radio spectrum-FieldGroup-item spectrum-Radio--emphasized"
|
||||
class:is-invalid={!!error}>
|
||||
class:is-invalid={!!error}
|
||||
>
|
||||
<input
|
||||
on:change={onChange}
|
||||
bind:group={value}
|
||||
value={getOptionValue(option)}
|
||||
type="radio"
|
||||
class="spectrum-Radio-input"
|
||||
{disabled} />
|
||||
{disabled}
|
||||
/>
|
||||
<span class="spectrum-Radio-button" />
|
||||
<label class="spectrum-Radio-label">{getOptionLabel(option)}</label>
|
||||
</div>
|
||||
|
|
|
@ -34,11 +34,13 @@
|
|||
<div
|
||||
class="spectrum-Textfield"
|
||||
class:is-focused={focus}
|
||||
class:is-disabled={disabled}>
|
||||
class:is-disabled={disabled}
|
||||
>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-icon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-Magnify" />
|
||||
</svg>
|
||||
<input
|
||||
|
@ -46,23 +48,26 @@
|
|||
on:keyup={updateValueOnEnter}
|
||||
{disabled}
|
||||
{id}
|
||||
value={value || ''}
|
||||
placeholder={placeholder || ''}
|
||||
value={value || ""}
|
||||
placeholder={placeholder || ""}
|
||||
on:blur={onBlur}
|
||||
on:focus={onFocus}
|
||||
on:input
|
||||
type="search"
|
||||
class="spectrum-Textfield-input spectrum-Search-input"
|
||||
autocomplete="off" />
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
on:click={() => updateValue('')}
|
||||
on:click={() => updateValue("")}
|
||||
type="reset"
|
||||
class="spectrum-ClearButton spectrum-Search-clearButton">
|
||||
class="spectrum-ClearButton spectrum-Search-clearButton"
|
||||
>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Cross75"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Cross75" />
|
||||
</svg>
|
||||
</button>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
export let getOptionLabel = option => option
|
||||
export let getOptionValue = option => option
|
||||
export let readonly = false
|
||||
export let quiet = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let open = false
|
||||
|
@ -43,6 +44,7 @@
|
|||
<Picker
|
||||
on:click
|
||||
bind:open
|
||||
{quiet}
|
||||
{id}
|
||||
{error}
|
||||
{disabled}
|
||||
|
@ -51,7 +53,8 @@
|
|||
{options}
|
||||
{getOptionLabel}
|
||||
{getOptionValue}
|
||||
isPlaceholder={value == null || value === ''}
|
||||
isPlaceholder={value == null || value === ""}
|
||||
placeholderOption={placeholder}
|
||||
isOptionSelected={option => option === value}
|
||||
onSelectOption={selectOption} />
|
||||
onSelectOption={selectOption}
|
||||
/>
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
on:change={onChange}
|
||||
{id}
|
||||
type="checkbox"
|
||||
class="spectrum-Switch-input" />
|
||||
class="spectrum-Switch-input"
|
||||
/>
|
||||
<span class="spectrum-Switch-switch" />
|
||||
<label class="spectrum-Switch-label" for={id}>{text}</label>
|
||||
</div>
|
||||
|
|
|
@ -25,23 +25,28 @@
|
|||
class="spectrum-Textfield spectrum-Textfield--multiline"
|
||||
class:is-invalid={!!error}
|
||||
class:is-disabled={disabled}
|
||||
class:is-focused={focus}>
|
||||
class:is-focused={focus}
|
||||
>
|
||||
{#if error}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
||||
class="spectrum-Icon spectrum-Icon--sizeM
|
||||
spectrum-Textfield-validationIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-Alert" />
|
||||
</svg>
|
||||
{/if}
|
||||
<!-- prettier-ignore -->
|
||||
<textarea
|
||||
bind:this={textarea}
|
||||
placeholder={placeholder || ''}
|
||||
placeholder={placeholder || ""}
|
||||
class="spectrum-Textfield-input"
|
||||
{disabled}
|
||||
{id}
|
||||
on:focus={() => (focus = true)}
|
||||
on:blur={onChange}>{value || ''}</textarea>
|
||||
on:blur={onChange}
|
||||
>{value || ""}</textarea>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
}
|
||||
focus = false
|
||||
updateValue(event.target.value)
|
||||
dispatch("blur")
|
||||
}
|
||||
|
||||
const updateValueOnEnter = event => {
|
||||
|
@ -53,12 +54,14 @@
|
|||
class="spectrum-Textfield"
|
||||
class:is-invalid={!!error}
|
||||
class:is-disabled={disabled}
|
||||
class:is-focused={focus}>
|
||||
class:is-focused={focus}
|
||||
>
|
||||
{#if error}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-Alert" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -68,13 +71,14 @@
|
|||
{disabled}
|
||||
{readonly}
|
||||
{id}
|
||||
value={value || ''}
|
||||
placeholder={placeholder || ''}
|
||||
value={value || ""}
|
||||
placeholder={placeholder || ""}
|
||||
on:blur={onBlur}
|
||||
on:focus={onFocus}
|
||||
on:input
|
||||
{type}
|
||||
class="spectrum-Textfield-input" />
|
||||
class="spectrum-Textfield-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -25,5 +25,6 @@
|
|||
{value}
|
||||
{placeholder}
|
||||
{enableTime}
|
||||
on:change={onChange} />
|
||||
on:change={onChange}
|
||||
/>
|
||||
</Field>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
export let fileSizeLimit = undefined
|
||||
export let processFiles = undefined
|
||||
export let handleFileTooLarge = undefined
|
||||
export let gallery = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const onChange = e => {
|
||||
|
@ -27,5 +28,7 @@
|
|||
{fileSizeLimit}
|
||||
{processFiles}
|
||||
{handleFileTooLarge}
|
||||
on:change={onChange} />
|
||||
{gallery}
|
||||
on:change={onChange}
|
||||
/>
|
||||
</Field>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
export let error = null
|
||||
</script>
|
||||
|
||||
<div class="spectrum-Form-item" class:above={labelPosition === 'above'}>
|
||||
<div class="spectrum-Form-item" class:above={labelPosition === "above"}>
|
||||
{#if label}
|
||||
<FieldLabel forId={id} {label} position={labelPosition} />
|
||||
{/if}
|
||||
|
|
|
@ -10,8 +10,9 @@
|
|||
|
||||
<label
|
||||
for={forId}
|
||||
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${className}`}>
|
||||
{label || ''}
|
||||
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${className}`}
|
||||
>
|
||||
{label || ""}
|
||||
</label>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -29,5 +29,7 @@
|
|||
{type}
|
||||
on:change={onChange}
|
||||
on:click
|
||||
on:input />
|
||||
on:input
|
||||
on:blur
|
||||
/>
|
||||
</Field>
|
||||
|
|
|
@ -31,5 +31,6 @@
|
|||
{getOptionLabel}
|
||||
{getOptionValue}
|
||||
on:change={onChange}
|
||||
on:click />
|
||||
on:click
|
||||
/>
|
||||
</Field>
|
||||
|
|
|
@ -33,5 +33,6 @@
|
|||
{options}
|
||||
{getOptionLabel}
|
||||
{getOptionValue}
|
||||
on:change={onChange} />
|
||||
on:change={onChange}
|
||||
/>
|
||||
</Field>
|
||||
|
|
|
@ -23,5 +23,6 @@
|
|||
{placeholder}
|
||||
on:change={onChange}
|
||||
on:click
|
||||
on:input />
|
||||
on:input
|
||||
/>
|
||||
</Field>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
export let options = []
|
||||
export let getOptionLabel = option => extractProperty(option, "label")
|
||||
export let getOptionValue = option => extractProperty(option, "value")
|
||||
export let quiet = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const onChange = e => {
|
||||
|
@ -29,6 +30,7 @@
|
|||
|
||||
<Field {label} {labelPosition} {error}>
|
||||
<Select
|
||||
{quiet}
|
||||
{error}
|
||||
{disabled}
|
||||
{readonly}
|
||||
|
@ -38,5 +40,6 @@
|
|||
{getOptionLabel}
|
||||
{getOptionValue}
|
||||
on:change={onChange}
|
||||
on:click />
|
||||
on:click
|
||||
/>
|
||||
</Field>
|
||||
|
|
|
@ -25,5 +25,6 @@
|
|||
{disabled}
|
||||
{value}
|
||||
{placeholder}
|
||||
on:change={onChange} />
|
||||
on:change={onChange}
|
||||
/>
|
||||
</Field>
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
<script>
|
||||
import "@spectrum-css/fieldlabel/dist/index-vars.css"
|
||||
|
||||
export let size = "M"
|
||||
</script>
|
||||
|
||||
<label
|
||||
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeL spectrum-Form-itemLabel`}>
|
||||
<label class={`spectrum-FieldLabel spectrum-FieldLabel--size${size}`}>
|
||||
<slot />
|
||||
</label>
|
||||
|
||||
<style>
|
||||
label {
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
|
@ -5,9 +5,11 @@
|
|||
export let noPadding = false
|
||||
export let gap = "M"
|
||||
export let noGap = false
|
||||
export let alignContent = "normal"
|
||||
</script>
|
||||
|
||||
<div
|
||||
style="align-content:{alignContent};"
|
||||
class:horizontal
|
||||
class="container paddingX-{!noPadding && paddingX} paddingY-{!noPadding &&
|
||||
paddingY} gap-{!noGap && gap}"
|
||||
|
@ -44,6 +46,9 @@
|
|||
padding-top: var(--spacing-l);
|
||||
padding-bottom: var(--spacing-l);
|
||||
}
|
||||
.gap-XS {
|
||||
grid-gap: var(--spacing-s);
|
||||
}
|
||||
.gap-S {
|
||||
grid-gap: var(--spectrum-alias-grid-gutter-xsmall);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<script>
|
||||
export let wide = false
|
||||
</script>
|
||||
|
||||
<div class:wide>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
max-width: 80ch;
|
||||
margin: 0 auto;
|
||||
padding: calc(var(--spacing-xl) * 2);
|
||||
}
|
||||
|
||||
.wide {
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: var(--spacing-xl) calc(var(--spacing-xl) * 2)
|
||||
calc(var(--spacing-xl) * 2) calc(var(--spacing-xl) * 2);
|
||||
}
|
||||
</style>
|
|
@ -17,4 +17,5 @@
|
|||
class:spectrum-Link--secondary={secondary}
|
||||
class:spectrum-Link--overBackground={overBackground}
|
||||
class:spectrum-Link--quiet={quiet}
|
||||
class="spectrum-Link spectrum-Link--size{size}"><slot /></a>
|
||||
class="spectrum-Link spectrum-Link--size{size}"><slot /></a
|
||||
>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
>
|
||||
{#if icon}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Menu-itemIcon"
|
||||
class="spectrum-Icon spectrum-Icon--sizeS spectrum-Menu-itemIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label={icon}
|
||||
|
@ -37,3 +37,9 @@
|
|||
{/if}
|
||||
<span class="spectrum-Menu-itemLabel"><slot /></span>
|
||||
</li>
|
||||
|
||||
<style>
|
||||
.spectrum-Menu-itemIcon {
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import "@spectrum-css/menu/dist/index-vars.css"
|
||||
import "@spectrum-css/menu/dist/index-vars.css"
|
||||
</script>
|
||||
|
||||
<ul class="spectrum-Menu" role="menu">
|
||||
<slot />
|
||||
<slot />
|
||||
</ul>
|
|
@ -1,9 +1,10 @@
|
|||
<script>
|
||||
export let heading
|
||||
export let heading
|
||||
</script>
|
||||
|
||||
<li role="presentation">
|
||||
<span class="spectrum-Menu-sectionHeading">{heading}</span>
|
||||
<ul class="spectrum-Menu" role="group">
|
||||
<slot />
|
||||
</ul>
|
||||
<span class="spectrum-Menu-sectionHeading">{heading}</span>
|
||||
<ul class="spectrum-Menu" role="group">
|
||||
<slot />
|
||||
</ul>
|
||||
</li>
|
|
@ -1 +1 @@
|
|||
<li class="spectrum-Menu-divider" role="separator"></li>
|
||||
<li class="spectrum-Menu-divider" role="separator" />
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<div on:click={increment}>
|
||||
Click me
|
||||
{remaining}
|
||||
more time{remaining === 1 ? '' : 's'}
|
||||
more time{remaining === 1 ? "" : "s"}
|
||||
to close this modal!
|
||||
</div>
|
||||
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
import Context from "../context"
|
||||
|
||||
export let fixed = false
|
||||
export let inline = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let visible = !!fixed
|
||||
let visible = fixed || inline
|
||||
$: dispatch(visible ? "show" : "hide")
|
||||
|
||||
export function show() {
|
||||
|
@ -20,7 +21,7 @@
|
|||
}
|
||||
|
||||
export function hide() {
|
||||
if (!visible || fixed) {
|
||||
if (!visible || fixed || inline) {
|
||||
return
|
||||
}
|
||||
visible = false
|
||||
|
@ -45,18 +46,26 @@
|
|||
|
||||
<svelte:window on:keydown={handleKey} />
|
||||
|
||||
{#if visible}
|
||||
<!-- These svelte if statements need to be defined like this. -->
|
||||
<!-- The modal transitions do not work if nested inside more than one "if" -->
|
||||
{#if visible && inline}
|
||||
<div use:focusFirstInput class="spectrum-Modal inline is-open">
|
||||
<slot />
|
||||
</div>
|
||||
{:else if visible}
|
||||
<Portal target=".modal-container">
|
||||
<div
|
||||
class="spectrum-Underlay is-open"
|
||||
transition:fade={{ duration: 200 }}
|
||||
on:mousedown|self={hide}>
|
||||
transition:fade|local={{ duration: 200 }}
|
||||
on:mousedown|self={hide}
|
||||
>
|
||||
<div class="modal-wrapper" on:mousedown|self={hide}>
|
||||
<div class="modal-inner-wrapper" on:mousedown|self={hide}>
|
||||
<div
|
||||
use:focusFirstInput
|
||||
class="spectrum-Modal is-open"
|
||||
transition:fly={{ y: 30, duration: 200 }}>
|
||||
transition:fly|local={{ y: 30, duration: 200 }}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -96,6 +105,7 @@
|
|||
}
|
||||
|
||||
.spectrum-Modal {
|
||||
background: var(--background);
|
||||
overflow: visible;
|
||||
max-height: none;
|
||||
margin: 40px 0;
|
||||
|
@ -104,4 +114,7 @@
|
|||
--spectrum-global-dimension-size-100
|
||||
);
|
||||
}
|
||||
:global(.spectrum--lightest .spectrum-Modal.inline) {
|
||||
border: var(--border-light);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import Context from "../context"
|
||||
|
||||
export let title = undefined
|
||||
export let size = "small"
|
||||
export let size = "S"
|
||||
export let cancelText = "Cancel"
|
||||
export let confirmText = "Confirm"
|
||||
export let showCancelButton = true
|
||||
|
@ -30,11 +30,16 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="spectrum-Dialog spectrum-Dialog--{size}"
|
||||
class="spectrum-Dialog"
|
||||
class:spectrum-Dialog--small={size === "S"}
|
||||
class:spectrum-Dialog--medium={size === "M"}
|
||||
class:spectrum-Dialog--large={size === "L"}
|
||||
class:spectrum-Dialog--extraLarge={size === "XL"}
|
||||
style="position: relative;"
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
aria-modal="true">
|
||||
aria-modal="true"
|
||||
>
|
||||
<div class="spectrum-Dialog-grid">
|
||||
<h1 class="spectrum-Dialog-heading spectrum-Dialog-heading--noHeader">
|
||||
{title}
|
||||
|
@ -47,7 +52,8 @@
|
|||
</section>
|
||||
{#if showCancelButton || showConfirmButton}
|
||||
<div
|
||||
class="spectrum-ButtonGroup spectrum-Dialog-buttonGroup spectrum-Dialog-buttonGroup--noFooter">
|
||||
class="spectrum-ButtonGroup spectrum-Dialog-buttonGroup spectrum-Dialog-buttonGroup--noFooter"
|
||||
>
|
||||
<slot name="footer" />
|
||||
{#if showCancelButton}
|
||||
<Button group secondary on:click={hide}>{cancelText}</Button>
|
||||
|
@ -58,7 +64,8 @@
|
|||
cta
|
||||
{...$$restProps}
|
||||
disabled={confirmDisabled}
|
||||
on:click={confirm}>
|
||||
on:click={confirm}
|
||||
>
|
||||
{confirmText}
|
||||
</Button>
|
||||
{/if}
|
||||
|
@ -73,6 +80,10 @@
|
|||
</div>
|
||||
|
||||
<style>
|
||||
.spectrum-Dialog--extraLarge {
|
||||
width: 1000px;
|
||||
}
|
||||
|
||||
.content-grid {
|
||||
display: grid;
|
||||
position: relative;
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
bind:this={modal}
|
||||
confirmText="Submit"
|
||||
onConfirm={answerQuiz}
|
||||
on:show={resetState}>
|
||||
on:show={resetState}
|
||||
>
|
||||
{#if error}
|
||||
<p class="error">Wrong answer! Try again.</p>
|
||||
{/if}
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
<script>
|
||||
import "@spectrum-css/toast/dist/index-vars.css"
|
||||
import Portal from "svelte-portal"
|
||||
import { flip } from "svelte/animate"
|
||||
import { fly } from "svelte/transition"
|
||||
import { notifications } from '../Stores/notifications'
|
||||
import "@spectrum-css/toast/dist/index-vars.css"
|
||||
import Portal from "svelte-portal"
|
||||
import { flip } from "svelte/animate"
|
||||
import { fly } from "svelte/transition"
|
||||
import { notifications } from "../Stores/notifications"
|
||||
</script>
|
||||
|
||||
<Portal target=".modal-container">
|
||||
<Portal target=".modal-container">
|
||||
<div class="notifications">
|
||||
{#each $notifications as {type, icon, message, id} (id)}
|
||||
<div animate:flip transition:fly={{ y: -30 }} class="spectrum-Toast spectrum-Toast--{type} notification-offset">
|
||||
{#each $notifications as { type, icon, message, id } (id)}
|
||||
<div
|
||||
animate:flip
|
||||
transition:fly={{ y: -30 }}
|
||||
class="spectrum-Toast spectrum-Toast--{type} notification-offset"
|
||||
>
|
||||
{#if icon}
|
||||
<svg class="spectrum-Icon spectrum-Icon--sizeM spectrum-Toast-typeIcon" focusable="false" aria-hidden="true">
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Toast-typeIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -21,24 +29,24 @@
|
|||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Portal>
|
||||
<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;
|
||||
}
|
||||
.notification-offset {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</Portal>
|
||||
|
||||
<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;
|
||||
}
|
||||
.notification-offset {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -37,7 +37,8 @@
|
|||
use:clickOutside={hide}
|
||||
on:keydown={handleEscape}
|
||||
class="spectrum-Popover is-open"
|
||||
role="presentation">
|
||||
role="presentation"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</Portal>
|
||||
|
|
|
@ -33,22 +33,14 @@
|
|||
>
|
||||
{#if $$slots}
|
||||
<div
|
||||
class:spectrum-FieldLabel--sizeS={s}
|
||||
class:spectrum-FieldLabel--sizeM={m}
|
||||
class:spectrum-FieldLabel--sizeL={l}
|
||||
class:spectrum-FieldLabel--sizeXL={xl}
|
||||
class="spectrum-FieldLabel spectrum-ProgressBar-label"
|
||||
class="spectrum-FieldLabel spectrum-ProgressBar-label spectrum-FieldLabel--size{size}"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
{#if value}
|
||||
<div
|
||||
class:spectrum-FieldLabel--sizeS={s}
|
||||
class:spectrum-FieldLabel--sizeM={m}
|
||||
class:spectrum-FieldLabel--sizeL={l}
|
||||
class:spectrum-FieldLabel--sizeXL={xl}
|
||||
class="spectrum-FieldLabel spectrum-ProgressBar-percentage"
|
||||
class="spectrum-FieldLabel spectrum-ProgressBar-percentage spectrum-FieldLabel--size{size}"
|
||||
>
|
||||
{Math.round($progress)}%
|
||||
</div>
|
||||
|
|
|
@ -1,29 +1,62 @@
|
|||
<script>
|
||||
// WIP! Does not yet work.
|
||||
import "@spectrum-css/progresscircle/dist/index-vars.css"
|
||||
import { tweened } from 'svelte/motion';
|
||||
import { cubicOut } from 'svelte/easing';
|
||||
import "@spectrum-css/progresscircle/dist/index-vars.css"
|
||||
|
||||
export let value = false
|
||||
export let small;
|
||||
export let large;
|
||||
export let size = "M"
|
||||
function convertSize(size) {
|
||||
switch (size) {
|
||||
case "S":
|
||||
return "small"
|
||||
case "L":
|
||||
return "large"
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
export let overBackground;
|
||||
export let value = false
|
||||
export let minValue = 0
|
||||
export let maxValue = 100
|
||||
|
||||
let subMask1Style
|
||||
let subMask2Style
|
||||
$: calculateSubMasks(value)
|
||||
|
||||
function calculateSubMasks(value) {
|
||||
if (value) {
|
||||
let percentage = ((value - minValue) / (maxValue - minValue)) * 100
|
||||
let angle
|
||||
if (percentage > 0 && percentage <= 50) {
|
||||
angle = -180 + (percentage / 50) * 180
|
||||
subMask1Style = `transform: rotate(${angle}deg);`
|
||||
subMask2Style = "transform: rotate(-180deg);"
|
||||
} else if (percentage > 50) {
|
||||
angle = -180 + ((percentage - 50) / 50) * 180
|
||||
subMask1Style = "transform: rotate(0deg);"
|
||||
subMask2Style = `transform: rotate(${angle}deg);`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export let overBackground
|
||||
</script>
|
||||
|
||||
<div class:spectrum-ProgressBar--indeterminate={!value} class:spectrum-ProgressCircle--small={small} class:spectrum-ProgressCircle--large={large} class="spectrum-ProgressCircle">
|
||||
<div class="spectrum-ProgressCircle-track"></div>
|
||||
<div class="spectrum-ProgressCircle-fills">
|
||||
<div class="spectrum-ProgressCircle-fillMask1">
|
||||
<div class="spectrum-ProgressCircle-fillSubMask1">
|
||||
<div class="spectrum-ProgressCircle-fill"></div>
|
||||
</div>
|
||||
<div
|
||||
on:click
|
||||
class:spectrum-ProgressBar--indeterminate={!value}
|
||||
class:spectrum-ProgressCircle--overBackground={overBackground}
|
||||
class="spectrum-ProgressCircle spectrum-ProgressCircle--{convertSize(size)}"
|
||||
>
|
||||
<div class="spectrum-ProgressCircle-track" />
|
||||
<div class="spectrum-ProgressCircle-fills">
|
||||
<div class="spectrum-ProgressCircle-fillMask1">
|
||||
<div class="spectrum-ProgressCircle-fillSubMask1" style={subMask1Style}>
|
||||
<div class="spectrum-ProgressCircle-fill" />
|
||||
</div>
|
||||
<div class="spectrum-ProgressCircle-fillMask2">
|
||||
<div class="spectrum-ProgressCircle-fillSubMask2">
|
||||
<div class="spectrum-ProgressCircle-fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spectrum-ProgressCircle-fillMask2">
|
||||
<div class="spectrum-ProgressCircle-fillSubMask2" style={subMask2Style}>
|
||||
<div class="spectrum-ProgressCircle-fill" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,30 +1,44 @@
|
|||
<script>
|
||||
import { getContext } from 'svelte'
|
||||
const multilevel = getContext('sidenav-type');
|
||||
export let href = "";
|
||||
export let external = false;
|
||||
export let heading = ""
|
||||
export let icon = "";
|
||||
export let selected = false;
|
||||
export let disabled = false;
|
||||
|
||||
import { getContext } from "svelte"
|
||||
const multilevel = getContext("sidenav-type")
|
||||
export let href = ""
|
||||
export let external = false
|
||||
export let heading = ""
|
||||
export let icon = ""
|
||||
export let selected = false
|
||||
export let disabled = false
|
||||
</script>
|
||||
|
||||
<li class="spectrum-SideNav-item" class:is-selected={selected} class:is-disabled={disabled}>
|
||||
{#if heading}
|
||||
<h2 class="spectrum-SideNav-heading" id="nav-heading-{heading}">{heading}</h2>
|
||||
{/if}
|
||||
<a target={external ? '_blank' : '_self'} {href} class="spectrum-SideNav-itemLink" aria-current="page">
|
||||
{#if icon}
|
||||
<svg class="spectrum-Icon spectrum-Icon--sizeM spectrum-SideNav-itemIcon" focusable="false" aria-hidden="true">
|
||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||
</svg>
|
||||
{/if}
|
||||
<slot />
|
||||
</a>
|
||||
{#if multilevel && $$slots.subnav}
|
||||
<ul class="spectrum-SideNav">
|
||||
<slot name="subnav" />
|
||||
</ul>
|
||||
<li
|
||||
class="spectrum-SideNav-item"
|
||||
class:is-selected={selected}
|
||||
class:is-disabled={disabled}
|
||||
>
|
||||
{#if heading}
|
||||
<h2 class="spectrum-SideNav-heading" id="nav-heading-{heading}">
|
||||
{heading}
|
||||
</h2>
|
||||
{/if}
|
||||
<a
|
||||
target={external ? "_blank" : ""}
|
||||
{href}
|
||||
class="spectrum-SideNav-itemLink"
|
||||
aria-current="page"
|
||||
>
|
||||
{#if icon}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-SideNav-itemIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||
</svg>
|
||||
{/if}
|
||||
<slot />
|
||||
</a>
|
||||
{#if multilevel && $$slots.subnav}
|
||||
<ul class="spectrum-SideNav">
|
||||
<slot name="subnav" />
|
||||
</ul>
|
||||
{/if}
|
||||
</li>
|
|
@ -1,8 +1,8 @@
|
|||
<script>
|
||||
import { setContext } from 'svelte'
|
||||
import "@spectrum-css/sidenav/dist/index-vars.css"
|
||||
export let multilevel = false;
|
||||
setContext('sidenav-type', multilevel)
|
||||
import { setContext } from "svelte"
|
||||
import "@spectrum-css/sidenav/dist/index-vars.css"
|
||||
export let multilevel = false
|
||||
setContext("sidenav-type", multilevel)
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
|
|
|
@ -50,12 +50,7 @@ export const createNotificationStore = () => {
|
|||
}
|
||||
|
||||
function id() {
|
||||
return (
|
||||
"_" +
|
||||
Math.random()
|
||||
.toString(36)
|
||||
.substr(2, 9)
|
||||
)
|
||||
return "_" + Math.random().toString(36).substr(2, 9)
|
||||
}
|
||||
|
||||
export const notifications = createNotificationStore()
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
<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>
|
|
@ -1,175 +0,0 @@
|
|||
<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>
|
|
@ -1,68 +0,0 @@
|
|||
<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>
|
Before Width: | Height: | Size: 4.2 KiB |
|
@ -1,201 +0,0 @@
|
|||
<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>
|
|
@ -1,74 +0,0 @@
|
|||
<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>
|
|
@ -1,16 +0,0 @@
|
|||
<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>
|
|
@ -1,8 +0,0 @@
|
|||
<script>
|
||||
import { View } from "svench";
|
||||
import Logo from "./Logo.svelte";
|
||||
</script>
|
||||
|
||||
<View name="default">
|
||||
<Logo />
|
||||
</View>
|
|
@ -1,191 +0,0 @@
|
|||
<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>
|
|
@ -1,227 +0,0 @@
|
|||
<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>
|
|
@ -5,24 +5,28 @@
|
|||
</script>
|
||||
|
||||
<label
|
||||
class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-Checkbox--emphasized">
|
||||
class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-Checkbox--emphasized"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="spectrum-Checkbox-input"
|
||||
id="checkbox-1"
|
||||
disabled
|
||||
checked={!!value} />
|
||||
checked={!!value}
|
||||
/>
|
||||
<span class="spectrum-Checkbox-box">
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Checkbox-checkmark"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
||||
</svg>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Dash100 spectrum-Checkbox-partialCheckmark"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Dash100" />
|
||||
</svg>
|
||||
</span>
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
}
|
||||
$: type = schema?.type ?? "string"
|
||||
$: customRenderer = customRenderers?.find(x => x.column === schema?.name)
|
||||
$: renderer = customRenderer?.component ?? typeMap[type]
|
||||
$: renderer = customRenderer?.component ?? typeMap[type] ?? StringRenderer
|
||||
</script>
|
||||
|
||||
{#if renderer && (customRenderer || (value != null && value !== ''))}
|
||||
{#if renderer && (customRenderer || (value != null && value !== ""))}
|
||||
<svelte:component this={renderer} {row} {schema} {value} on:clickrelationship>
|
||||
<slot />
|
||||
</svelte:component>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
export let value
|
||||
</script>
|
||||
|
||||
<div>{dayjs(value).format('MMMM D YYYY, HH:mm')}</div>
|
||||
<div>{dayjs(value).format("MMMM D YYYY, HH:mm")}</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
// Table state
|
||||
let height = 0
|
||||
let loaded = false
|
||||
$: schema = fixSchema(schema)
|
||||
$: if (!loading) loaded = true
|
||||
$: rows = data ?? []
|
||||
$: visibleRowCount = getVisibleRowCount(loaded, height, rows.length, rowCount)
|
||||
|
@ -50,7 +51,7 @@
|
|||
rows.length
|
||||
)
|
||||
|
||||
// Reset state when data chanegs
|
||||
// Reset state when data changes
|
||||
$: data.length, reset()
|
||||
const reset = () => {
|
||||
nextScrollTop = 0
|
||||
|
@ -59,6 +60,24 @@
|
|||
timeout = null
|
||||
}
|
||||
|
||||
const fixSchema = schema => {
|
||||
let fixedSchema = {}
|
||||
Object.entries(schema || {}).forEach(([fieldName, fieldSchema]) => {
|
||||
if (typeof fieldSchema === "string") {
|
||||
fixedSchema[fieldName] = {
|
||||
type: fieldSchema,
|
||||
name: fieldName,
|
||||
}
|
||||
} else {
|
||||
fixedSchema[fieldName] = {
|
||||
...fieldSchema,
|
||||
name: fieldName,
|
||||
}
|
||||
}
|
||||
})
|
||||
return fixedSchema
|
||||
}
|
||||
|
||||
const getVisibleRowCount = (loaded, height, allRows, rowCount) => {
|
||||
if (!loaded) {
|
||||
return rowCount || 0
|
||||
|
@ -118,7 +137,6 @@
|
|||
if (!field || !fieldSchema) {
|
||||
return
|
||||
}
|
||||
schema[field].name = field
|
||||
if (!fieldSchema?.autocolumn) {
|
||||
columns.push(fieldSchema)
|
||||
} else if (showAutoColumns) {
|
||||
|
@ -192,16 +210,17 @@
|
|||
on:scroll={onScroll}
|
||||
class:quiet
|
||||
style={`--row-height: ${rowHeight}px; --header-height: ${headerHeight}px;`}
|
||||
class="container">
|
||||
class="container"
|
||||
>
|
||||
<div style={contentStyle}>
|
||||
<table class="spectrum-Table" class:spectrum-Table--quiet={quiet}>
|
||||
{#if sortedRows?.length}
|
||||
{#if fields.length}
|
||||
<thead class="spectrum-Table-head">
|
||||
<tr>
|
||||
{#if showEditColumn}
|
||||
<th class="spectrum-Table-headCell">
|
||||
<div class="spectrum-Table-headCell-content">
|
||||
{editColumnTitle || ''}
|
||||
{editColumnTitle || ""}
|
||||
</div>
|
||||
</th>
|
||||
{/if}
|
||||
|
@ -209,15 +228,19 @@
|
|||
<th
|
||||
class="spectrum-Table-headCell"
|
||||
class:is-sortable={schema[field].sortable !== false}
|
||||
class:is-sorted-desc={sortColumn === field && sortOrder === 'Descending'}
|
||||
class:is-sorted-asc={sortColumn === field && sortOrder === 'Ascending'}
|
||||
on:click={() => sortBy(schema[field])}>
|
||||
class:is-sorted-desc={sortColumn === field &&
|
||||
sortOrder === "Descending"}
|
||||
class:is-sorted-asc={sortColumn === field &&
|
||||
sortOrder === "Ascending"}
|
||||
on:click={() => sortBy(schema[field])}
|
||||
>
|
||||
<div class="spectrum-Table-headCell-content">
|
||||
<div class="title">{getDisplayName(schema[field])}</div>
|
||||
{#if schema[field]?.autocolumn}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Table-autoIcon"
|
||||
focusable="false">
|
||||
focusable="false"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-MagicWand" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -225,7 +248,8 @@
|
|||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-ArrowDown100 spectrum-Table-sortedIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Arrow100" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -233,7 +257,8 @@
|
|||
<svg
|
||||
class="spectrum-Icon spectrum-Table-editIcon"
|
||||
focusable="false"
|
||||
on:click={e => editColumn(e, field)}>
|
||||
on:click={e => editColumn(e, field)}
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-Edit" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -244,16 +269,18 @@
|
|||
</thead>
|
||||
{/if}
|
||||
<tbody class="spectrum-Table-body">
|
||||
{#if sortedRows?.length}
|
||||
{#if sortedRows?.length && fields.length}
|
||||
{#each sortedRows as row, idx}
|
||||
<tr
|
||||
on:click={() => toggleSelectRow(row)}
|
||||
class="spectrum-Table-row"
|
||||
class:hidden={idx < firstVisibleRow || idx > lastVisibleRow}>
|
||||
class:hidden={idx < firstVisibleRow || idx > lastVisibleRow}
|
||||
>
|
||||
{#if idx >= firstVisibleRow && idx <= lastVisibleRow}
|
||||
{#if showEditColumn}
|
||||
<td
|
||||
class="spectrum-Table-cell spectrum-Table-cell--divider">
|
||||
class="spectrum-Table-cell spectrum-Table-cell--divider"
|
||||
>
|
||||
<div class="spectrum-Table-cell-content">
|
||||
<SelectEditRenderer
|
||||
data={row}
|
||||
|
@ -261,21 +288,25 @@
|
|||
onToggleSelection={() => toggleSelectRow(row)}
|
||||
onEdit={e => editRow(e, row)}
|
||||
{allowSelectRows}
|
||||
{allowEditRows} />
|
||||
{allowEditRows}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
{/if}
|
||||
{#each fields as field}
|
||||
<td
|
||||
class="spectrum-Table-cell"
|
||||
class:spectrum-Table-cell--divider={!!schema[field].divider}>
|
||||
class:spectrum-Table-cell--divider={!!schema[field]
|
||||
.divider}
|
||||
>
|
||||
<div class="spectrum-Table-cell-content">
|
||||
<CellRenderer
|
||||
{customRenderers}
|
||||
{row}
|
||||
schema={schema[field]}
|
||||
value={row[field]}
|
||||
on:clickrelationship>
|
||||
on:clickrelationship
|
||||
>
|
||||
<slot />
|
||||
</CellRenderer>
|
||||
</div>
|
||||
|
@ -285,14 +316,25 @@
|
|||
</tr>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="placeholder">
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeXXL"
|
||||
focusable="false">
|
||||
<use xlink:href="#spectrum-icon-18-Table" />
|
||||
</svg>
|
||||
<div>No rows found</div>
|
||||
</div>
|
||||
<tr class="placeholder-row">
|
||||
{#if showEditColumn}
|
||||
<td class="placeholder-offset" />
|
||||
{/if}
|
||||
{#each fields as field}
|
||||
<td />
|
||||
{/each}
|
||||
<div class="placeholder" class:has-fields={fields.length > 0}>
|
||||
<div class="placeholder-content">
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeXXL"
|
||||
focusable="false"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-Table" />
|
||||
</svg>
|
||||
<div>No rows found</div>
|
||||
</div>
|
||||
</div>
|
||||
</tr>
|
||||
{/if}
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -315,7 +357,7 @@
|
|||
overflow: auto;
|
||||
}
|
||||
.container.quiet {
|
||||
border: none !important;
|
||||
border: none;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
|
@ -349,7 +391,7 @@
|
|||
z-index: 2;
|
||||
background-color: var(--spectrum-alias-background-color-secondary);
|
||||
border-bottom: 1px solid
|
||||
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid)) !important;
|
||||
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
|
||||
}
|
||||
.spectrum-Table-headCell-content {
|
||||
white-space: nowrap;
|
||||
|
@ -364,7 +406,34 @@
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.placeholder-row {
|
||||
position: relative;
|
||||
height: 150px;
|
||||
}
|
||||
.placeholder-row td {
|
||||
border-top: none !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
.placeholder-offset {
|
||||
width: 1px;
|
||||
}
|
||||
.placeholder {
|
||||
top: 0;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.placeholder.has-fields {
|
||||
top: var(--header-height);
|
||||
height: calc(100% - var(--header-height));
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -375,12 +444,13 @@
|
|||
var(--spectrum-alias-text-color)
|
||||
);
|
||||
}
|
||||
.placeholder div {
|
||||
.placeholder-content div {
|
||||
margin-top: 10px;
|
||||
font-size: var(
|
||||
--spectrum-table-cell-text-size,
|
||||
var(--spectrum-alias-font-size-default)
|
||||
);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
tbody {
|
||||
|
@ -399,17 +469,17 @@
|
|||
td {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: none !important;
|
||||
border-bottom: none;
|
||||
border-top: 1px solid
|
||||
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid)) !important;
|
||||
border-radius: 0 !important;
|
||||
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
|
||||
border-radius: 0;
|
||||
}
|
||||
tr:first-child td {
|
||||
border-top: none !important;
|
||||
border-top: none;
|
||||
}
|
||||
tr:last-child td {
|
||||
border-bottom: 1px solid
|
||||
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid)) !important;
|
||||
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
|
||||
}
|
||||
td.spectrum-Table-cell--divider {
|
||||
width: 1px;
|
||||
|
|
|
@ -30,13 +30,15 @@
|
|||
on:click={onClick}
|
||||
class:is-selected={$selected.title === title}
|
||||
class="spectrum-Tabs-item"
|
||||
tabindex="0">
|
||||
tabindex="0"
|
||||
>
|
||||
{#if icon}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label="Folder">
|
||||
aria-label="Folder"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
|
|
@ -44,23 +44,22 @@
|
|||
})
|
||||
|
||||
function id() {
|
||||
return (
|
||||
"_" +
|
||||
Math.random()
|
||||
.toString(36)
|
||||
.substr(2, 9)
|
||||
)
|
||||
return "_" + Math.random().toString(36).substr(2, 9)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={container}
|
||||
class="selected-border spectrum-Tabs spectrum-Tabs--{vertical ? 'vertical' : 'horizontal'}">
|
||||
class="selected-border spectrum-Tabs spectrum-Tabs--{vertical
|
||||
? 'vertical'
|
||||
: 'horizontal'}"
|
||||
>
|
||||
<slot />
|
||||
{#if $tab.info}
|
||||
<div
|
||||
class="spectrum-Tabs-selectionIndicator indicator-transition"
|
||||
style="width: {width}; height: {height}; left: {left}; top: {top};" />
|
||||
style="width: {width}; height: {height}; left: {left}; top: {top};"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,24 +1,34 @@
|
|||
<script>
|
||||
import Avatar from '../Avatar/Avatar.svelte'
|
||||
import ClearButton from '../ClearButton/ClearButton.svelte'
|
||||
export let icon = "";
|
||||
export let avatar = "";
|
||||
export let invalid = false;
|
||||
export let disabled = false;
|
||||
export let closable = false;
|
||||
import Avatar from "../Avatar/Avatar.svelte"
|
||||
import ClearButton from "../ClearButton/ClearButton.svelte"
|
||||
export let icon = ""
|
||||
export let avatar = ""
|
||||
export let invalid = false
|
||||
export let disabled = false
|
||||
export let closable = false
|
||||
</script>
|
||||
|
||||
<div class:is-invalid={invalid} class:is-disabled={disabled} class="spectrum-Tags-item" role="listitem">
|
||||
<div
|
||||
class:is-invalid={invalid}
|
||||
class:is-disabled={disabled}
|
||||
class="spectrum-Tags-item"
|
||||
role="listitem"
|
||||
>
|
||||
{#if avatar}
|
||||
<Avatar url={avatar} />
|
||||
{/if}
|
||||
{#if icon}
|
||||
<svg class="spectrum-Icon spectrum-Icon--sizeS" focusable="false" aria-hidden="true" aria-label="Tag">
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeS"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label="Tag"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-24-{icon}" />
|
||||
</svg>
|
||||
{/if}
|
||||
<span class="spectrum-Tags-itemLabel"><slot /></span>
|
||||
{#if closable}
|
||||
<ClearButton on:click />
|
||||
<ClearButton on:click />
|
||||
{/if}
|
||||
</div>
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import "@spectrum-css/tags/dist/index-vars.css"
|
||||
import "@spectrum-css/tags/dist/index-vars.css"
|
||||
</script>
|
||||
|
||||
<div class="spectrum-Tags" role="list" aria-label="list">
|
||||
<slot />
|
||||
<slot />
|
||||
</div>
|
|
@ -1,28 +1,40 @@
|
|||
<script>
|
||||
export let selected = false;
|
||||
export let open = false;
|
||||
export let title;
|
||||
export let icon;
|
||||
export let selected = false
|
||||
export let open = false
|
||||
export let title
|
||||
export let icon
|
||||
</script>
|
||||
|
||||
<li
|
||||
class:is-selected={selected} class:is-open={open} class="spectrum-TreeView-item">
|
||||
<a on:click class="spectrum-TreeView-itemLink" href="#">
|
||||
{#if $$slots.default}
|
||||
<svg class="spectrum-Icon spectrum-UIIcon-ChevronRight100 spectrum-TreeView-itemIndicator" focusable="false" aria-hidden="true">
|
||||
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
||||
</svg>
|
||||
{/if}
|
||||
{#if icon}
|
||||
<svg class="spectrum-TreeView-itemIcon spectrum-Icon spectrum-Icon--sizeM" focusable="false" aria-hidden="true" aria-label="Layers">
|
||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||
</svg>
|
||||
{/if}
|
||||
<span class="spectrum-TreeView-itemLabel">{title}</span>
|
||||
</a>
|
||||
{#if $$slots.default}
|
||||
<ul class="spectrum-TreeView">
|
||||
<slot />
|
||||
</ul>
|
||||
{/if}
|
||||
class:is-selected={selected}
|
||||
class:is-open={open}
|
||||
class="spectrum-TreeView-item"
|
||||
>
|
||||
<a on:click class="spectrum-TreeView-itemLink" href="#">
|
||||
{#if $$slots.default}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-ChevronRight100 spectrum-TreeView-itemIndicator"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
||||
</svg>
|
||||
{/if}
|
||||
{#if icon}
|
||||
<svg
|
||||
class="spectrum-TreeView-itemIcon spectrum-Icon spectrum-Icon--sizeM"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label="Layers"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||
</svg>
|
||||
{/if}
|
||||
<span class="spectrum-TreeView-itemLabel">{title}</span>
|
||||
</a>
|
||||
{#if $$slots.default}
|
||||
<ul class="spectrum-TreeView">
|
||||
<slot />
|
||||
</ul>
|
||||
{/if}
|
||||
</li>
|
|
@ -1,11 +1,16 @@
|
|||
<script>
|
||||
import "@spectrum-css/treeview/dist/index-vars.css"
|
||||
import "@spectrum-css/treeview/dist/index-vars.css"
|
||||
|
||||
export let quiet = false
|
||||
export let standalone = true
|
||||
export let width = '250px';
|
||||
export let quiet = false
|
||||
export let standalone = true
|
||||
export let width = "250px"
|
||||
</script>
|
||||
|
||||
<ul class:spectrum-TreeView--standalone={standalone} class:spectrum-TreeView--quiet={quiet} class="spectrum-TreeView" style="width: {width}">
|
||||
<slot />
|
||||
<ul
|
||||
class:spectrum-TreeView--standalone={standalone}
|
||||
class:spectrum-TreeView--quiet={quiet}
|
||||
class="spectrum-TreeView"
|
||||
style="width: {width}"
|
||||
>
|
||||
<slot />
|
||||
</ul>
|
|
@ -1,29 +1,22 @@
|
|||
<script>
|
||||
import "@spectrum-css/typography/dist/index-vars.css"
|
||||
import "@spectrum-css/typography/dist/index-vars.css"
|
||||
|
||||
// Sizes
|
||||
export let xxxl = false;
|
||||
export let xxl = false;
|
||||
export let xl = false;
|
||||
export let l = false;
|
||||
export let m = false;
|
||||
export let s = false;
|
||||
export let xs = false;
|
||||
export let xxs = false;
|
||||
|
||||
export let serif = false;
|
||||
$: useDefault = ![xxxl, xxl, xl, l, m, s, xs, xxs].includes(true)
|
||||
export let size = "M"
|
||||
export let serif = false
|
||||
export let noPadding = false
|
||||
</script>
|
||||
|
||||
<p class="spectrum-Body"
|
||||
class:spectrum-Body--serif={serif}
|
||||
class:spectrum-Body--sizeXXXL={xxxl}
|
||||
class:spectrum-Body--sizeXXL={xxl}
|
||||
class:spectrum-Body--sizeXL={xl}
|
||||
class:spectrum-Body--sizeL={l}
|
||||
class:spectrum-Body--sizeM={m || useDefault}
|
||||
class:spectrum-Body--sizeS={s}
|
||||
class:spectrum-Body--sizeXS={xs}
|
||||
class:spectrum-Body--sizeXXS={xxs}>
|
||||
<slot />
|
||||
<p
|
||||
class:noPadding
|
||||
class="spectrum-Body spectrum-Body--size{size}"
|
||||
class:spectrum-Body--serif={serif}
|
||||
>
|
||||
<slot />
|
||||
</p>
|
||||
|
||||
<style>
|
||||
.noPadding {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,20 +1,10 @@
|
|||
<script>
|
||||
import "@spectrum-css/typography/dist/index-vars.css"
|
||||
import "@spectrum-css/typography/dist/index-vars.css"
|
||||
|
||||
// Sizes
|
||||
export let xl = false;
|
||||
export let l = false;
|
||||
export let m = false;
|
||||
export let s = false;
|
||||
export let xs = false;
|
||||
$: useDefault = ![xl, l, m, s, xs].includes(true)
|
||||
// Sizes
|
||||
export let size = "M"
|
||||
</script>
|
||||
|
||||
<code class="spectrum-Code"
|
||||
class:spectrum-Code--sizeXL={xl}
|
||||
class:spectrum-Code--sizeL={l}
|
||||
class:spectrum-Code--sizeM={m || useDefault}
|
||||
class:spectrum-Code--sizeS={s}
|
||||
class:spectrum-Code--sizeXS={xs}>
|
||||
<slot />
|
||||
<code class="spectrum-Code spectrum-Code--size{size}">
|
||||
<slot />
|
||||
</code>
|
|
@ -1,21 +1,15 @@
|
|||
<script>
|
||||
import "@spectrum-css/typography/dist/index-vars.css"
|
||||
import "@spectrum-css/typography/dist/index-vars.css"
|
||||
|
||||
// Sizes
|
||||
export let xl = false;
|
||||
export let l = false;
|
||||
export let m = false;
|
||||
export let s = false;
|
||||
// Sizes
|
||||
export let size = "M"
|
||||
|
||||
export let serif = false;
|
||||
$: useDefault = ![xl, l, m, s].includes(true)
|
||||
export let serif = false
|
||||
</script>
|
||||
|
||||
<p class="spectrum-Detail"
|
||||
class:spectrum-Detail--serif={serif}
|
||||
class:spectrum-Detail--sizeXL={xl}
|
||||
class:spectrum-Detail--sizeL={l}
|
||||
class:spectrum-Detail--sizeM={m || useDefault}
|
||||
class:spectrum-Detail--sizeS={s}>
|
||||
<slot />
|
||||
<p
|
||||
class="spectrum-Detail spectrum-Detail--size{size}"
|
||||
class:spectrum-Detail--serif={serif}
|
||||
>
|
||||
<slot />
|
||||
</p>
|
|
@ -2,27 +2,9 @@
|
|||
import "@spectrum-css/typography/dist/index-vars.css"
|
||||
|
||||
// Sizes
|
||||
export let xxxl = false
|
||||
export let xxl = false
|
||||
export let xl = false
|
||||
export let l = false
|
||||
export let m = false
|
||||
export let s = false
|
||||
export let xs = false
|
||||
export let xxs = false
|
||||
|
||||
$: useDefault = ![xxxl, xxl, xl, l, m, s, xs, xxs].includes(true)
|
||||
export let size = "M"
|
||||
</script>
|
||||
|
||||
<h1
|
||||
class="spectrum-Heading"
|
||||
class:spectrum-Heading--sizeXXXL={xxxl}
|
||||
class:spectrum-Heading--sizeXXL={xxl}
|
||||
class:spectrum-Heading--sizeXL={xl}
|
||||
class:spectrum-Heading--sizeL={l}
|
||||
class:spectrum-Heading--sizeM={m || useDefault}
|
||||
class:spectrum-Heading--sizeS={s}
|
||||
class:spectrum-Heading--sizeXS={xs}
|
||||
class:spectrum-Heading--sizeXXS={xxs}>
|
||||
<h1 class="spectrum-Heading spectrum-Heading--size{size}">
|
||||
<slot />
|
||||
</h1>
|
||||
|
|
|
@ -26,8 +26,9 @@ export { default as DetailSummary } from "./DetailSummary/DetailSummary.svelte"
|
|||
export { default as Popover } from "./Popover/Popover.svelte"
|
||||
export { default as ProgressBar } from "./ProgressBar/ProgressBar.svelte"
|
||||
export { default as ProgressCircle } from "./ProgressCircle/ProgressCircle.svelte"
|
||||
export { default as Label } from "./Styleguide/Label.svelte"
|
||||
export { default as Label } from "./Label/Label.svelte"
|
||||
export { default as Layout } from "./Layout/Layout.svelte"
|
||||
export { default as Page } from "./Layout/Page.svelte"
|
||||
export { default as Link } from "./Link/Link.svelte"
|
||||
export { default as Menu } from "./Menu/Menu.svelte"
|
||||
export { default as MenuSection } from "./Menu/Section.svelte"
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue