forked from tsai/budibase
463 changed files with 8402 additions and 5215 deletions
@ -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() |
||||
@ -1,18 +1,27 @@ |
|||||
{ |
{ |
||||
"name": "@budibase/auth", |
"name": "@budibase/auth", |
||||
"version": "0.0.1", |
"version": "0.18.6", |
||||
"description": "Authentication middlewares for budibase builder and apps", |
"description": "Authentication middlewares for budibase builder and apps", |
||||
"main": "src/index.js", |
"main": "src/index.js", |
||||
"author": "Budibase", |
"author": "Budibase", |
||||
"license": "AGPL-3.0", |
"license": "AGPL-3.0", |
||||
"dependencies": { |
"dependencies": { |
||||
|
"aws-sdk": "^2.901.0", |
||||
"bcryptjs": "^2.4.3", |
"bcryptjs": "^2.4.3", |
||||
|
"ioredis": "^4.27.1", |
||||
"jsonwebtoken": "^8.5.1", |
"jsonwebtoken": "^8.5.1", |
||||
"koa-passport": "^4.1.4", |
"koa-passport": "^4.1.4", |
||||
|
"node-fetch": "^2.6.1", |
||||
"passport-google-auth": "^1.0.2", |
"passport-google-auth": "^1.0.2", |
||||
"passport-google-oauth": "^2.0.0", |
"passport-google-oauth": "^2.0.0", |
||||
"passport-jwt": "^4.0.0", |
"passport-jwt": "^4.0.0", |
||||
"passport-local": "^1.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" |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -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 = { |
module.exports = { |
||||
JWT_SECRET: process.env.JWT_SECRET, |
JWT_SECRET: process.env.JWT_SECRET, |
||||
COUCH_DB_URL: process.env.COUCH_DB_URL, |
COUCH_DB_URL: process.env.COUCH_DB_URL, |
||||
SALT_ROUNDS: process.env.SALT_ROUNDS, |
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, |
||||
} |
} |
||||
|
|||||
@ -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] |
||||
|
} |
||||
|
} |
||||
@ -1,12 +1,53 @@ |
|||||
<script> |
<script> |
||||
import "@spectrum-css/avatar/dist/index-vars.css" |
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 url = "" |
||||
export let disabled = false |
export let disabled = false |
||||
|
export let name = "John Doe" |
||||
|
|
||||
|
function getInitials(name) { |
||||
|
let parts = name.split(" ") |
||||
|
return parts.map(name => name[0]).join("") |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<img |
{#if url} |
||||
class:is-disabled={disabled} |
<img |
||||
class="spectrum-Avatar" |
class:is-disabled={disabled} |
||||
src={url} |
class="spectrum-Avatar" |
||||
alt="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> |
||||
|
|||||
@ -1,15 +1,19 @@ |
|||||
<script> |
<script> |
||||
import "@spectrum-css/buttongroup/dist/index-vars.css" |
import "@spectrum-css/buttongroup/dist/index-vars.css" |
||||
export let vertical = false |
export let vertical = false |
||||
|
|
||||
function group(element) { |
function group(element) { |
||||
const buttons = Array.from(element.getElementsByTagName('button')) |
const buttons = Array.from(element.getElementsByTagName("button")) |
||||
buttons.forEach(button => { |
buttons.forEach(button => { |
||||
button.classList.add('spectrum-ButtonGroup-item') |
button.classList.add("spectrum-ButtonGroup-item") |
||||
}) |
}) |
||||
} |
} |
||||
</script> |
</script> |
||||
|
|
||||
<div use:group class="spectrum-ButtonGroup" class:spectrum-ButtonGroup--vertical={vertical}> |
<div |
||||
<slot /> |
use:group |
||||
</div> |
class="spectrum-ButtonGroup" |
||||
|
class:spectrum-ButtonGroup--vertical={vertical} |
||||
|
> |
||||
|
<slot /> |
||||
|
</div> |
||||
|
|||||
@ -1,14 +1,16 @@ |
|||||
<script> |
<script> |
||||
import "@spectrum-css/fieldlabel/dist/index-vars.css" |
import "@spectrum-css/fieldlabel/dist/index-vars.css" |
||||
|
|
||||
|
export let size = "M" |
||||
</script> |
</script> |
||||
|
|
||||
<label |
<label class={`spectrum-FieldLabel spectrum-FieldLabel--size${size}`}> |
||||
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeL spectrum-Form-itemLabel`}> |
|
||||
<slot /> |
<slot /> |
||||
</label> |
</label> |
||||
|
|
||||
<style> |
<style> |
||||
label { |
label { |
||||
|
padding: 0; |
||||
white-space: nowrap; |
white-space: nowrap; |
||||
} |
} |
||||
</style> |
</style> |
||||
@ -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> |
||||
@ -1,7 +1,7 @@ |
|||||
<script> |
<script> |
||||
import "@spectrum-css/menu/dist/index-vars.css" |
import "@spectrum-css/menu/dist/index-vars.css" |
||||
</script> |
</script> |
||||
|
|
||||
<ul class="spectrum-Menu" role="menu"> |
<ul class="spectrum-Menu" role="menu"> |
||||
<slot /> |
<slot /> |
||||
</ul> |
</ul> |
||||
|
|||||
@ -1,9 +1,10 @@ |
|||||
<script> |
<script> |
||||
export let heading |
export let heading |
||||
</script> |
</script> |
||||
|
|
||||
<li role="presentation"> |
<li role="presentation"> |
||||
<span class="spectrum-Menu-sectionHeading">{heading}</span> |
<span class="spectrum-Menu-sectionHeading">{heading}</span> |
||||
<ul class="spectrum-Menu" role="group"> |
<ul class="spectrum-Menu" role="group"> |
||||
<slot /> |
<slot /> |
||||
</ul> |
</ul> |
||||
</li> |
</li> |
||||
|
|||||
@ -1 +1 @@ |
|||||
<li class="spectrum-Menu-divider" role="separator"></li> |
<li class="spectrum-Menu-divider" role="separator" /> |
||||
|
|||||
@ -1,29 +1,62 @@ |
|||||
<script> |
<script> |
||||
// WIP! Does not yet work. |
import "@spectrum-css/progresscircle/dist/index-vars.css" |
||||
import "@spectrum-css/progresscircle/dist/index-vars.css" |
|
||||
import { tweened } from 'svelte/motion'; |
|
||||
import { cubicOut } from 'svelte/easing'; |
|
||||
|
|
||||
export let value = false |
export let size = "M" |
||||
export let small; |
function convertSize(size) { |
||||
export let large; |
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> |
</script> |
||||
|
|
||||
<div class:spectrum-ProgressBar--indeterminate={!value} class:spectrum-ProgressCircle--small={small} class:spectrum-ProgressCircle--large={large} class="spectrum-ProgressCircle"> |
<div |
||||
<div class="spectrum-ProgressCircle-track"></div> |
on:click |
||||
<div class="spectrum-ProgressCircle-fills"> |
class:spectrum-ProgressBar--indeterminate={!value} |
||||
<div class="spectrum-ProgressCircle-fillMask1"> |
class:spectrum-ProgressCircle--overBackground={overBackground} |
||||
<div class="spectrum-ProgressCircle-fillSubMask1"> |
class="spectrum-ProgressCircle spectrum-ProgressCircle--{convertSize(size)}" |
||||
<div class="spectrum-ProgressCircle-fill"></div> |
> |
||||
</div> |
<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> |
||||
<div class="spectrum-ProgressCircle-fillMask2"> |
</div> |
||||
<div class="spectrum-ProgressCircle-fillSubMask2"> |
<div class="spectrum-ProgressCircle-fillMask2"> |
||||
<div class="spectrum-ProgressCircle-fill"></div> |
<div class="spectrum-ProgressCircle-fillSubMask2" style={subMask2Style}> |
||||
</div> |
<div class="spectrum-ProgressCircle-fill" /> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
</div> |
||||
|
|||||
@ -1,30 +1,44 @@ |
|||||
<script> |
<script> |
||||
import { getContext } from 'svelte' |
import { getContext } from "svelte" |
||||
const multilevel = getContext('sidenav-type'); |
const multilevel = getContext("sidenav-type") |
||||
export let href = ""; |
export let href = "" |
||||
export let external = false; |
export let external = false |
||||
export let heading = "" |
export let heading = "" |
||||
export let icon = ""; |
export let icon = "" |
||||
export let selected = false; |
export let selected = false |
||||
export let disabled = false; |
export let disabled = false |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<li class="spectrum-SideNav-item" class:is-selected={selected} class:is-disabled={disabled}> |
<li |
||||
{#if heading} |
class="spectrum-SideNav-item" |
||||
<h2 class="spectrum-SideNav-heading" id="nav-heading-{heading}">{heading}</h2> |
class:is-selected={selected} |
||||
{/if} |
class:is-disabled={disabled} |
||||
<a target={external ? '_blank' : '_self'} {href} class="spectrum-SideNav-itemLink" aria-current="page"> |
> |
||||
{#if icon} |
{#if heading} |
||||
<svg class="spectrum-Icon spectrum-Icon--sizeM spectrum-SideNav-itemIcon" focusable="false" aria-hidden="true"> |
<h2 class="spectrum-SideNav-heading" id="nav-heading-{heading}"> |
||||
<use xlink:href="#spectrum-icon-18-{icon}" /> |
{heading} |
||||
</svg> |
</h2> |
||||
{/if} |
{/if} |
||||
<slot /> |
<a |
||||
</a> |
target={external ? "_blank" : ""} |
||||
{#if multilevel && $$slots.subnav} |
{href} |
||||
<ul class="spectrum-SideNav"> |
class="spectrum-SideNav-itemLink" |
||||
<slot name="subnav" /> |
aria-current="page" |
||||
</ul> |
> |
||||
|
{#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} |
{/if} |
||||
</li> |
<slot /> |
||||
|
</a> |
||||
|
{#if multilevel && $$slots.subnav} |
||||
|
<ul class="spectrum-SideNav"> |
||||
|
<slot name="subnav" /> |
||||
|
</ul> |
||||
|
{/if} |
||||
|
</li> |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
<script> |
<script> |
||||
import { setContext } from 'svelte' |
import { setContext } from "svelte" |
||||
import "@spectrum-css/sidenav/dist/index-vars.css" |
import "@spectrum-css/sidenav/dist/index-vars.css" |
||||
export let multilevel = false; |
export let multilevel = false |
||||
setContext('sidenav-type', multilevel) |
setContext("sidenav-type", multilevel) |
||||
</script> |
</script> |
||||
|
|
||||
<nav> |
<nav> |
||||
<ul class="spectrum-SideNav" class:spectrum-SideNav--multiLevel={multilevel}> |
<ul class="spectrum-SideNav" class:spectrum-SideNav--multiLevel={multilevel}> |
||||
<slot /> |
<slot /> |
||||
</ul> |
</ul> |
||||
</nav> |
</nav> |
||||
|
|||||
@ -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> |
|
||||
|
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> |
|
||||
@ -1,24 +1,34 @@ |
|||||
<script> |
<script> |
||||
import Avatar from '../Avatar/Avatar.svelte' |
import Avatar from "../Avatar/Avatar.svelte" |
||||
import ClearButton from '../ClearButton/ClearButton.svelte' |
import ClearButton from "../ClearButton/ClearButton.svelte" |
||||
export let icon = ""; |
export let icon = "" |
||||
export let avatar = ""; |
export let avatar = "" |
||||
export let invalid = false; |
export let invalid = false |
||||
export let disabled = false; |
export let disabled = false |
||||
export let closable = false; |
export let closable = false |
||||
</script> |
</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} |
{#if avatar} |
||||
<Avatar url={avatar} /> |
<Avatar url={avatar} /> |
||||
{/if} |
{/if} |
||||
{#if icon} |
{#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}" /> |
<use xlink:href="#spectrum-icon-24-{icon}" /> |
||||
</svg> |
</svg> |
||||
{/if} |
{/if} |
||||
<span class="spectrum-Tags-itemLabel"><slot /></span> |
<span class="spectrum-Tags-itemLabel"><slot /></span> |
||||
{#if closable} |
{#if closable} |
||||
<ClearButton on:click /> |
<ClearButton on:click /> |
||||
{/if} |
{/if} |
||||
</div> |
</div> |
||||
|
|||||
@ -1,7 +1,7 @@ |
|||||
<script> |
<script> |
||||
import "@spectrum-css/tags/dist/index-vars.css" |
import "@spectrum-css/tags/dist/index-vars.css" |
||||
</script> |
</script> |
||||
|
|
||||
<div class="spectrum-Tags" role="list" aria-label="list"> |
<div class="spectrum-Tags" role="list" aria-label="list"> |
||||
<slot /> |
<slot /> |
||||
</div> |
</div> |
||||
|
|||||
@ -1,28 +1,40 @@ |
|||||
<script> |
<script> |
||||
export let selected = false; |
export let selected = false |
||||
export let open = false; |
export let open = false |
||||
export let title; |
export let title |
||||
export let icon; |
export let icon |
||||
</script> |
</script> |
||||
|
|
||||
<li |
<li |
||||
class:is-selected={selected} class:is-open={open} class="spectrum-TreeView-item"> |
class:is-selected={selected} |
||||
<a on:click class="spectrum-TreeView-itemLink" href="#"> |
class:is-open={open} |
||||
{#if $$slots.default} |
class="spectrum-TreeView-item" |
||||
<svg class="spectrum-Icon spectrum-UIIcon-ChevronRight100 spectrum-TreeView-itemIndicator" focusable="false" aria-hidden="true"> |
> |
||||
<use xlink:href="#spectrum-css-icon-Chevron100" /> |
<a on:click class="spectrum-TreeView-itemLink" href="#"> |
||||
</svg> |
{#if $$slots.default} |
||||
{/if} |
<svg |
||||
{#if icon} |
class="spectrum-Icon spectrum-UIIcon-ChevronRight100 spectrum-TreeView-itemIndicator" |
||||
<svg class="spectrum-TreeView-itemIcon spectrum-Icon spectrum-Icon--sizeM" focusable="false" aria-hidden="true" aria-label="Layers"> |
focusable="false" |
||||
<use xlink:href="#spectrum-icon-18-{icon}" /> |
aria-hidden="true" |
||||
</svg> |
> |
||||
{/if} |
<use xlink:href="#spectrum-css-icon-Chevron100" /> |
||||
<span class="spectrum-TreeView-itemLabel">{title}</span> |
</svg> |
||||
</a> |
{/if} |
||||
{#if $$slots.default} |
{#if icon} |
||||
<ul class="spectrum-TreeView"> |
<svg |
||||
<slot /> |
class="spectrum-TreeView-itemIcon spectrum-Icon spectrum-Icon--sizeM" |
||||
</ul> |
focusable="false" |
||||
{/if} |
aria-hidden="true" |
||||
</li> |
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> |
<script> |
||||
import "@spectrum-css/treeview/dist/index-vars.css" |
import "@spectrum-css/treeview/dist/index-vars.css" |
||||
|
|
||||
export let quiet = false |
export let quiet = false |
||||
export let standalone = true |
export let standalone = true |
||||
export let width = '250px'; |
export let width = "250px" |
||||
</script> |
</script> |
||||
|
|
||||
<ul class:spectrum-TreeView--standalone={standalone} class:spectrum-TreeView--quiet={quiet} class="spectrum-TreeView" style="width: {width}"> |
<ul |
||||
<slot /> |
class:spectrum-TreeView--standalone={standalone} |
||||
</ul> |
class:spectrum-TreeView--quiet={quiet} |
||||
|
class="spectrum-TreeView" |
||||
|
style="width: {width}" |
||||
|
> |
||||
|
<slot /> |
||||
|
</ul> |
||||
|
|||||
@ -1,29 +1,22 @@ |
|||||
<script> |
<script> |
||||
import "@spectrum-css/typography/dist/index-vars.css" |
import "@spectrum-css/typography/dist/index-vars.css" |
||||
|
|
||||
// Sizes |
export let size = "M" |
||||
export let xxxl = false; |
export let serif = false |
||||
export let xxl = false; |
export let noPadding = 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) |
|
||||
</script> |
</script> |
||||
|
|
||||
<p class="spectrum-Body" |
<p |
||||
class:spectrum-Body--serif={serif} |
class:noPadding |
||||
class:spectrum-Body--sizeXXXL={xxxl} |
class="spectrum-Body spectrum-Body--size{size}" |
||||
class:spectrum-Body--sizeXXL={xxl} |
class:spectrum-Body--serif={serif} |
||||
class:spectrum-Body--sizeXL={xl} |
> |
||||
class:spectrum-Body--sizeL={l} |
<slot /> |
||||
class:spectrum-Body--sizeM={m || useDefault} |
</p> |
||||
class:spectrum-Body--sizeS={s} |
|
||||
class:spectrum-Body--sizeXS={xs} |
<style> |
||||
class:spectrum-Body--sizeXXS={xxs}> |
.noPadding { |
||||
<slot /> |
padding: 0; |
||||
</p> |
margin: 0; |
||||
|
} |
||||
|
</style> |
||||
|
|||||
@ -1,20 +1,10 @@ |
|||||
<script> |
<script> |
||||
import "@spectrum-css/typography/dist/index-vars.css" |
import "@spectrum-css/typography/dist/index-vars.css" |
||||
|
|
||||
// Sizes |
// Sizes |
||||
export let xl = false; |
export let size = "M" |
||||
export let l = false; |
|
||||
export let m = false; |
|
||||
export let s = false; |
|
||||
export let xs = false; |
|
||||
$: useDefault = ![xl, l, m, s, xs].includes(true) |
|
||||
</script> |
</script> |
||||
|
|
||||
<code class="spectrum-Code" |
<code class="spectrum-Code spectrum-Code--size{size}"> |
||||
class:spectrum-Code--sizeXL={xl} |
<slot /> |
||||
class:spectrum-Code--sizeL={l} |
</code> |
||||
class:spectrum-Code--sizeM={m || useDefault} |
|
||||
class:spectrum-Code--sizeS={s} |
|
||||
class:spectrum-Code--sizeXS={xs}> |
|
||||
<slot /> |
|
||||
</code> |
|
||||
|
|||||
@ -1,21 +1,15 @@ |
|||||
<script> |
<script> |
||||
import "@spectrum-css/typography/dist/index-vars.css" |
import "@spectrum-css/typography/dist/index-vars.css" |
||||
|
|
||||
// Sizes |
// Sizes |
||||
export let xl = false; |
export let size = "M" |
||||
export let l = false; |
|
||||
export let m = false; |
|
||||
export let s = false; |
|
||||
|
|
||||
export let serif = false; |
export let serif = false |
||||
$: useDefault = ![xl, l, m, s].includes(true) |
|
||||
</script> |
</script> |
||||
|
|
||||
<p class="spectrum-Detail" |
<p |
||||
class:spectrum-Detail--serif={serif} |
class="spectrum-Detail spectrum-Detail--size{size}" |
||||
class:spectrum-Detail--sizeXL={xl} |
class:spectrum-Detail--serif={serif} |
||||
class:spectrum-Detail--sizeL={l} |
> |
||||
class:spectrum-Detail--sizeM={m || useDefault} |
<slot /> |
||||
class:spectrum-Detail--sizeS={s}> |
</p> |
||||
<slot /> |
|
||||
</p> |
|
||||
|
|||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue