mirror of https://github.com/Budibase/budibase.git
committed by
GitHub
26 changed files with 8776 additions and 328 deletions
File diff suppressed because it is too large
@ -0,0 +1,66 @@ |
|||||
|
<script> |
||||
|
export let item |
||||
|
</script> |
||||
|
|
||||
|
<div class="item-item" on:click> |
||||
|
<div class="item-icon"> |
||||
|
<i class={item.icon} /> |
||||
|
</div> |
||||
|
<div class="item-text"> |
||||
|
<div class="item-name">{item.name}</div> |
||||
|
<div class="item-description"> |
||||
|
<p>{item.description}</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.item-item { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
padding: 10px 0px 8px 10px; |
||||
|
align-items: center; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.item-item:hover { |
||||
|
background: #fbfbfb; |
||||
|
border-radius: 5px; |
||||
|
} |
||||
|
|
||||
|
.item-icon { |
||||
|
flex: 0 0 40px; |
||||
|
background: #f1f4fc; |
||||
|
height: 40px; |
||||
|
border-radius: 5px; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.item-text { |
||||
|
display: flex; |
||||
|
padding-left: 16px; |
||||
|
padding-top: 8px; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.item-name { |
||||
|
font-size: 14px; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.item-description { |
||||
|
font-size: 12px; |
||||
|
color: #808192; |
||||
|
} |
||||
|
|
||||
|
p { |
||||
|
line-height: 15px; |
||||
|
} |
||||
|
|
||||
|
i { |
||||
|
font-size: 24px; |
||||
|
color: #808192; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,44 @@ |
|||||
|
<script> |
||||
|
import { createEventDispatcher } from "svelte" |
||||
|
const dispatch = createEventDispatcher() |
||||
|
|
||||
|
import Item from "./Item.svelte" |
||||
|
import { store } from "builderStore" |
||||
|
export let list |
||||
|
|
||||
|
let category = list |
||||
|
|
||||
|
const handleClick = item => { |
||||
|
if (item.children && item.children.length > 0) { |
||||
|
list = item |
||||
|
} else { |
||||
|
dispatch("selectItem", item) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const goBack = () => { |
||||
|
list = category |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
{#if !list.isCategory} |
||||
|
<button class="back-button" on:click={() => (list = category)}>Back</button> |
||||
|
{/if} |
||||
|
|
||||
|
{#each list.children as item} |
||||
|
<Item {item} on:click={() => handleClick(item)} /> |
||||
|
{/each} |
||||
|
|
||||
|
<style> |
||||
|
.back-button { |
||||
|
font-size: 16px; |
||||
|
width: 100%; |
||||
|
text-align: center; |
||||
|
height: 40px; |
||||
|
border-radius: 3px; |
||||
|
border: solid 1px #e8e8ef; |
||||
|
background: white; |
||||
|
margin-bottom: 20px; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,335 @@ |
|||||
|
<script> |
||||
|
import { splitName } from "./pagesParsing/splitRootComponentName.js" |
||||
|
import { store } from "builderStore" |
||||
|
import { |
||||
|
find, |
||||
|
sortBy, |
||||
|
groupBy, |
||||
|
values, |
||||
|
filter, |
||||
|
map, |
||||
|
uniqBy, |
||||
|
flatten, |
||||
|
} from "lodash/fp" |
||||
|
import { ImageIcon, InputIcon, LayoutIcon } from "components/common/Icons/" |
||||
|
import Select from "components/common/Select.svelte" |
||||
|
import Button from "components/common/PlusButton.svelte" |
||||
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte" |
||||
|
import { |
||||
|
getRecordNodes, |
||||
|
getIndexNodes, |
||||
|
getIndexSchema, |
||||
|
pipe, |
||||
|
} from "components/common/core" |
||||
|
|
||||
|
export let toggleTab |
||||
|
|
||||
|
let componentLibraries = [] |
||||
|
let current_view = "text" |
||||
|
let selectedComponent = null |
||||
|
let selectedLib |
||||
|
let selectTemplateDialog |
||||
|
let templateInstances = [] |
||||
|
let selectedTemplateInstance |
||||
|
|
||||
|
//Info: Components seem to be generated from individual templates. Will this be the same going forward |
||||
|
$: templatesByComponent = groupBy(t => t.component)($store.templates) |
||||
|
$: hierarchy = $store.hierarchy |
||||
|
$: libraryModules = $store.libraries |
||||
|
$: standaloneTemplates = pipe( |
||||
|
templatesByComponent, |
||||
|
[ |
||||
|
values, |
||||
|
flatten, |
||||
|
filter(t => !$store.components.some(c => c.name === t.component)), |
||||
|
map(t => ({ name: splitName(t.component).componentName, template: t })), |
||||
|
uniqBy(t => t.name), |
||||
|
] |
||||
|
) |
||||
|
|
||||
|
const addRootComponent = (component, allComponents) => { |
||||
|
const { libName } = splitName(component.name) |
||||
|
let group = find(r => r.libName === libName)(allComponents) |
||||
|
|
||||
|
if (!group) { |
||||
|
group = { |
||||
|
libName, |
||||
|
components: [], |
||||
|
} |
||||
|
|
||||
|
allComponents.push(group) |
||||
|
} |
||||
|
|
||||
|
group.components.push(component) |
||||
|
} |
||||
|
|
||||
|
const onComponentChosen = component => { |
||||
|
if (component.template) { |
||||
|
onTemplateChosen(component.template) |
||||
|
} else { |
||||
|
store.addChildComponent(component.name) |
||||
|
toggleTab() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//Info: Called from menu beside components with presets and templates |
||||
|
const onTemplateChosen = template => { |
||||
|
selectedComponent = null |
||||
|
const { componentName, libName } = splitName(template.name) |
||||
|
//Info: how will DB changes effect this? |
||||
|
const templateOptions = { |
||||
|
records: getRecordNodes(hierarchy), |
||||
|
indexes: getIndexNodes(hierarchy), |
||||
|
helpers: { |
||||
|
indexSchema: getIndexSchema(hierarchy), |
||||
|
}, |
||||
|
} |
||||
|
//Info: go off and get template instances by library and component name |
||||
|
//libraryModules and hierarchies (used above) come from builderStore |
||||
|
templateInstances = libraryModules[libName][componentName](templateOptions) |
||||
|
if (!templateInstances || templateInstances.length === 0) return |
||||
|
selectedTemplateInstance = templateInstances[0].name |
||||
|
selectTemplateDialog.show() |
||||
|
} |
||||
|
|
||||
|
const onTemplateInstanceChosen = () => { |
||||
|
selectedComponent = null |
||||
|
const instance = templateInstances.find( |
||||
|
i => i.name === selectedTemplateInstance |
||||
|
) |
||||
|
debugger |
||||
|
store.addTemplatedComponent(instance.props) |
||||
|
toggleTab() |
||||
|
} |
||||
|
|
||||
|
function generate_components_list(components) { |
||||
|
debugger |
||||
|
return ($store.currentFrontEndType === "page" |
||||
|
? $store.builtins.concat(components) |
||||
|
: components |
||||
|
).concat(standaloneTemplates) |
||||
|
} |
||||
|
|
||||
|
$: { |
||||
|
const newComponentLibraries = [] |
||||
|
|
||||
|
for (let comp of sortBy(["name"])($store.components)) { |
||||
|
addRootComponent(comp, newComponentLibraries) |
||||
|
} |
||||
|
|
||||
|
componentLibraries = newComponentLibraries |
||||
|
if (!selectedLib) selectedLib = newComponentLibraries[0].libName |
||||
|
} |
||||
|
|
||||
|
$: componentLibrary = componentLibraries.find(l => l.libName === selectedLib) |
||||
|
</script> |
||||
|
|
||||
|
<div class="root"> |
||||
|
<Select on:change={e => (selectedLib = e.target.value)}> |
||||
|
{#each componentLibraries as lib} |
||||
|
<option value={lib.libName}>{lib.libName}</option> |
||||
|
{/each} |
||||
|
</Select> |
||||
|
|
||||
|
<div class="library-container"> |
||||
|
<!-- <ul> |
||||
|
<li> |
||||
|
<button |
||||
|
class:selected={current_view === 'text'} |
||||
|
on:click={() => (current_view = 'text')}> |
||||
|
<InputIcon /> |
||||
|
</button> |
||||
|
</li> |
||||
|
<li> |
||||
|
<button |
||||
|
class:selected={current_view === 'layout'} |
||||
|
on:click={() => (current_view = 'layout')}> |
||||
|
<LayoutIcon /> |
||||
|
</button> |
||||
|
</li> |
||||
|
<li> |
||||
|
<button |
||||
|
class:selected={current_view === 'media'} |
||||
|
on:click={() => (current_view = 'media')}> |
||||
|
<ImageIcon /> |
||||
|
</button> |
||||
|
</li> |
||||
|
</ul> --> |
||||
|
|
||||
|
{#if componentLibrary} |
||||
|
{#each generate_components_list(componentLibrary.components) as component} |
||||
|
<div class="component-container"> |
||||
|
<div class="component" on:click={() => onComponentChosen(component)}> |
||||
|
<div class="name">{splitName(component.name).componentName}</div> |
||||
|
{#if (component.presets || templatesByComponent[component.name]) && component.name === selectedComponent} |
||||
|
<ul class="preset-menu"> |
||||
|
{#if component.presets} |
||||
|
<span>{splitName(component.name).componentName} Presets</span> |
||||
|
{#each Object.keys(component.presets) as preset} |
||||
|
<li |
||||
|
on:click|stopPropagation={() => onComponentChosen(component, preset)}> |
||||
|
{preset} |
||||
|
</li> |
||||
|
{/each} |
||||
|
{/if} |
||||
|
{#if templatesByComponent[component.name]} |
||||
|
<span> |
||||
|
{splitName(component.name).componentName} Templates |
||||
|
</span> |
||||
|
{#each templatesByComponent[component.name] as template} |
||||
|
<li |
||||
|
on:click|stopPropagation={() => onTemplateChosen(template)}> |
||||
|
{template.description} |
||||
|
</li> |
||||
|
{/each} |
||||
|
{/if} |
||||
|
</ul> |
||||
|
{/if} |
||||
|
</div> |
||||
|
{#if component.presets || templatesByComponent[component.name]} |
||||
|
<Button |
||||
|
on:click={() => { |
||||
|
selectedComponent = selectedComponent ? null : component.name |
||||
|
}}> |
||||
|
<span |
||||
|
class="open-presets" |
||||
|
class:open={selectedComponent === component.name}> |
||||
|
... |
||||
|
</span> |
||||
|
</Button> |
||||
|
{/if} |
||||
|
</div> |
||||
|
{/each} |
||||
|
{/if} |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
<ConfirmDialog |
||||
|
bind:this={selectTemplateDialog} |
||||
|
title="Choose Template" |
||||
|
onCancel={() => (selectedComponent = null)} |
||||
|
onOk={onTemplateInstanceChosen}> |
||||
|
{#each templateInstances.map(i => i.name) as instance} |
||||
|
<div class="uk-margin uk-grid-small uk-child-width-auto uk-grid"> |
||||
|
<label> |
||||
|
<input |
||||
|
class="uk-radio" |
||||
|
type="radio" |
||||
|
bind:group={selectedTemplateInstance} |
||||
|
value={instance} /> |
||||
|
<span class="template-instance-label">{instance}</span> |
||||
|
</label> |
||||
|
</div> |
||||
|
{/each} |
||||
|
</ConfirmDialog> |
||||
|
|
||||
|
<style> |
||||
|
.root { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.library-container { |
||||
|
padding: 0 0 10px 0; |
||||
|
flex: 1 1 auto; |
||||
|
min-height: 0px; |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
|
||||
|
.component-container { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.component { |
||||
|
position: relative; |
||||
|
padding: 0 15px; |
||||
|
cursor: pointer; |
||||
|
border: 1px solid #d8d8d8; |
||||
|
border-radius: 2px; |
||||
|
margin: 5px 0; |
||||
|
height: 40px; |
||||
|
box-sizing: border-box; |
||||
|
color: #000333; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
flex: 1; |
||||
|
margin-right: 5px; |
||||
|
} |
||||
|
|
||||
|
.component:hover { |
||||
|
background-color: var(--lightslate); |
||||
|
} |
||||
|
|
||||
|
.component > .name { |
||||
|
color: #000333; |
||||
|
display: inline-block; |
||||
|
font-size: 13px; |
||||
|
opacity: 0.8; |
||||
|
} |
||||
|
|
||||
|
ul { |
||||
|
list-style: none; |
||||
|
display: flex; |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
.preset-menu { |
||||
|
flex-direction: column; |
||||
|
position: absolute; |
||||
|
top: 25px; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1; |
||||
|
background: #fafafa; |
||||
|
padding: 10px; |
||||
|
border-radius: 2px; |
||||
|
color: var(--secondary80); |
||||
|
} |
||||
|
|
||||
|
.preset-menu > span { |
||||
|
font-size: 13px; |
||||
|
text-transform: uppercase; |
||||
|
margin-top: 5px; |
||||
|
} |
||||
|
|
||||
|
.preset-menu li { |
||||
|
font-size: 14px; |
||||
|
margin-top: 13px; |
||||
|
} |
||||
|
|
||||
|
.preset-menu li:hover { |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
li { |
||||
|
margin-right: 20px; |
||||
|
background: none; |
||||
|
border-radius: 5px; |
||||
|
} |
||||
|
|
||||
|
/* li button { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
background: none; |
||||
|
border: none; |
||||
|
border-radius: 5px; |
||||
|
padding: 13px; |
||||
|
outline: none; |
||||
|
cursor: pointer; |
||||
|
} */ |
||||
|
|
||||
|
/* .selected { |
||||
|
color: var(--button-text); |
||||
|
background: var(--background-button) !important; |
||||
|
} */ |
||||
|
|
||||
|
.open { |
||||
|
color: rgba(0, 85, 255, 1); |
||||
|
} |
||||
|
|
||||
|
.template-instance-label { |
||||
|
margin-left: 20px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,162 @@ |
|||||
|
export default { |
||||
|
categories: [ |
||||
|
{ |
||||
|
name: 'Basic', |
||||
|
isCategory: true, |
||||
|
children: [ |
||||
|
{ |
||||
|
_component: "@budibase/standard-components/container", |
||||
|
name: 'Container', |
||||
|
description: 'This component contains things within itself', |
||||
|
icon: 'ri-layout-row-fill', |
||||
|
commonProps: {}, |
||||
|
children: [] |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Text', |
||||
|
description: 'This is a simple text component', |
||||
|
icon: 'ri-t-box-fill', |
||||
|
commonProps: {}, |
||||
|
children: [ |
||||
|
{ |
||||
|
_component: '@budibase/standard-components/heading', |
||||
|
name: 'Headline', |
||||
|
description: "A component for displaying heading text", |
||||
|
icon: "ri-heading", |
||||
|
props: { |
||||
|
type: { |
||||
|
type: "options", |
||||
|
options: ["h1", "h2", "h3", "h4", "h5", "h6"], |
||||
|
default: "h1", |
||||
|
}, |
||||
|
text: "string", |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
_component: '@budibase/standard-components/text', |
||||
|
name: 'Paragraph', |
||||
|
description: "A component for displaying paragraph text.", |
||||
|
icon: 'ri-paragraph', |
||||
|
props: {} |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Input', |
||||
|
description: "These components handle user input.", |
||||
|
icon: 'ri-edit-box-line', |
||||
|
commonProps: {}, |
||||
|
children: [ |
||||
|
{ |
||||
|
_component: "@budibase/standard-components/textfield", |
||||
|
name: "Textfield", |
||||
|
description: "A textfield component that allows the user to input text.", |
||||
|
icon: 'ri-edit-box-line', |
||||
|
props: {} |
||||
|
}, |
||||
|
{ |
||||
|
_component: "@budibase/standard-components/checkbox", |
||||
|
name: "Checkbox", |
||||
|
description: "A selectable checkbox component", |
||||
|
icon: 'ri-checkbox-line', |
||||
|
props: {} |
||||
|
}, |
||||
|
{ |
||||
|
_component: "@budibase/standard-components/radiobutton", |
||||
|
name: "Radiobutton", |
||||
|
description: "A selectable radiobutton component", |
||||
|
icon: 'ri-radio-button-line', |
||||
|
props: {} |
||||
|
}, |
||||
|
{ |
||||
|
_component: "@budibase/standard-components/select", |
||||
|
name: "Select", |
||||
|
description: "A select component for choosing from different options", |
||||
|
icon: 'ri-file-list-line', |
||||
|
props: {} |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
_component: "@budibase/standard-components/button", |
||||
|
name: 'Button', |
||||
|
description: 'A basic html button that is ready for styling', |
||||
|
icon: 'ri-radio-button-fill', |
||||
|
commonProps: {}, |
||||
|
children: [] |
||||
|
}, |
||||
|
{ |
||||
|
_component: "@budibase/standard-components/icon", |
||||
|
name: 'Icon', |
||||
|
description: 'A basic component for displaying icons', |
||||
|
icon: 'ri-sun-fill', |
||||
|
commonProps: {}, |
||||
|
children: [] |
||||
|
}, |
||||
|
{ |
||||
|
_component: "@budibase/standard-components/link", |
||||
|
name: 'Link', |
||||
|
description: 'A basic link component for internal and external links', |
||||
|
icon: 'ri-link', |
||||
|
commonProps: {}, |
||||
|
children: [] |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Blocks', |
||||
|
isCategory: true, |
||||
|
children: [ |
||||
|
{ |
||||
|
_component: "@budibase/materialdesign-components/BasicCard", |
||||
|
name: 'Card', |
||||
|
description: 'A basic card component that can contain content and actions.', |
||||
|
icon: 'ri-layout-bottom-line', |
||||
|
commonProps: {}, |
||||
|
children: [] |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Login', |
||||
|
description: 'A component that automatically generates a login screen for your app.', |
||||
|
icon: 'ri-login-box-fill', |
||||
|
commonProps: {}, |
||||
|
children: [] |
||||
|
}, |
||||
|
{ |
||||
|
name: "Navigation Bar", |
||||
|
_component: "@budibase/standard-components/Navigation", |
||||
|
description: "A component for handling the navigation within your app.", |
||||
|
icon: "ri-navigation-fill", |
||||
|
commonProps: {}, |
||||
|
children: [] |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Data', |
||||
|
isCategory: true, |
||||
|
children: [ |
||||
|
{ |
||||
|
name: 'Table', |
||||
|
description: 'A component that generates a table from your data.', |
||||
|
icon: 'ri-archive-drawer-fill', |
||||
|
commonProps: {}, |
||||
|
children: [] |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Form', |
||||
|
description: 'A component that generates a form from your data.', |
||||
|
icon: 'ri-file-edit-fill', |
||||
|
commonProps: {}, |
||||
|
component: "@budibase/materialdesign-components/Form", |
||||
|
template: { |
||||
|
component: "@budibase/materialdesign-components/Form", |
||||
|
description: "Form for saving a record", |
||||
|
name: "@budibase/materialdesign-components/recordForm", |
||||
|
}, |
||||
|
children: [] |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
{ |
||||
|
"categories": [ |
||||
|
{ |
||||
|
"name": "Basic", |
||||
|
"components": [ |
||||
|
{ |
||||
|
"component": "Container", |
||||
|
"description": "This component contains things within itself", |
||||
|
"icon": "ri-layout-row-fill", |
||||
|
"commonProps": {}, |
||||
|
"type": [] |
||||
|
}, |
||||
|
{ |
||||
|
"component": "Text", |
||||
|
"description": "This is a simple text component", |
||||
|
"icon": "ri-t-box-fill", |
||||
|
"commonProps": { |
||||
|
}, |
||||
|
"type": [ |
||||
|
{ |
||||
|
"_component": "@budibase/standard-components/header", |
||||
|
"name": "Headline", |
||||
|
"icon": "headline", |
||||
|
"props": { |
||||
|
"type": { |
||||
|
"type": "options", |
||||
|
"options": [ |
||||
|
"h1", |
||||
|
"h2" |
||||
|
], |
||||
|
"default": "h1" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"_component": "@budibase/standard-components/text", |
||||
|
"name": "Paragraph", |
||||
|
"icon": "paragraph", |
||||
|
"props": { |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
"component": "Button", |
||||
|
"description": "A basic html button that is ready for styling", |
||||
|
"icon": "ri-radio-button-fill", |
||||
|
"commonProps": {}, |
||||
|
"type": [] |
||||
|
}, |
||||
|
{ |
||||
|
"component": "Icon", |
||||
|
"description": "A basic component for displaying icons", |
||||
|
"icon": "ri-sun-fill", |
||||
|
"commonProps": {}, |
||||
|
"type": [] |
||||
|
}, |
||||
|
{ |
||||
|
"component": "Avatar", |
||||
|
"description": "A basic component for rendering an avatar", |
||||
|
"icon": "ri-user-smile-fill", |
||||
|
"commonProps": {}, |
||||
|
"type": [] |
||||
|
}, |
||||
|
{ |
||||
|
"component": "Link", |
||||
|
"description": "A basic link component for internal and external links", |
||||
|
"icon": "ri-link", |
||||
|
"commonProps": {}, |
||||
|
"type": [] |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
<script> |
||||
|
export let heading = "" |
||||
|
export let subheading = "" |
||||
|
export let content = "" |
||||
|
export let imageUrl = "" |
||||
|
export let button1Text = "" |
||||
|
export let button2Text = "" |
||||
|
export let cardClick = () => {} |
||||
|
export let button1Click = () => {} |
||||
|
export let button2Click = () => {} |
||||
|
|
||||
|
$: showImage = !!imageUrl |
||||
|
$: showButton1 = !!button1Text |
||||
|
$: showButton2 = !!button2Text |
||||
|
$: showButtons = !!showButton1 && !!showButton2 |
||||
|
</script> |
||||
|
|
||||
|
<div class="mdc-card" on:click={cardClick}> |
||||
|
<div class="mdc-card__primary-action demo-card__primary-action" tabindex="0"> |
||||
|
{#if showImage} |
||||
|
<div |
||||
|
class="mdc-card__media mdc-card__media--16-9 demo-card__media" |
||||
|
style="background-image: url("{imageUrl}");" /> |
||||
|
{/if} |
||||
|
<div class="pad"> |
||||
|
<div class="demo-card__primary"> |
||||
|
<h2 class="demo-card__title mdc-typography mdc-typography--headline6"> |
||||
|
{heading} |
||||
|
</h2> |
||||
|
<h3 |
||||
|
class="demo-card__subtitle mdc-typography mdc-typography--subtitle2"> |
||||
|
{subheading} |
||||
|
</h3> |
||||
|
</div> |
||||
|
<div class="demo-card__secondary mdc-typography mdc-typography--body2"> |
||||
|
{content} |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
{#if showButtons} |
||||
|
<div class="mdc-card__actions"> |
||||
|
<div class="mdc-card__action-buttons"> |
||||
|
{#if showButton1} |
||||
|
<button class="mdc-button mdc-card__action mdc-card__action--button"> |
||||
|
<span class="mdc-button__ripple" on:click={button1Click} /> |
||||
|
{button1Text} |
||||
|
</button> |
||||
|
{/if} |
||||
|
{#if showButton2} |
||||
|
<button |
||||
|
class="mdc-button mdc-card__action mdc-card__action--button" |
||||
|
on:click={button2Click}> |
||||
|
<span class="mdc-button__ripple" /> |
||||
|
{button2Text} |
||||
|
</button> |
||||
|
{/if} |
||||
|
</div> |
||||
|
</div> |
||||
|
{/if} |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.pad { |
||||
|
padding: 10px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,11 @@ |
|||||
|
<script> |
||||
|
import Input from "./Input.svelte" |
||||
|
export let _bb |
||||
|
|
||||
|
export let label = "" |
||||
|
export let checked = false |
||||
|
export let value = "" |
||||
|
export let onchange = () => {} |
||||
|
</script> |
||||
|
|
||||
|
<Input type="checkbox" {_bb} {checked} {label} {value} {onchange} /> |
||||
@ -0,0 +1,9 @@ |
|||||
|
<script> |
||||
|
export let icon = "" |
||||
|
export let fontSize = "1em" |
||||
|
export let _bb |
||||
|
|
||||
|
$: style = { fontSize } |
||||
|
</script> |
||||
|
|
||||
|
<i class={icon} {style} /> |
||||
@ -0,0 +1,75 @@ |
|||||
|
<script> |
||||
|
import { cssVars, createClasses } from "./cssVars" |
||||
|
|
||||
|
export let className = "" |
||||
|
export let onLoad |
||||
|
export let backgroundColor |
||||
|
export let color |
||||
|
export let borderWidth |
||||
|
export let borderColor |
||||
|
export let borderStyle |
||||
|
export let logoUrl |
||||
|
export let title |
||||
|
export let _bb |
||||
|
|
||||
|
let itemContainer |
||||
|
let hasLoaded |
||||
|
let currentChildren |
||||
|
|
||||
|
$: cssVariables = { |
||||
|
backgroundColor, |
||||
|
color, |
||||
|
borderWidth, |
||||
|
borderColor, |
||||
|
borderStyle, |
||||
|
} |
||||
|
|
||||
|
$: { |
||||
|
if (itemContainer) { |
||||
|
_bb.attachChildren(itemContainer) |
||||
|
if (!hasLoaded) { |
||||
|
_bb.call(onLoad) |
||||
|
hasLoaded = true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<nav use:cssVars={cssVariables}> |
||||
|
<a href="/"> |
||||
|
<img class="logo" alt="logo" src={logoUrl} height="30" /> |
||||
|
<span>{title}</span> |
||||
|
</a> |
||||
|
<div class="menu-items" bind:this={itemContainer} /> |
||||
|
</nav> |
||||
|
|
||||
|
<style> |
||||
|
nav { |
||||
|
color: var(--color); |
||||
|
background-color: var(--backgroundColor); |
||||
|
align-items: center; |
||||
|
display: flex; |
||||
|
font-weight: bold; |
||||
|
justify-content: space-between; |
||||
|
padding: 20px 0 20px 40px; |
||||
|
} |
||||
|
|
||||
|
nav > a { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
font-size: 1.5em; |
||||
|
color: var(--color); |
||||
|
text-decoration: none; |
||||
|
} |
||||
|
|
||||
|
nav a img { |
||||
|
border-radius: 15px; |
||||
|
margin-right: 15px; |
||||
|
} |
||||
|
.menu-items { |
||||
|
display: flex; |
||||
|
} |
||||
|
.menu-items > :global(*) { |
||||
|
margin: 0 10px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,11 @@ |
|||||
|
<script> |
||||
|
import Input from "./Input.svelte" |
||||
|
export let _bb |
||||
|
|
||||
|
export let label = "" |
||||
|
export let checked = false |
||||
|
export let value = "" |
||||
|
export let onchange = () => {} |
||||
|
</script> |
||||
|
|
||||
|
<Input type="radio" {_bb} {checked} {label} {value} {onchange} /> |
||||
@ -0,0 +1,10 @@ |
|||||
|
<script> |
||||
|
import Input from "./Input.svelte" |
||||
|
export let _bb |
||||
|
|
||||
|
export let label = "" |
||||
|
export let value = "" |
||||
|
export let onchange = () => {} |
||||
|
</script> |
||||
|
|
||||
|
<Input type="text" {_bb} {label} {value} {onchange} /> |
||||
Loading…
Reference in new issue