Merge branch 'feature/rest-redesign' into feature/query-auth

This commit is contained in:
Rory Powell 2021-12-11 21:43:03 +00:00
commit 9fa4fe6c76
20 changed files with 641 additions and 179 deletions

View File

@ -9,6 +9,7 @@
export let size = "M"
export let hoverable = false
export let disabled = false
export let color
$: rotation = getRotation(direction)
@ -25,7 +26,9 @@
focusable="false"
aria-hidden={hidden}
aria-label={name}
style={`transform: rotate(${rotation}deg)`}
style={`transform: rotate(${rotation}deg); ${
color ? `color: ${color};` : ""
}`}
>
<use xlink:href="#spectrum-icon-18-{name}" />
</svg>

View File

@ -19,6 +19,7 @@
<div class="icon-container">
<div
class="icon"
class:icon-small={size === "M" || size === "S"}
on:mouseover={() => (showTooltip = true)}
on:mouseleave={() => (showTooltip = false)}
>
@ -44,6 +45,7 @@
}
.container {
display: flex;
align-items: center;
}
.icon-container {
position: relative;
@ -64,4 +66,8 @@
.icon {
transform: scale(0.75);
}
.icon-small {
margin-top: -2px;
margin-bottom: -5px;
}
</style>

View File

@ -11,6 +11,8 @@
export let quiet = false
export let emphasized = false
let thisSelected = undefined
let _id = id()
const tab = writable({ title: selected, id: _id, emphasized })
setContext("tab", tab)
@ -20,9 +22,18 @@
const dispatch = createEventDispatcher()
$: {
if ($tab.title !== selected) {
selected = $tab.title
dispatch("select", selected)
if (thisSelected !== selected) {
thisSelected = selected
dispatch("select", thisSelected)
} else if ($tab.title !== thisSelected) {
thisSelected = $tab.title
dispatch("select", thisSelected)
}
if ($tab.title !== thisSelected) {
tab.update(state => {
state.title = thisSelected
return state
})
}
}

View File

@ -0,0 +1,227 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 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 424.9 376.9" style="enable-background:new 0 0 424.9 376.9;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#SVGID_00000173146453699406745440000014498258185172885933_);}
.st2{fill:url(#SVGID_00000072276482643233320020000011975551142177928352_);}
.st3{fill:url(#SVGID_00000000209806784423878430000006497884456548529061_);}
.st4{fill:url(#SVGID_00000145771761158215620900000018209346423627093124_);}
.st5{fill:#FFFFFF;}
.st6{fill:url(#SVGID_00000134247842479715961920000014109114775597637809_);}
.st7{fill:url(#SVGID_00000119810811088729404010000001339012453506459048_);}
.st8{fill:url(#SVGID_00000056391310230795810690000008029868739465234354_);}
.st9{fill:#010202;}
</style>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="51.0692" y1="421.4218" x2="245.8004" y2="138.7009">
<stop offset="0" style="stop-color:#90D1E1"/>
<stop offset="0.9921" style="stop-color:#F195BE"/>
</linearGradient>
<circle class="st0" cx="211.1" cy="189.1" r="179.2"/>
<linearGradient id="SVGID_00000181791594591671767050000008664012113219957652_" gradientUnits="userSpaceOnUse" x1="240.147" y1="117.7065" x2="241.3703" y2="117.7065">
<stop offset="6.070287e-03" style="stop-color:#EAB593"/>
<stop offset="0.9975" style="stop-color:#F4C278"/>
</linearGradient>
<path style="fill:url(#SVGID_00000181791594591671767050000008664012113219957652_);" d="M240.1,118
C241.1,117.6,242.4,117,240.1,118L240.1,118z"/>
<linearGradient id="SVGID_00000058550945621228767580000005935124031691961279_" gradientUnits="userSpaceOnUse" x1="94.8494" y1="142.6618" x2="97.0305" y2="142.6618">
<stop offset="6.070287e-03" style="stop-color:#EAB593"/>
<stop offset="0.9975" style="stop-color:#F4C278"/>
</linearGradient>
<path style="fill:url(#SVGID_00000058550945621228767580000005935124031691961279_);" d="M97,142.9c-3.2-0.6-2.2-0.3-1.3-0.2
c0.4,0,0.7,0.1,1.1,0.2C96.9,142.8,97,142.8,97,142.9z"/>
<linearGradient id="SVGID_00000137132525261985581510000000877506361518317499_" gradientUnits="userSpaceOnUse" x1="178.9468" y1="243.9432" x2="180.17" y2="243.9432">
<stop offset="6.070287e-03" style="stop-color:#EAB593"/>
<stop offset="0.9975" style="stop-color:#F4C278"/>
</linearGradient>
<path style="fill:url(#SVGID_00000137132525261985581510000000877506361518317499_);" d="M178.9,244.2
C179.9,243.8,181.2,243.3,178.9,244.2L178.9,244.2z"/>
<linearGradient id="SVGID_00000147899999161542128250000012606861005779996338_" gradientUnits="userSpaceOnUse" x1="147.0169" y1="218.2912" x2="147.8155" y2="218.2912">
<stop offset="6.070287e-03" style="stop-color:#EAB593"/>
<stop offset="0.9975" style="stop-color:#F4C278"/>
</linearGradient>
<path style="fill:url(#SVGID_00000147899999161542128250000012606861005779996338_);" d="M147.8,218.4c-1.2-0.2-0.8-0.1-0.5-0.1
C147.5,218.3,147.6,218.3,147.8,218.4C147.8,218.4,147.8,218.4,147.8,218.4z"/>
<path class="st5" d="M136.2,205.2l0.6,2.3l-6.8,1.6l0.8,3.3l-1.9,0.5l-1.3-5.6L136.2,205.2z"/>
<path class="st5" d="M134.6,216.1c2.7-1.1,4.9-0.1,5.8,2.1c0.9,2.3,0,4.5-2.7,5.6c-2.7,1.1-5,0.2-5.9-2.1
C130.9,219.4,131.9,217.2,134.6,216.1z M136.8,221.5c1.5-0.6,2.1-1.6,1.7-2.6c-0.4-1-1.5-1.3-3-0.7c-1.5,0.6-2.2,1.6-1.8,2.6
C134.1,221.9,135.3,222.1,136.8,221.5z"/>
<radialGradient id="SVGID_00000148647616164358038390000000096934996311696560_" cx="211.1145" cy="189.2326" r="154.4542" gradientUnits="userSpaceOnUse">
<stop offset="3.599497e-04" style="stop-color:#FFFFFF"/>
<stop offset="0.9083" style="stop-color:#FFFFFF;stop-opacity:9.169579e-02"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</radialGradient>
<circle style="fill:url(#SVGID_00000148647616164358038390000000096934996311696560_);" cx="211.1" cy="189.2" r="154.5"/>
<linearGradient id="SVGID_00000181795849749029799540000017664282943098263974_" gradientUnits="userSpaceOnUse" x1="201.3407" y1="294.9478" x2="220.3445" y2="87.1506">
<stop offset="0" style="stop-color:#5760A9"/>
<stop offset="0.5157" style="stop-color:#ADB3D9"/>
<stop offset="0.6156" style="stop-color:#BEC4E2"/>
<stop offset="0.7199" style="stop-color:#BBC1DF"/>
<stop offset="0.8218" style="stop-color:#B0B7D7"/>
<stop offset="0.9223" style="stop-color:#9FA6CA"/>
<stop offset="1" style="stop-color:#8D95BD"/>
</linearGradient>
<path style="fill:url(#SVGID_00000181795849749029799540000017664282943098263974_);" d="M397.7,131.5L314,95.7L85.6,142.5l-2.7-4.6
l-4.4,0.8l-2-4.5c0,0-0.8-2.1,2.2-1.8c3,0.3,4.7,0.2,10.6-3.7c5.9-3.9,2.3-2.2,2.8-5.5c0.5-3.3-5.2-6.1-11.5-10.7
c-6.3-4.6-12.4-5.9-14.1-2.7c-4.9,11,1.7,17.2,1.7,17.2l3.9,3.3l-0.6,2.1l2.7,7.4l-4,0.7l-1,5.4l-46.2,9.5l1.3,19.3
c-0.3-0.6,0.8,1.9-0.8,2c-3.1,0.1-5.7,0.5-8.1,1.6c-5.4,2.4-9.5,7-11.2,12.6C2.4,196.4,5,203.4,10,207c3.3,2.4,5.2,4.1,8.9,3.4
c0.8,0.2,1.2,0,2,0c1,0,3-0.1,3.8-0.5c1,0.1,1.3,1,1.4-0.3c0-0.1,1.6,0.1,1.6,0c0.1-0.1-0.3,1.8-0.1,1.7l-0.5,3.8
c-5.1,0.8-10.1,2.8-13.7,6.6c-4,4.3-5.4,9.6-4.9,15.3c0.4,5.2,1.3,9.2,6.7,11.3c2.9,1.1,6.6,2.3,9.5,1.5c1.6-0.4,4.5-0.2,6.1-0.6
l1.2,9l44.4,25.4l276.3-52.7l67.8-67.9L397.7,131.5z M26.2,202.7c0-0.2,0-0.4,0-0.6L26.2,202.7C26.2,202.7,26.2,202.7,26.2,202.7z
M28,229.2l0.1,0.9c-0.2-0.2-0.3-0.3-0.5-0.4C27.7,229.5,27.9,229.4,28,229.2z M27.2,233.9c0,0-0.1,0-0.1,0c0.4-0.4,0.7-0.8,1.1-1.1
c0.1-0.1,0.1-0.1,0.1-0.2l0.1,1.1C28,233.6,27.6,233.7,27.2,233.9z M28.8,244.6c0.1-0.1,0.1-0.2,0.2-0.3c0-0.1,0.1-0.3,0.1-0.4
l0,0.7C29,244.5,28.9,244.6,28.8,244.6z"/>
<linearGradient id="SVGID_00000152227878615532122350000012236979203658928007_" gradientUnits="userSpaceOnUse" x1="318.3721" y1="135.4384" x2="340.2567" y2="95.2667" gradientTransform="matrix(0.921 -3.872472e-02 0 1 31.6061 15.4856)">
<stop offset="0" style="stop-color:#B263A4"/>
<stop offset="2.387382e-02" style="stop-color:#B465A4"/>
<stop offset="0.7022" style="stop-color:#E5A177"/>
<stop offset="0.9988" style="stop-color:#FAB84D"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000152227878615532122350000012236979203658928007_);" points="312.1,97 315.4,129.8 367,120.3
"/>
<g>
<path class="st9" d="M422.2,162.2c-0.3-1.1-1.1-1.9-1.7-2.8c-0.8-1-1.6-2-2.3-3.1c-2.6-3.4-4.4-6.6-7-10c-1.6-2.1-3-4.2-4.6-6.4
c-1.3-1.9-4.9-6.9-7.8-9.6c-1.6-1.4-11-5.7-19.1-9.2C352.7,54.5,287.3,7.3,211.1,7.3c-87.4,0-160.5,62-177.8,144.4c0,0-0.1,0-0.1,0
c-1.2,0.3-2.4,0.5-3.6,0.8c-0.9,0.2-1.9,0.4-2.8,0.6c-0.7,0.1-1.4,0.3-2,0.4c-0.4,0.1-0.8,0.2-1.2,0.2c-0.1,0-0.2,0-0.3,0.1
l-1.7,0.2l0.2,1.8l1.5,18.7c-0.7,0.1-1.5,0.3-2.2,0.4c-0.9,0.2-1.8,0.4-2.7,0.5c-4.8,0.8-9,3.5-11.9,7.6c-2.9,4.1-4.2,9-3.5,13.9
c0.6,4.9,3.1,9.2,6.9,12.1c3.2,2.5,7.1,3.7,11.1,3.5c0.7,0,1.5-0.1,2.2-0.2l1.7-0.3c0.5-0.1,0.9-0.2,1.4-0.3l0.2,2.4
c-0.6,0.1-1.1,0.2-1.7,0.3c-0.9,0.2-1.8,0.4-2.7,0.5c-4.8,0.8-9,3.5-11.9,7.6c-2.9,4.1-4.2,9-3.5,13.9c1.2,9.4,9,16,18,15.6
c0.7,0,1.4-0.1,2.2-0.2l1.7-0.3c0.3-0.1,0.6-0.1,0.9-0.2l0.5,6.4l0.2,1.1l0.8,0.4l17.6,10.1c29.7,59.9,91.5,101.3,162.9,101.3
c98.8,0,179.5-79.3,181.6-177.7c1.3-1.3,2.7-2.6,4-4c2.7-2.6,5.4-5.3,8.1-7.9c2.3-2.3,4.7-4.6,7-6.9c2.1-2.1,4.2-4.2,6.3-6.2
c1-1,2-1.9,3-2.9C421.9,164.3,422.5,163.3,422.2,162.2z M211.1,12.3c72.3,0,134.6,43.7,161.9,106.1c-7-3-14.1-5.9-21.1-8.9
c-8.4-3.6-16.9-7.1-25.3-10.7c-2.5-1.1-5-2.1-7.5-3.2c-1.3-0.5-2.5-1.1-3.8-1.6c-1.1-0.5-2.4-1.2-3.6-1c-0.2,0-0.3,0.1-0.5,0.1
c-0.5,0.1-1.1,0.2-1.6,0.3c-0.8,0.2-1.6,0.3-2.4,0.5c-1.1,0.2-2.2,0.5-3.2,0.7c-1.3,0.3-2.7,0.6-4,0.8c-1.6,0.3-3.1,0.7-4.7,1
c-1.8,0.4-3.6,0.7-5.3,1.1c-2,0.4-4,0.8-6,1.3c-2.2,0.5-4.4,0.9-6.5,1.4c-2.4,0.5-4.7,1-7.1,1.5c-2.5,0.5-5,1.1-7.5,1.6
c-2.7,0.6-5.3,1.1-8,1.7c-2.8,0.6-5.6,1.2-8.4,1.8c-2.9,0.6-5.8,1.2-8.7,1.8c-3,0.6-6,1.3-9.1,1.9c-3.1,0.7-6.2,1.3-9.3,2
c-3.2,0.7-6.4,1.3-9.6,2c-3.2,0.7-6.5,1.4-9.7,2.1c-3.3,0.7-6.6,1.4-9.9,2.1c-3.3,0.7-6.7,1.4-10,2.1c-3.3,0.7-6.7,1.4-10,2.1
c-3.3,0.7-6.7,1.4-10,2.1c-3.3,0.7-6.7,1.4-10,2.1c-3.3,0.7-6.6,1.4-9.9,2.1c-3.3,0.7-6.5,1.4-9.8,2.1c-3.2,0.7-6.4,1.4-9.7,2
c-3.1,0.7-6.3,1.3-9.4,2c-3.1,0.6-6.1,1.3-9.2,1.9c-3,0.6-5.9,1.3-8.9,1.9c-2.4,0.5-4.8,1-7.2,1.5c-0.7-1.2-1.5-2.3-2.5-3.2
c-0.2-0.2-0.5-0.2-0.8-0.2c-0.3-0.3-0.7-0.3-1.1-0.2c-0.9,0.2-1.7,0.4-2.6,0.5c-0.7-1.4-1.3-2.9-1.8-4.4c6.3,1,12.7-1.9,16.6-7.4
c0.3-0.4,0.2-0.8-0.1-1c0-1.9-1.7-4.2-3.6-6.1c0.6-1.1,1.3-2.1,1.8-3.2c0.7-0.1,1.3-0.5,1.6-1.1c0.6-1.2-0.1-2.7-1.4-3.1
c-1.3-0.4-3.1,1.1-2.3,2.5c0,0.3,0.1,0.5,0.2,0.8c-0.6,0.9-1.1,1.7-1.6,2.6c-1.4-1.2-2.7-2.2-3.4-2.8c-3.1-2.2-13.4-9.7-18-6.5
c-0.3,0-0.6,0.1-0.7,0.4c-0.1,0.2-0.2,0.4-0.3,0.6c-0.1,0.1-0.1,0.2-0.2,0.4c-3.8,7.4-2.1,16.7,5.2,21.5c0.1,0,0.2,0.1,0.2,0.1
c-0.2,0.3-0.4,0.7-0.5,1.1c-0.2,0.2-0.2,0.5-0.1,0.8c0,0.1,0,0.1,0.1,0.2c0,0.6,0.3,1.2,0.6,1.7c0.5,1.5,1.1,3,1.6,4.4
c-0.9,0.2-1.8,0.4-2.7,0.7c-0.4,0-0.8,0.2-1,0.6c-0.5,1.4-1.1,2.7-1.4,4.2c-2.2,0.5-4.4,0.9-6.5,1.4c-2.3,0.5-4.5,1-6.8,1.4
c-2.1,0.4-4.2,0.9-6.2,1.3c-1.9,0.4-3.8,0.8-5.7,1.2c-1.3,0.3-2.6,0.5-3.9,0.8C56.3,71.6,126.9,12.3,211.1,12.3z M312.1,97
L312.1,97l10.6,4.4c-0.1,0.1-0.2,0.3-0.3,0.4c-0.1,0.2-0.3,0.4-0.5,0.6c0,0-0.2,0.2-0.3,0.3c-0.1,0.1-0.3,0.3-0.3,0.4
c-0.2,0.2-0.3,0.4-0.5,0.6c-0.3,0.4-0.7,0.8-1.1,1.1c-0.7,0.7-1.4,1.4-2.2,2c0,0-0.4,0.3-0.4,0.3c-0.1,0.1-0.3,0.2-0.4,0.3
c-0.4,0.3-0.9,0.6-1.3,0.9c-0.6,0.4-1.3,0.8-2,1.1L312.1,97z M327.3,115.9c3.5-1.8,8.4-4.4,11.4-7.8l6.6,2.8c0,0.1,0,0.2,0,0.3
c0,0.3,0,0,0,0.4c-0.1,0.3-0.1,0.7-0.2,1c-0.3,1.2-0.6,1.8-1.3,2.9c-1.2,2-3.1,3.9-4.9,5.2c-3.3,2.3-6.8,4.2-10.3,6.3l-12.7,2.6
l-0.4-3.8C317.4,121.1,323.1,118.1,327.3,115.9z M359.2,116.8l6.4,2.7l-7.2,1.5C358.8,119.6,359.1,118.2,359.2,116.8z M70.8,141.6
c3.9-0.5,7.7-1.3,11.5-2.2c0.1,0,0.1-0.1,0.2-0.1c0.3,0.7,0.6,1.3,1,1.9c-2.4,0.5-4.7,1-7.1,1.5c-2.2,0.5-4.3,0.9-6.5,1.4
C70.2,143.2,70.5,142.4,70.8,141.6z M66.3,112.9c0.7,1.2,1.7,2.4,2.8,3.5c-1.3,2-1.6,4.7-1.3,7.1c0.1,0.5,0.2,1.2,0.4,1.9
C65.6,121.8,65.1,117.2,66.3,112.9z M84,115.9c0.6,0.5,1.9,1.7,3.3,3c-0.3,0.5-0.5,0.9-0.8,1.4c-0.8,1.3-1.7,2.7-2.2,4.1
c-1.1-0.5-2.2-1-3.3-1.6c-0.2-0.5-0.7-0.8-1.1-0.7c-1.6-1-3.2-2-4.6-3.1c0,0-0.1-0.1-0.1-0.1c0.1-0.9,0-1.8,0.1-2.7
c0.3-1.6,1.2-2.8,2-4.1c0.1-0.2,0.1-0.4,0.1-0.6C79.6,112.8,81.9,114.4,84,115.9z M76.3,131c0-0.1-0.1-0.1-0.1-0.1
c0.9-0.5,1.6-1.8,2.2-2.6c0.7-1.1,1.4-2.3,2-3.5c2,1.1,4.1,2.2,6.1,2.7c-0.9,0.9-1.8,1.6-2.9,2.2c-1.3,0.7-2.7,0.9-3.9,1.7
C78.6,131.4,77.4,131.3,76.3,131z M88.6,127.9c0.2,0,0.3,0,0.5,0c-0.5,0.4-1.1,0.8-1.6,1.1C87.9,128.7,88.3,128.3,88.6,127.9z
M87.8,125.5c-0.6-0.1-1.3-0.3-1.9-0.5c0.9-1.1,1.6-2.5,2.3-3.7c0.2-0.3,0.3-0.5,0.5-0.8C91.3,123.5,92.8,126.5,87.8,125.5z
M76,111.3c-1.3,1.1-2,3-2.3,4.6c-0.1,0.5-0.2,1.1-0.2,1.8c-3.2-3-8.8-9.1-3-8.3c2,0.3,3.9,0.9,5.8,1.8
C76.2,111.2,76.1,111.2,76,111.3z M69.7,123.8c-0.4-2.2,0.1-4.1,0.7-6.2c1.6,1.4,3.1,2.5,3.8,3.1c0.9,0.7,2.6,1.9,4.5,3.1
c-0.5,1.2-1,2.3-1.7,3.4c-0.5,0.9-1.5,1.9-1.8,2.9c-0.6-0.8-1.7-0.9-2.6-0.6c-0.3-0.2-0.6-0.3-0.8-0.5c-0.3-0.2-0.5-0.4-0.7-0.6
C71.3,126.9,70,125.3,69.7,123.8z M74.3,132.4c0.1,0.2,0.1,0.4,0,0.6c-0.2,0.5-0.9,0.5-1.3,0.2c-0.5-0.3-0.5-1-0.2-1.5
C73.3,132,73.8,132.2,74.3,132.4z M73.7,135.7c0.8,0,1.5-0.3,2.1-0.8c0.4,1,0.8,2,1.3,3c-0.8,0.2-1.6,0.4-2.4,0.6
C74.4,137.5,74,136.6,73.7,135.7z M91.7,113.1C91.7,113.1,91.7,113.1,91.7,113.1c0.1,0.1,0.2,0.2,0.1,0.3c0,0,0,0-0.1,0
c-0.1,0-0.1-0.1-0.2-0.1C91.5,113.2,91.6,113.1,91.7,113.1z M12.3,206.2c0.4-0.1,0.7-0.3,1.1-0.4c0.5-0.2,1-0.6,1.5-0.8
c0.4-0.2,0.1-0.7-0.3-0.7c-0.6,0.1-1.1,0.1-1.7,0.2c-0.5,0.1-1.1,0.2-1.6,0.4c-0.1,0-0.3,0.1-0.4,0.1c-1.2-1.2-2.2-2.5-2.9-4
c0.3-0.1,0.5-0.2,0.8-0.3c0.5-0.2,1.1-0.4,1.6-0.7c0.5-0.2,1-0.6,1.5-0.8c0.4-0.2,0.1-0.7-0.3-0.7c-0.6,0.1-1.1,0.1-1.7,0.2
c-0.5,0.1-1.1,0.2-1.6,0.4c-0.3,0.1-0.6,0.2-0.9,0.3c-0.3-1-0.6-2-0.7-3c-0.1-0.7-0.1-1.4-0.1-2.2c0.4-0.2,0.9-0.3,1.3-0.5
c0.5-0.2,1.1-0.4,1.6-0.7c0.5-0.2,1-0.6,1.5-0.8c0.4-0.2,0.1-0.7-0.3-0.7c-0.6,0.1-1.1,0.1-1.7,0.2c-0.5,0.1-1.1,0.2-1.6,0.4
c-0.2,0.1-0.4,0.1-0.7,0.2c0.2-1.5,0.6-3.1,1.3-4.5c0.2,0,0.3,0,0.5,0c0.3-0.1,0.5-0.1,0.8-0.2c0.5-0.1,1-0.3,1.4-0.4
c0.5-0.1,1-0.3,1.4-0.4c0.5-0.2,1-0.4,1.5-0.6c0.4-0.1,0.4-0.8-0.1-0.8c-0.3,0-0.5,0-0.8,0c-0.3,0-0.5,0-0.8,0
c-0.5,0.1-1.1,0.1-1.6,0.3c-0.4,0.1-0.9,0.2-1.3,0.3c0.2-0.2,0.3-0.5,0.5-0.7c2.1-2.9,5-4.9,8.2-5.7c-0.8,1.2-1.5,2.4-2.1,3.7
c-1.4,3.2-2.1,6.7-2,10.1c0.1,3.3,1.1,6.6,2.7,9.5c0.8,1.6,1.9,3,3,4.4c0.3,0.4,0.7,0.9,1.1,1.4C17.4,208.8,14.6,207.9,12.3,206.2z
M24.3,208.3l-0.7,0.1c-0.6-0.9-1.6-1.6-2.2-2.4c-1-1.2-1.9-2.5-2.7-3.9c-1.5-2.7-2.4-5.7-2.6-8.7c-0.1-3.2,0.5-6.3,1.8-9.2
c0.7-1.5,1.5-2.9,2.4-4.3c0.2-0.3,0.5-0.7,0.7-1c0.2,0,0.4-0.1,0.6-0.1c0.7-0.1,1.3-0.2,1.9-0.4l2.3,29.6
C25.3,208.1,24.8,208.2,24.3,208.3z M15.9,245.7c0.5-0.2,1-0.4,1.4-0.6c0.5-0.2,1-0.6,1.5-0.8c0.4-0.2,0.1-0.7-0.3-0.7
c-0.6,0.1-1.1,0.1-1.7,0.2c-0.5,0.1-1.1,0.2-1.6,0.4c-0.3,0.1-0.6,0.2-0.9,0.3c-1.2-1.2-2.2-2.6-3-4.2c0.4-0.2,0.9-0.3,1.3-0.5
c0.5-0.2,1.1-0.4,1.6-0.7c0.5-0.2,1-0.6,1.5-0.8c0.4-0.2,0.1-0.7-0.3-0.7c-0.6,0.1-1.1,0.1-1.7,0.2c-0.5,0.1-1.1,0.2-1.6,0.4
c-0.5,0.1-0.9,0.3-1.4,0.4c-0.3-0.9-0.5-1.8-0.6-2.8c-0.1-1.1-0.2-2.1-0.1-3.2c0.1,0,0.2-0.1,0.3-0.1c0.5-0.2,1.1-0.3,1.6-0.5
c0.5-0.2,1.1-0.4,1.6-0.6c0.5-0.2,0.9-0.6,1.4-0.8c0.3-0.2,0.2-0.7-0.2-0.7c-0.5,0.1-1.1,0-1.6,0.1c-0.6,0.1-1.2,0.2-1.7,0.3
c-0.4,0.1-0.8,0.2-1.1,0.3c0.4-1.9,1.1-3.6,2.2-5.3c0.1,0,0.1,0,0.2,0c0.3-0.1,0.5-0.1,0.8-0.2c0.5-0.1,1-0.3,1.4-0.4
c0.5-0.1,1-0.3,1.4-0.4c0.5-0.2,1-0.4,1.5-0.6c0.4-0.1,0.4-0.8-0.1-0.8c-0.3,0-0.5,0-0.8,0c-0.3,0-0.5,0-0.8,0
c-0.5,0.1-1.1,0.1-1.6,0.3c-0.1,0-0.3,0.1-0.4,0.1c2.2-2.4,5.1-4,8.3-4.6c0,0,0,0,0.1,0c-0.4,0.5-0.7,1.1-1,1.6
c-0.8,1.4-1.5,2.9-2.1,4.5c-1.1,3.2-1.4,6.7-0.9,10c0.4,3.2,1.5,6.2,3.2,8.9c0.8,1.3,1.8,2.6,2.9,3.8c0.2,0.2,0.4,0.4,0.6,0.6
C21.7,248.6,18.5,247.6,15.9,245.7z M28.4,247.7c-0.7-0.6-1.4-1.1-2-1.7c-1-1-1.8-2.1-2.5-3.2c-1.6-2.5-2.6-5.3-3-8.2
c-0.4-3-0.1-6,0.8-8.9c0.4-1.5,1-2.8,1.7-4.2c0.6-1,1.5-2.1,1.8-3.3c0,0,0,0,0,0c0.5-0.1,0.9-0.2,1.4-0.3l2.3,29.6
C28.7,247.7,28.6,247.7,28.4,247.7z M25.7,157.2l282.7-59.5c0.5,4,0.9,7.9,1.4,11.9c0.7,6.2,1.4,12.4,2.1,18.6c0.1,1,0.3,6,2,5.6
c0.5-0.1,1.1-0.2,1.6-0.3c1.7-0.4,3.5-0.7,5.2-1.1c5.3-1.1,10.7-2.2,16-3.3c6-1.2,12-2.4,18-3.7c4.6-0.9,9.1-1.9,13.7-2.8
c1.2-0.2,2.4-0.5,3.6-0.7l23.3,10.4L325,198.2L33.4,255.5L25.7,157.2z M325.2,202.1l23.2,27.2L76.3,281.1l-39.2-22.5L325.2,202.1z
M211.1,365.8c-66.6,0-124.7-37.1-154.8-91.6L75,284.8l0.7,0.2h0.6c0.7-0.1,1.3-0.3,2-0.4c1.9-0.4,3.8-0.7,5.7-1.1
c3-0.6,6-1.2,9.1-1.7c4-0.8,8-1.5,12-2.3c4.8-0.9,9.6-1.8,14.5-2.8c5.5-1.1,11-2.1,16.5-3.2c6.1-1.2,12.1-2.3,18.2-3.5
c6.5-1.2,13-2.5,19.4-3.7c6.8-1.3,13.5-2.6,20.3-3.9c6.9-1.3,13.8-2.6,20.7-3.9c6.9-1.3,13.8-2.6,20.7-3.9
c6.7-1.3,13.5-2.6,20.2-3.9c6.5-1.2,12.9-2.5,19.4-3.7c6-1.2,12.1-2.3,18.1-3.5c5.5-1,11-2.1,16.5-3.1c4.8-0.9,9.6-1.8,14.4-2.7
c4-0.8,7.9-1.5,11.9-2.3c3-0.6,6-1.1,9-1.7c1.9-0.4,3.8-0.7,5.6-1.1c1.2-0.2,2.3-0.3,3.2-1.2c1.6-1.6,3.2-3.2,4.8-4.8
c1.9-1.9,3.8-3.8,5.7-5.7c2.4-2.4,4.8-4.7,7.2-7.1c2.7-2.7,5.4-5.3,8.1-8c2.6-2.6,5.3-5.2,7.9-7.8
C382.7,291.4,305.5,365.8,211.1,365.8z M352.1,227.7l-23.5-27.6l68.9-64.5l20.6,27.1L352.1,227.7z"/>
<path class="st9" d="M180.7,145.3l6.5,65.1l27.8-4.7l-5.8-65.5L180.7,145.3z M188.9,208.2l-6.1-61.3l24.7-4.4l5.5,61.6L188.9,208.2
z"/>
<path class="st9" d="M150.8,168.3l4.4,48.5l20.4-4.2l-4.6-48.2L150.8,168.3z M156.9,214.5l-4.1-44.7l16.4-3.1l4.1,44.4L156.9,214.5
z"/>
<path class="st9" d="M120.2,199.8l2.5,24l28.7-6.4l-1.9-24.7L120.2,199.8z M124.5,221.4l-2.1-20.2l25.4-6.1l1.6,20.8L124.5,221.4z"
/>
<path class="st9" d="M40.5,215.3l2.9,27.8l22.4-3.3l-2.5-28.5L40.5,215.3z M45.2,240.9l-2.5-24l19-3.3l2.2,24.6L45.2,240.9z"/>
<path class="st9" d="M308.3,222.2l-10.7-7.4l-25.9,4.3l10.7,7.4L308.3,222.2z M297.3,216.3l7.3,5l-21.8,3.7l-7.3-5L297.3,216.3z"/>
<path class="st9" d="M295.2,236.7l25.9-4.3l-10.7-7.4l-25.9,4.3L295.2,236.7z M317.4,231.5l-21.8,3.7l-7.3-5l21.8-3.7L317.4,231.5z
"/>
<path class="st9" d="M88.7,275.9l25.9-4.3l-10.7-7.4l-25.9,4.3L88.7,275.9z M110.8,270.8L89,274.4l-7.3-5l21.8-3.7L110.8,270.8z"/>
<path class="st9" d="M91.3,254.3l-25.9,4.3l10.7,7.4l25.9-4.3L91.3,254.3z M69.1,259.4l21.8-3.7l7.3,5l-21.8,3.7L69.1,259.4z"/>
<path class="st9" d="M245.4,175l-2.8-36c-0.2-2-1.9-3.5-3.9-3.3c-1,0.1-1.9,0.5-2.5,1.3c-0.6,0.7-0.9,1.7-0.9,2.6l2.9,36.7
l-1.9,0.3l-2.8-35.8c-0.1-1-0.5-1.9-1.3-2.5c-0.7-0.6-1.7-0.9-2.6-0.9c-1,0.1-1.8,0.5-2.5,1.3c-0.6,0.7-0.9,1.7-0.9,2.6l2.9,36.5
l-1.9,0.3l-2.7-35.1c-0.2-2-1.9-3.5-3.9-3.3c-1,0.1-1.9,0.5-2.5,1.3c-0.6,0.7-0.9,1.7-0.9,2.6l2.8,35.7l-1.5,0.3l2.5,24.2l28.1-4.3
l-1.9-24.8L245.4,175z M237.2,139.4c0-0.5,0.1-0.9,0.4-1.2c0.3-0.3,0.7-0.6,1.2-0.6c0,0,0.1,0,0.1,0c0.9,0,1.6,0.7,1.7,1.6
l2.8,36.2l-3.4,0.6L237.2,139.4z M228.2,141.1c0-0.5,0.1-0.9,0.4-1.2c0.3-0.3,0.7-0.6,1.2-0.6h0c0,0,0.1,0,0.1,0
c0.4,0,0.8,0.1,1.1,0.4c0.3,0.3,0.6,0.7,0.6,1.2l2.8,36l-3.4,0.6L228.2,141.1z M219.1,143.4c0-0.5,0.1-0.9,0.4-1.2
c0.3-0.3,0.7-0.6,1.2-0.6c0,0,0.1,0,0.1,0c0.9,0,1.6,0.7,1.7,1.6l2.8,35.3l-3.4,0.6L219.1,143.4z M222.6,201.5l-2.1-20.4l24.8-4.2
l1.6,20.9L222.6,201.5z"/>
<path class="st9" d="M99.7,179c0.2-2.1-2.5-3-4.1-2.1c-1.1,0.7-1.8,2.1-1.5,3.3c-1.6-0.8-3.5,0-5.3,0.3c-2.6,0.4-5.2,0.9-7.8,1.5
c0.1-2.5-0.1-5-0.3-7.4c2.6-0.2,5.3-0.6,7.9-1.1c0.2,0,0.3-0.1,0.4-0.2c1.1,1.3,3.5,1.5,4.5,0c0.4-0.6,0.6-1.6,0.4-2.5
c0,0,0,0,0-0.1c0.2-2.1-2.5-3-4.1-2.1c-1,0.6-1.6,1.9-1.6,3c-0.1,0-0.2,0-0.2,0c-2.5,0.4-5,0.8-7.5,1.4c0-0.2,0-0.4-0.1-0.6
c-0.2-2.4-0.3-5-1.2-7.2c2.7-0.3,5.3-0.6,8-1c1.9-0.3,4.5-0.1,5.8-1.8c0.1,0.4,0.2,0.7,0.4,1c1.1,1.4,3.6,1.7,4.6,0.2
c0.4-0.6,0.6-1.6,0.4-2.5c0,0,0,0,0-0.1c0.2-2.1-2.5-3-4.1-2.1c-1.1,0.6-1.7,2-1.5,3.2c-1.6-1.2-3.8-0.2-5.7,0.1
c-2.9,0.5-5.7,1-8.5,1.6c-0.7,0.2-0.6,1.2,0,1.3c-0.2,0.7-0.3,1.4-0.3,2.1c-1.8-0.4-3.7,0.3-5.6,0.7c-3.4,0.6-6.9,1.2-10.3,1.9
c-0.7,0.1-0.6,1.2,0,1.3c-0.6,4.3,0.1,8.7,0.4,12.9c0.3,4.4,0.6,8.7,1.4,13c-1.7-0.4-3.6-0.2-5.4,0.2c0.1-1.4-0.1-2.7-0.3-4.1
c-0.1-1-0.1-2.1-0.4-3.1c0.5-0.1,1-0.2,1.5-0.2c0.4-0.1,0.7-0.5,0.7-0.9c-0.2-2.5-0.4-5.1-0.6-7.6c0-0.5-0.4-1-0.9-0.9
c-5.9,0.7-10.8-2.7-11.6-8.7c0-0.1,0-0.1,0-0.1c0,0,0,0,0,0c0-0.1-0.1-0.2-0.1-0.3c0-0.1-0.1-0.1-0.1-0.2c0,0,0,0-0.1-0.1
c-0.2-0.2-0.5-0.3-0.8-0.2c-2.5,0.4-5.1,0.8-7.6,1.2c-0.4,0.1-0.7,0.5-0.7,0.9c0.5,6.4,1.1,12.7,1.6,19.1c0.1,0.7,0.6,1,1.2,0.9
c0.4-0.1,0.8-0.1,1.1-0.2c-0.1,0.9,0,1.8,0.1,2.7c0.1,1.8,0.2,3.7,0.8,5.4c0,0.4,0.2,0.9,0.6,0.8c3.7-0.5,7.4-1,11.1-1.5
c3.2-0.5,7.3-0.5,10.1-2.2c0.1,0.5,0.9,0.4,0.9-0.1c0.1-4.9-0.4-9.7-0.8-14.6c-0.3-4-0.4-8.1-1.6-11.9c3-0.3,6-0.7,9-1.2
c2-0.3,4.5-0.3,6.4-1.2c0.1,1.7,0.3,3.4,0.4,5c0.2,2.9,0.4,5.7,1,8.5c-0.2,0.4,0,1.1,0.6,1.1c2.8-0.3,5.5-0.7,8.3-1.1
c1.8-0.3,4.2-0.1,5.5-1.5c0.1,0.2,0.1,0.3,0.2,0.4c1.1,1.4,3.6,1.7,4.6,0.2C99.7,180.9,99.9,179.9,99.7,179
C99.7,179,99.7,179,99.7,179z M90.9,170.5c0.7-0.5,1.2,0.1,1.5,0.7c0,0.3,0,0.5-0.2,0.8c-0.3,0.6-1.2,0.6-1.6,0.1
C90.1,171.7,90.4,170.8,90.9,170.5z M95.5,160.8c0.7-0.5,1.2,0.1,1.5,0.7c0,0.3,0,0.5-0.2,0.8c-0.3,0.6-1.2,0.6-1.6,0.1
C94.8,162,95,161.2,95.5,160.8z M40.1,174.1c0-0.1,0-0.1,0-0.2c1.8,0.1,4-0.5,5.9-1c1.3,6,6.1,9.8,12.3,9.5c0,1.9-0.1,4.2,0.5,5.8
c-0.1,0-0.1,0-0.2,0c-1-0.1-2.6,0.4-3.6,0.6c-4.5,0.7-9,1.5-13.4,2.2c-0.4-4.5-0.7-9-1.1-13.5C40.3,176.6,40.5,175.1,40.1,174.1z
M49.6,191.6c-0.1,0.9,0,2,0.1,2.9c0.1,1.6,0.1,3.1,0.5,4.6c-2,0.4-4.1,0.7-6.1,1.1c0.1-1.6-0.1-3.1-0.2-4.7
c-0.1-0.9-0.1-2-0.4-2.9C45.5,192.3,47.6,192,49.6,191.6z M51.7,198.9c0.1-1.4-0.1-2.9-0.3-4.3c-0.1-1-0.1-2.1-0.4-3.1
c2.1-0.3,4.1-0.7,6.2-1c-0.1,0.9,0,2,0.1,2.9c0.1,1.5,0.1,3,0.5,4.4c-0.7,0.1-1.3,0.3-2,0.4C54.4,198.4,53,198.6,51.7,198.9z
M97.9,180.2c-0.3,0.6-1.2,0.6-1.6,0.1c-0.4-0.5-0.2-1.3,0.3-1.7c0.7-0.5,1.2,0.1,1.5,0.7C98.1,179.6,98.1,179.9,97.9,180.2z"/>
<path class="st9" d="M293.1,170.4c-5.6,0.8-11.1,1.6-16.7,2.4c0.2-6.9-0.6-14.5-2.3-21.1c0.5-0.2,0.9-0.4,1.2-0.9
c0.4-0.6,0.6-1.6,0.4-2.5c0,0,0,0,0-0.1c0.2-2.1-2.5-3-4.1-2.1c-1.5,0.9-2.1,3.1-1,4.5c0.6,0.8,1.7,1.3,2.8,1.2
c-0.6,6.8-0.1,14.5,1.3,21.2c-2.4,0.4-4.7,0.8-7.1,1.2c0.2-4-0.3-8.1-0.7-12.1c-0.3-2.8-0.4-5.5-1.3-8.1c0.6-0.1,1.2-0.4,1.5-0.9
c0.4-0.6,0.6-1.6,0.4-2.5c0,0,0,0,0-0.1c0.2-2.1-2.5-3-4.1-2.1c-1.5,0.9-2.1,3.1-1,4.5c0.5,0.7,1.3,1.1,2.1,1.2
c-0.5,3.2-0.1,6.6,0.2,9.8c0.3,3.6,0.5,7.2,1.2,10.7c-2.2,0.4-4.5,0.8-6.7,1.3c-0.1-7-0.4-15-2.3-21.8c0.5-0.2,0.9-0.4,1.2-0.9
c0.4-0.6,0.6-1.6,0.4-2.5c0,0,0,0,0-0.1c0.2-2.1-2.5-3-4.1-2.1c-1.5,0.9-2.1,3.1-1,4.5c0.5,0.7,1.5,1.2,2.4,1.2
c-0.7,7.3,0.3,15.7,1.5,22.7c0.1,0.5,0.5,0.7,0.9,0.7c0.1,0.1,0.3,0.2,0.6,0.2c11.5-1.4,23-3.3,34.4-5.4c-0.4,3.7,0.1,7.7,0.5,11.3
c0,0.1,0,0.2,0.1,0.2c-2.7,0-5.4,0.5-8,1c-1.9,0.3-4.1,0.4-5.8,1.4c-0.9-0.8-2.4-1-3.4-0.4c-1.5,0.9-2.1,3.1-1,4.5
c1.1,1.4,3.6,1.7,4.6,0.2c0.4-0.6,0.6-1.6,0.4-2.5c0,0,0,0,0-0.1c0-0.2,0-0.3,0-0.5c1.5,0.1,3-0.2,4.5-0.5c3-0.5,6.2-0.8,9.1-1.8
c0.3-0.1,0.5-0.4,0.5-0.7c0.4,0,0.7-0.3,0.7-0.8c-0.2-3.8-0.3-8.1-1.5-11.8C294.6,171.2,294,170.2,293.1,170.4z M272.4,149.6
c-0.4-0.5-0.2-1.3,0.3-1.7c0.7-0.5,1.2,0.1,1.5,0.7c0,0.3,0,0.5-0.2,0.8C273.7,150.1,272.8,150.1,272.4,149.6z M264.6,150.2
c0.7-0.5,1.2,0.1,1.5,0.7c0,0.3,0,0.5-0.2,0.8c-0.3,0.6-1.2,0.6-1.6,0.1C263.8,151.4,264.1,150.6,264.6,150.2z M255.5,151.9
c-0.4-0.5-0.2-1.3,0.3-1.7c0.7-0.5,1.2,0.1,1.5,0.7c0,0.3,0,0.5-0.2,0.8C256.8,152.4,255.9,152.4,255.5,151.9z M279.2,189.1
c-0.3,0.6-1.2,0.6-1.6,0.1c-0.4-0.5-0.2-1.3,0.3-1.7c0.7-0.5,1.2,0.1,1.5,0.7C279.4,188.5,279.4,188.8,279.2,189.1z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -8,6 +8,7 @@
import EditQueryPopover from "./popovers/EditQueryPopover.svelte"
import NavItem from "components/common/NavItem.svelte"
import TableNavigator from "components/backend/TableNavigator/TableNavigator.svelte"
import { customQueryIconText, customQueryIconColor } from "helpers/data/utils"
import ICONS from "./icons"
let openDataSources = []
@ -129,6 +130,8 @@
<NavItem
indentLevel={1}
icon="SQLQuery"
iconText={customQueryIconText(datasource, query)}
iconColor={customQueryIconColor(datasource, query)}
text={query.name}
opened={$queries.selected === query._id}
selected={$queries.selected === query._id}

View File

@ -1,12 +1,30 @@
<script>
import { Icon } from "@budibase/bbui"
import { Icon, Body } from "@budibase/bbui"
</script>
<a target="_blank" href="https://github.com/Budibase/budibase/discussions">
<Icon hoverable name="Help" size="XXL" />
<div class="inner hoverable">
<div class="hidden hoverable">
<Body size="S">Need help? Go to our forums</Body>
</div>
<Icon name="Help" size="XXL" />
</div>
</a>
<style>
.inner {
display: flex;
align-items: center;
gap: var(--spacing-s);
}
.inner :global(*) {
pointer-events: all;
transition: color var(--spectrum-global-animation-duration-100, 130ms);
}
.inner:hover :global(*) {
color: var(--spectrum-alias-icon-color-selected-hover);
cursor: pointer;
}
a {
color: inherit;
position: absolute;
@ -14,4 +32,10 @@
right: var(--spacing-m);
border-radius: 55%;
}
.hidden {
display: none;
}
.inner:hover .hidden {
display: block;
}
</style>

View File

@ -11,6 +11,8 @@
export let selected = false
export let opened = false
export let draggable = false
export let iconText
export let iconColor
const dispatch = createEventDispatcher()
@ -42,9 +44,13 @@
{/if}
<slot name="icon" />
{#if icon}
{#if iconText}
<div class="iconText" style={iconColor ? `color: ${iconColor};` : ""}>
{iconText}
</div>
{:else if icon}
<div class="icon">
<Icon size="S" name={icon} />
<Icon color={iconColor} size="S" name={icon} />
</div>
{/if}
<div class="text">{text}</div>
@ -123,4 +129,9 @@
justify-content: center;
align-items: center;
}
.iconText {
margin-top: 1px;
font-size: var(--spectrum-global-dimension-font-size-50);
}
</style>

View File

@ -17,7 +17,7 @@
queries as queriesStore,
} from "stores/backend"
import { datasources, integrations } from "stores/backend"
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
import BindingBuilder from "components/integration/QueryBindingBuilder.svelte"
import IntegrationQueryEditor from "components/integration/index.svelte"
import { makePropSafe as safe } from "@budibase/string-templates"
@ -148,15 +148,15 @@
/>
{#if value?.type === "query"}
<i class="ri-settings-5-line" on:click={openQueryParamsDrawer} />
<Drawer title={"Query Parameters"} bind:this={drawer}>
<Drawer title={"Query Bindings"} bind:this={drawer}>
<Button slot="buttons" cta on:click={saveQueryParams}>Save</Button>
<DrawerContent slot="body">
<Layout noPadding>
<Layout noPadding gap="XS">
{#if getQueryParams(value).length > 0}
<ParameterBuilder
<BindingBuilder
bind:customParams={tmpQueryParams}
parameters={getQueryParams(value)}
{bindings}
bindings={getQueryParams(value)}
bind:bindableOptions={bindings}
/>
{/if}
<IntegrationQueryEditor

View File

@ -1,7 +1,7 @@
<script>
import { Select, Layout, Input, Checkbox } from "@budibase/bbui"
import { datasources, integrations, queries } from "stores/backend"
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
import BindingBuilder from "components/integration/QueryBindingBuilder.svelte"
import IntegrationQueryEditor from "components/integration/index.svelte"
export let parameters
@ -53,10 +53,10 @@
{#if query?.parameters?.length > 0}
<div>
<ParameterBuilder
<BindingBuilder
bind:customParams={parameters.queryParams}
parameters={query.parameters}
{bindings}
bindings={query.parameters}
bind:bindableOptions={bindings}
/>
<IntegrationQueryEditor
height={200}

View File

@ -21,6 +21,9 @@
export let headings = false
export let options
export let toggle
export let keyPlaceholder = "Key"
export let valuePlaceholder = "Value"
export let tooltip
let fields = Object.entries(object).map(([name, value]) => ({ name, value }))
let fieldActivity = []
@ -76,8 +79,8 @@
{#if Object.keys(object || {}).length > 0}
{#if headings}
<div class="container" class:container-active={toggle}>
<Label>Key</Label>
<Label>Value</Label>
<Label {tooltip}>{keyPlaceholder}</Label>
<Label>{valuePlaceholder}</Label>
{#if toggle}
<Label>Active</Label>
{/if}
@ -85,12 +88,16 @@
{/if}
<div class="container" class:container-active={toggle} class:readOnly>
{#each fields as field, idx}
<Input placeholder="Key" bind:value={field.name} on:change={changed} />
<Input
placeholder={keyPlaceholder}
bind:value={field.name}
on:change={changed}
/>
{#if options}
<Select bind:value={field.value} on:change={changed} {options} />
{:else}
<Input
placeholder="Value"
placeholder={valuePlaceholder}
bind:value={field.value}
on:change={changed}
/>
@ -127,4 +134,7 @@
.container-active {
grid-template-columns: 1fr 1fr 50px 20px;
}
.readOnly {
grid-template-columns: 1fr 1fr;
}
</style>

View File

@ -1,5 +1,5 @@
<script>
import { Icon, Body, Button, Input, Heading, Layout } from "@budibase/bbui"
import { Body, Button, Heading, Icon, Input, Layout } from "@budibase/bbui"
import {
readableToRuntimeBinding,
runtimeToReadableBinding,
@ -7,83 +7,82 @@
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
export let bindable = true
export let parameters = []
export let bindings = []
export let bindableOptions = []
export let customParams = {}
function newQueryParameter() {
parameters = [...parameters, {}]
function newQueryBinding() {
bindings = [...bindings, {}]
}
function deleteQueryParameter(idx) {
parameters.splice(idx, 1)
parameters = parameters
function deleteQueryBinding(idx) {
bindings.splice(idx, 1)
bindings = bindings
}
// This is necessary due to the way readable and writable bindings are stored.
// The readable binding in the UI gets converted to a UUID value that the client understands
// for parsing, then converted back so we can display it the readable form in the UI
function onBindingChange(param, valueToParse) {
const parsedBindingValue = readableToRuntimeBinding(bindings, valueToParse)
customParams[param] = parsedBindingValue
customParams[param] = readableToRuntimeBinding(
bindableOptions,
valueToParse
)
}
</script>
<Layout paddingX="none" gap="S">
<div class="controls">
<Heading size="XS">Parameters</Heading>
<Layout noPadding={bindable} gap="S">
<div class="controls" class:height={!bindable}>
<Heading size="XS">Bindings</Heading>
{#if !bindable}
<Button secondary on:click={newQueryParameter}>Add Param</Button>
<Button secondary on:click={newQueryBinding}>Add Binding</Button>
{/if}
</div>
<Body size="S">
{#if !bindable}
Parameters come in two parts: the parameter name, and a default/fallback
value.
Bindings come in two parts: the binding name, and a default/fallback
value. These bindings can be used as Handlebars expressions throughout the
query.
{:else}
Enter a value for each parameter. The default values will be used for any
Enter a value for each binding. The default values will be used for any
values left blank.
{/if}
</Body>
<div class="parameters" class:bindable>
{#each parameters as parameter, idx}
<div class="bindings" class:bindable>
{#each bindings as binding, idx}
<Input
placeholder="Parameter Name"
placeholder="Binding Name"
thin
disabled={bindable}
bind:value={parameter.name}
bind:value={binding.name}
/>
<Input
placeholder="Default"
thin
disabled={bindable}
bind:value={parameter.default}
bind:value={binding.default}
/>
{#if bindable}
<DrawerBindableInput
title={`Query parameter "${parameter.name}"`}
title={`Query binding "${binding.name}"`}
placeholder="Value"
thin
on:change={evt => onBindingChange(parameter.name, evt.detail)}
on:change={evt => onBindingChange(binding.name, evt.detail)}
value={runtimeToReadableBinding(
bindings,
customParams?.[parameter.name]
bindableOptions,
customParams?.[binding.name]
)}
{bindings}
{bindableOptions}
/>
{:else}
<Icon
hoverable
name="Close"
on:click={() => deleteQueryParameter(idx)}
/>
<Icon hoverable name="Close" on:click={() => deleteQueryBinding(idx)} />
{/if}
{/each}
</div>
</Layout>
<style>
.parameters.bindable {
.bindings.bindable {
grid-template-columns: 1fr 1fr 1fr;
}
@ -91,13 +90,16 @@
display: flex;
align-items: center;
justify-content: space-between;
height: 40px;
}
.parameters {
.bindings {
display: grid;
grid-template-columns: 1fr 1fr 5%;
grid-gap: 10px;
align-items: center;
}
.height {
height: 40px;
}
</style>

View File

@ -17,7 +17,7 @@
import ExtraQueryConfig from "./ExtraQueryConfig.svelte"
import IntegrationQueryEditor from "components/integration/index.svelte"
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
import BindingBuilder from "components/integration/QueryBindingBuilder.svelte"
import { datasources, integrations, queries } from "stores/backend"
import { capitalise } from "../../helpers"
import CodeMirrorEditor from "components/common/CodeMirrorEditor.svelte"
@ -120,7 +120,7 @@
config={integrationInfo.extra}
/>
{/if}
<ParameterBuilder bind:parameters={query.parameters} bindable={false} />
<BindingBuilder bind:bindings={query.parameters} bindable={false} />
{/if}
</div>
{#if shouldShowQueryConfig}

View File

@ -1,3 +1,5 @@
import { IntegrationTypes } from "constants/backend"
export function schemaToFields(schema) {
const response = {}
if (schema && typeof schema === "object") {
@ -49,3 +51,60 @@ export function buildQueryString(obj) {
}
return str
}
export function keyValueToQueryParameters(obj) {
let array = []
if (obj && typeof obj === "object") {
for (let [key, value] of Object.entries(obj)) {
array.push({ name: key, default: value })
}
}
return array
}
export function queryParametersToKeyValue(array) {
let obj = {}
if (Array.isArray(array)) {
for (let param of array) {
obj[param.name] = param.default
}
}
return obj
}
export function customQueryIconText(datasource, query) {
if (datasource.source !== IntegrationTypes.REST) {
return
}
switch (query.queryVerb) {
case "create":
return "POST"
case "update":
return "PUT"
case "read":
return "GET"
case "delete":
return "DELETE"
case "patch":
return "PATCH"
}
}
export function customQueryIconColor(datasource, query) {
if (datasource.source !== IntegrationTypes.REST) {
return
}
switch (query.queryVerb) {
case "create":
return "#dcc339"
case "update":
return "#5197ec"
case "read":
return "#53a761"
case "delete":
return "#ea7d82"
case "patch":
default:
return
}
}

View File

@ -13,6 +13,7 @@
Heading,
RadioGroup,
Label,
Body,
TextArea,
Table,
notifications,
@ -30,6 +31,8 @@
schemaToFields,
breakQueryString,
buildQueryString,
keyValueToQueryParameters,
queryParametersToKeyValue,
} from "helpers/data/utils"
import {
RestBodyTypes as bodyTypes,
@ -37,9 +40,12 @@
} from "constants/backend"
import JSONPreview from "components/integration/JSONPreview.svelte"
import AccessLevelSelect from "components/integration/AccessLevelSelect.svelte"
import Placeholder from "assets/bb-spaceship.svg"
import { cloneDeep } from "lodash/fp"
let query, datasource
let breakQs = {}
let breakQs = {},
bindings = {}
let url = ""
let saveId
let response, schema, isGet
@ -56,7 +62,7 @@
response?.info?.code >= 200 && response?.info?.code <= 206
function getSelectedQuery() {
return (
return cloneDeep(
$queries.list.find(q => q._id === $queries.selected) || {
datasourceId: $params.selectedDatasource,
parameters: [],
@ -85,7 +91,9 @@
return qs.length > 0 ? `${newUrl}?${qs}` : newUrl
}
function learnMoreBanner() {}
function learnMoreBanner() {
window.open("https://docs.budibase.com/building-apps/data/transformers")
}
function buildQuery() {
const newQuery = { ...query }
@ -93,6 +101,7 @@
newQuery.fields.path = url.split("?")[0]
newQuery.fields.queryString = queryString
newQuery.schema = fieldsToSchema(schema)
newQuery.parameters = keyValueToQueryParameters(bindings)
return newQuery
}
@ -101,6 +110,7 @@
try {
const { _id } = await queries.save(toSave.datasourceId, toSave)
saveId = _id
query = getSelectedQuery()
notifications.success(`Request saved successfully.`)
} catch (err) {
notifications.error(`Error creating query. ${err.message}`)
@ -114,6 +124,7 @@
notifications.info("Request did not return any data.")
} else {
response.info = response.info || { code: 200 }
schema = response.schema
notifications.success("Request sent successfully.")
}
} catch (err) {
@ -127,6 +138,7 @@
breakQs = breakQueryString(qs)
url = buildUrl(query.fields.path, breakQs)
schema = schemaToFields(query.schema)
bindings = queryParametersToKeyValue(query.parameters)
if (query && !query.transformer) {
query.transformer = "return data"
}
@ -168,11 +180,21 @@
/>
</div>
<div class="url">
<Input bind:value={url} />
<Input bind:value={url} placeholder="http://www.api.com/endpoint" />
</div>
<Button cta disabled={!url} on:click={runQuery}>Send</Button>
</div>
<Tabs selected="Params" quiet noPadding noHorizPadding>
<Tabs selected="Bindings" quiet noPadding noHorizPadding>
<Tab title="Bindings">
<KeyValueBuilder
bind:object={bindings}
tooltip="Set the name of the binding which can be used in Handlebars statements throughout your query"
name="binding"
headings
keyPlaceholder="Binding name"
valuePlaceholder="Default"
/>
</Tab>
<Tab title="Params">
<KeyValueBuilder bind:object={breakQs} name="param" headings />
</Tab>
@ -219,29 +241,52 @@
</Tabs>
</Layout>
</div>
<div class="bottom">
<Layout paddingY="S" gap="S">
<Divider size="S" />
{#if !response}
{#if !response && Object.keys(schema).length === 0}
<Heading size="M">Response</Heading>
<div class="placeholder">
<div class="placeholder-internal">
<img alt="placeholder" src={Placeholder} />
<Body size="XS" textAlign="center"
>{"enter a url in the textbox above and click send to get a response".toUpperCase()}</Body
>
</div>
</div>
{:else}
<Tabs selected="JSON" quiet noPadding noHorizPadding>
<Tabs
selected={!response ? "Schema" : "JSON"}
quiet
noPadding
noHorizPadding
>
{#if response}
<Tab title="JSON">
<div>
<JSONPreview height="300" data={response.rows[0]} />
</div>
</Tab>
{/if}
{#if schema || response}
<Tab title="Schema">
<KeyValueBuilder
bind:object={response.schema}
name="header"
bind:object={schema}
name="schema"
headings
options={SchemaTypeOptions}
/>
</Tab>
{/if}
{#if response}
<Tab title="Raw">
<TextArea disabled value={response.raw} height="300" />
<TextArea disabled value={response.extra?.raw} height="300" />
</Tab>
<Tab title="Headers">
<KeyValueBuilder object={response.extra?.headers} readOnly />
</Tab>
<Tab title="Preview">
<div class="table">
{#if response}
<Table
schema={response?.schema}
@ -251,6 +296,7 @@
allowSelectRows={false}
/>
{/if}
</div>
</Tab>
<div class="stats">
<Label size="L">
@ -272,10 +318,12 @@
>Save query</Button
>
</div>
{/if}
</Tabs>
{/if}
</Layout>
</div>
</div>
{/if}
<style>
@ -284,6 +332,9 @@
margin: 0 auto;
height: 100%;
}
.table {
width: 960px;
}
.url-block {
display: flex;
gap: var(--spacing-s);
@ -297,6 +348,9 @@
.top {
min-height: 50%;
}
.bottom {
padding-bottom: 50px;
}
.stats {
display: flex;
gap: var(--spacing-xl);
@ -319,4 +373,15 @@
gap: var(--spacing-m);
align-items: center;
}
.placeholder-internal {
display: flex;
flex-direction: column;
width: 200px;
gap: var(--spacing-l);
}
.placeholder {
display: flex;
margin-top: var(--spacing-xl);
justify-content: center;
}
</style>

View File

@ -6,6 +6,9 @@ module FetchMock {
return {
status,
headers: {
raw: () => {
return { "content-type": ["application/json"] }
},
get: () => {
return ["application/json"]
},

View File

@ -123,7 +123,7 @@ async function enrichQueryFields(fields, parameters = {}) {
enrichedQuery.requestBody
)
} catch (err) {
throw { message: `JSON Invalid - error: ${err}` }
// no json found, ignore
}
delete enrichedQuery.customData
}
@ -151,7 +151,7 @@ exports.preview = async function (ctx) {
const enrichedQuery = await enrichQueryFields(fields, parameters)
try {
const { rows, keys, info, raw } = await Runner.run({
const { rows, keys, info, extra } = await Runner.run({
datasource,
queryVerb,
query: enrichedQuery,
@ -162,7 +162,7 @@ exports.preview = async function (ctx) {
rows,
schemaFields: [...new Set(keys)],
info,
raw,
extra,
}
} catch (err) {
ctx.throw(400, err)

View File

@ -19,6 +19,7 @@ exports.queryValidation = () => {
extra: Joi.object().optional(),
schema: Joi.object({}).required().unknown(true),
transformer: Joi.string().optional(),
flags: Joi.object().optional(),
})
}

View File

@ -62,6 +62,18 @@ module RestModule {
const { formatBytes } = require("../utilities")
const { performance } = require("perf_hooks")
interface RestQuery {
path: string
queryString?: string
headers: { [key: string]: any }
enabledHeaders: { [key: string]: any }
requestBody: any
bodyType: string
json: object
method: string
authConfigId: string
}
interface RestConfig {
url: string
defaultHeaders: {
@ -70,13 +82,6 @@ module RestModule {
authConfigs: AuthConfig[]
}
interface Request {
path: string
queryString?: string
headers?: string
json?: any
}
const SCHEMA: Integration = {
docs: "https://github.com/node-fetch/node-fetch",
description:
@ -140,7 +145,7 @@ module RestModule {
}
async parseResponse(response: any) {
let data, raw
let data, raw, headers
const contentType = response.headers.get("content-type")
if (contentType && contentType.indexOf("application/json") !== -1) {
data = await response.json()
@ -149,8 +154,12 @@ module RestModule {
data = await response.text()
raw = data
}
const size = formatBytes(response.headers.get("content-length") || 0)
const size = formatBytes(response.headers.get("content-length") || Buffer.byteLength(raw, "utf8"))
const time = `${Math.round(performance.now() - this.startTimeMs)}ms`
headers = response.headers.raw()
for (let [key, value] of Object.entries(headers)) {
headers[key] = Array.isArray(value) ? value[0] : value
}
return {
data,
info: {
@ -158,23 +167,26 @@ module RestModule {
size,
time,
},
extra: {
raw,
headers,
},
}
}
getUrl(path: string, queryString: string): string {
const main = `${path}?${queryString}`
if (!this.config.url) {
return main
} else {
return `${this.config.url}/${main}`
let complete = !this.config.url ? main : `${this.config.url}/${main}`
if (!complete.startsWith("http")) {
complete = `http://${complete}`
}
return complete
}
processAuth(authConfigId: string) {
if (!this.config.authConfigs || !authConfigId) {
return
}
getAuthHeaders(authConfigId: string): { [key: string]: any }{
let headers: any = {}
if (this.config.authConfigs && authConfigId) {
const authConfig = this.config.authConfigs.filter(
c => c._id === authConfigId
)[0]
@ -182,31 +194,47 @@ module RestModule {
switch (authConfig.type) {
case AuthType.BASIC:
config = authConfig.config as BasicAuthConfig
this.headers.Authorization = `Basic ${Buffer.from(
headers.Authorization = `Basic ${Buffer.from(
`${config.username}:${config.password}`
).toString("base64")}`
break
case AuthType.BEARER:
config = authConfig.config as BearerAuthConfig
this.headers.Authorization = `Bearer ${config.token}`
headers.Authorization = `Bearer ${config.token}`
break
}
}
async _req({
path = "",
queryString = "",
headers = {},
json = {},
method = "GET",
authConfigId = "",
}) {
return headers
}
async _req(query: RestQuery) {
const { path = "", queryString = "", headers = {}, method = "GET", enabledHeaders, bodyType, requestBody, authConfigId } = query
const authHeaders = this.getAuthHeaders(authConfigId)
this.headers = {
...this.config.defaultHeaders,
...headers,
...authHeaders,
}
this.processAuth(authConfigId)
if (enabledHeaders) {
for (let headerKey of Object.keys(this.headers)) {
if (!enabledHeaders[headerKey]) {
delete this.headers[headerKey]
}
}
}
let json
if (bodyType === BodyTypes.JSON && requestBody) {
try {
json = JSON.parse(requestBody)
} catch (err) {
throw "Invalid JSON for request body"
}
}
const input: any = { method, headers: this.headers }
if (json && typeof json === "object" && Object.keys(json).length > 0) {
@ -218,23 +246,23 @@ module RestModule {
return await this.parseResponse(response)
}
async create(opts: Request) {
async create(opts: RestQuery) {
return this._req({ ...opts, method: "POST" })
}
async read(opts: Request) {
async read(opts: RestQuery) {
return this._req({ ...opts, method: "GET" })
}
async update(opts: Request) {
async update(opts: RestQuery) {
return this._req({ ...opts, method: "PUT" })
}
async patch(opts: Request) {
async patch(opts: RestQuery) {
return this._req({ ...opts, method: "PATCH" })
}
async delete(opts: Request) {
async delete(opts: RestQuery) {
return this._req({ ...opts, method: "DELETE" })
}
}

View File

@ -1,6 +1,9 @@
jest.mock("node-fetch", () =>
jest.fn(() => ({
headers: {
raw: () => {
return { "content-type": ["application/json"] }
},
get: () => ["application/json"]
},
json: jest.fn(),
@ -25,6 +28,7 @@ describe("REST Integration", () => {
config = new TestConfiguration({
url: BASE_URL,
})
jest.clearAllMocks()
})
it("calls the create method with the correct params", async () => {
@ -34,9 +38,10 @@ describe("REST Integration", () => {
headers: {
Accept: "application/json",
},
json: {
bodyType: "json",
requestBody: JSON.stringify({
name: "test",
},
}),
}
const response = await config.integration.create(query)
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
@ -61,6 +66,7 @@ describe("REST Integration", () => {
headers: {
Accept: "text/html",
},
method: "GET",
})
})
@ -71,13 +77,14 @@ describe("REST Integration", () => {
headers: {
Accept: "application/json",
},
json: {
bodyType: "json",
requestBody: JSON.stringify({
name: "test",
},
}),
}
const response = await config.integration.update(query)
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
method: "POST",
method: "PUT",
body: '{"name":"test"}',
headers: {
Accept: "application/json",
@ -92,9 +99,10 @@ describe("REST Integration", () => {
headers: {
Accept: "application/json",
},
json: {
bodyType: "json",
requestBody: JSON.stringify({
name: "test",
},
}),
}
const response = await config.integration.delete(query)
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
@ -102,6 +110,7 @@ describe("REST Integration", () => {
headers: {
Accept: "application/json",
},
body: '{"name":"test"}',
})
})

View File

@ -19,8 +19,8 @@ function hasExtraData(response) {
return (
typeof response === "object" &&
!Array.isArray(response) &&
response.data &&
response.info
response.data != null &&
response.info != null
)
}
@ -34,11 +34,11 @@ async function runAndTransform(datasource, queryVerb, query, transformer) {
let output = formatResponse(await integration[queryVerb](query))
let rows = output,
info = undefined,
raw = undefined
extra = undefined
if (hasExtraData(output)) {
rows = output.data
info = output.info
raw = output.raw
extra = output.extra
}
// transform as required
@ -64,7 +64,7 @@ async function runAndTransform(datasource, queryVerb, query, transformer) {
integration.end()
}
return { rows, keys, info, raw }
return { rows, keys, info, extra }
}
module.exports = (input, callback) => {