@budibase/bbui 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/LICENSE +375 -0
  2. package/README.md +42 -0
  3. package/dist/bbui.es.js +8 -0
  4. package/dist/bbui.es.js.map +1 -0
  5. package/package.json +94 -0
  6. package/src/Accordion/Accordion.svelte +58 -0
  7. package/src/ActionButton/ActionButton.svelte +132 -0
  8. package/src/ActionGroup/ActionGroup.svelte +26 -0
  9. package/src/ActionMenu/ActionMenu.svelte +43 -0
  10. package/src/Actions/ClickOutside.svench.svx +30 -0
  11. package/src/Actions/PositionDropdown.svench.svx +81 -0
  12. package/src/Actions/autoresize_textarea.js +14 -0
  13. package/src/Actions/click_outside.js +76 -0
  14. package/src/Actions/position_dropdown.js +116 -0
  15. package/src/Avatar/Avatar.svelte +63 -0
  16. package/src/Badge/Badge.svelte +42 -0
  17. package/src/Banner/Banner.svelte +72 -0
  18. package/src/Banner/BannerDisplay.svelte +41 -0
  19. package/src/Button/Button.svelte +118 -0
  20. package/src/Button/Button.svench +138 -0
  21. package/src/ButtonGroup/ButtonGroup.svelte +30 -0
  22. package/src/ClearButton/ClearButton.svelte +19 -0
  23. package/src/ColorPicker/ColorPicker.svelte +304 -0
  24. package/src/DetailSummary/DetailSummary.svelte +92 -0
  25. package/src/Divider/Divider.svelte +28 -0
  26. package/src/Drawer/Drawer.svelte +113 -0
  27. package/src/Drawer/Drawer.svench +42 -0
  28. package/src/Drawer/DrawerContent.svelte +45 -0
  29. package/src/FancyForm/FancyButton.svelte +30 -0
  30. package/src/FancyForm/FancyButtonRadio.svelte +70 -0
  31. package/src/FancyForm/FancyCheckbox.svelte +53 -0
  32. package/src/FancyForm/FancyField.svelte +126 -0
  33. package/src/FancyForm/FancyFieldLabel.svelte +25 -0
  34. package/src/FancyForm/FancyForm.svelte +40 -0
  35. package/src/FancyForm/FancyInput.svelte +109 -0
  36. package/src/FancyForm/FancySelect.svelte +147 -0
  37. package/src/FancyForm/index.js +6 -0
  38. package/src/Form/Checkbox.svelte +23 -0
  39. package/src/Form/Combobox.svelte +45 -0
  40. package/src/Form/Core/Checkbox.svelte +69 -0
  41. package/src/Form/Core/CheckboxGroup.svelte +68 -0
  42. package/src/Form/Core/Combobox.svelte +142 -0
  43. package/src/Form/Core/DatePicker.svelte +265 -0
  44. package/src/Form/Core/Dropzone.svelte +482 -0
  45. package/src/Form/Core/EnvDropdown.svelte +276 -0
  46. package/src/Form/Core/File.svelte +115 -0
  47. package/src/Form/Core/InputDropdown.svelte +235 -0
  48. package/src/Form/Core/Multiselect.svelte +99 -0
  49. package/src/Form/Core/Picker.svelte +314 -0
  50. package/src/Form/Core/PickerDropdown.svelte +431 -0
  51. package/src/Form/Core/RadioGroup.svelte +65 -0
  52. package/src/Form/Core/RichTextField.svelte +42 -0
  53. package/src/Form/Core/Search.svelte +105 -0
  54. package/src/Form/Core/Select.svelte +90 -0
  55. package/src/Form/Core/Slider.svelte +86 -0
  56. package/src/Form/Core/Stepper.svelte +172 -0
  57. package/src/Form/Core/Switch.svelte +35 -0
  58. package/src/Form/Core/TextArea.svelte +78 -0
  59. package/src/Form/Core/TextField.svelte +115 -0
  60. package/src/Form/Core/index.js +16 -0
  61. package/src/Form/DatePicker.svelte +46 -0
  62. package/src/Form/Dropzone.svelte +44 -0
  63. package/src/Form/EnvDropdown.svelte +50 -0
  64. package/src/Form/Field.svelte +42 -0
  65. package/src/Form/FieldLabel.svelte +32 -0
  66. package/src/Form/File.svelte +37 -0
  67. package/src/Form/Input.svelte +45 -0
  68. package/src/Form/InputDropdown.svelte +53 -0
  69. package/src/Form/Multiselect.svelte +49 -0
  70. package/src/Form/PickerDropdown.svelte +131 -0
  71. package/src/Form/RadioGroup.svelte +42 -0
  72. package/src/Form/RichTextField.svelte +36 -0
  73. package/src/Form/Search.svelte +36 -0
  74. package/src/Form/Select.svelte +67 -0
  75. package/src/Form/Slider.svelte +24 -0
  76. package/src/Form/Stepper.svelte +45 -0
  77. package/src/Form/TextArea.svelte +34 -0
  78. package/src/Form/Toggle.svelte +22 -0
  79. package/src/Icon/Icon.svelte +94 -0
  80. package/src/Icon/IconAvatar.svelte +61 -0
  81. package/src/IconPicker/IconPicker.svelte +184 -0
  82. package/src/IconSideNav/IconSideNav.svelte +14 -0
  83. package/src/IconSideNav/IconSideNavItem.svelte +56 -0
  84. package/src/InlineAlert/InlineAlert.svelte +63 -0
  85. package/src/Input/CopyInput.svelte +59 -0
  86. package/src/Label/Label.svelte +31 -0
  87. package/src/Layout/Layout.svelte +94 -0
  88. package/src/Layout/Page.svelte +101 -0
  89. package/src/Link/Link.svelte +27 -0
  90. package/src/List/List.svelte +28 -0
  91. package/src/List/ListItem.svelte +93 -0
  92. package/src/Markdown/MarkdownEditor.svelte +60 -0
  93. package/src/Markdown/MarkdownViewer.svelte +70 -0
  94. package/src/Markdown/SpectrumMDE.svelte +184 -0
  95. package/src/Menu/Item.svelte +94 -0
  96. package/src/Menu/Menu.svelte +7 -0
  97. package/src/Menu/Menu.svench +19 -0
  98. package/src/Menu/Section.svelte +10 -0
  99. package/src/Menu/Separator.svelte +1 -0
  100. package/src/Modal/Content.svelte +6 -0
  101. package/src/Modal/CustomContent.svelte +37 -0
  102. package/src/Modal/Modal.svelte +178 -0
  103. package/src/Modal/Modal.svench +116 -0
  104. package/src/Modal/ModalContent.svelte +190 -0
  105. package/src/Modal/QuizModal.svelte +53 -0
  106. package/src/Notification/Notification.svelte +74 -0
  107. package/src/Notification/NotificationDisplay.svelte +43 -0
  108. package/src/Pagination/Pagination.svelte +57 -0
  109. package/src/Popover/Popover.svelte +95 -0
  110. package/src/Popover/Popover.svench +120 -0
  111. package/src/ProgressBar/ProgressBar.svelte +67 -0
  112. package/src/ProgressCircle/ProgressCircle.svelte +62 -0
  113. package/src/SideNavigation/Item.svelte +59 -0
  114. package/src/SideNavigation/Navigation.svelte +12 -0
  115. package/src/StatusLight/StatusLight.svelte +80 -0
  116. package/src/Stores/Notifications.svench.svx +78 -0
  117. package/src/Stores/banner.js +65 -0
  118. package/src/Stores/notifications.js +84 -0
  119. package/src/Table/ArrayRenderer.svelte +18 -0
  120. package/src/Table/AttachmentRenderer.svelte +73 -0
  121. package/src/Table/BoldRenderer.svelte +15 -0
  122. package/src/Table/BooleanRenderer.svelte +42 -0
  123. package/src/Table/CellRenderer.svelte +70 -0
  124. package/src/Table/CodeRenderer.svelte +14 -0
  125. package/src/Table/DateTimeRenderer.svelte +29 -0
  126. package/src/Table/InternalRenderer.svelte +24 -0
  127. package/src/Table/RelationshipRenderer.svelte +35 -0
  128. package/src/Table/SelectEditRenderer.svelte +28 -0
  129. package/src/Table/StringRenderer.svelte +22 -0
  130. package/src/Table/Table.svelte +657 -0
  131. package/src/Table/index.js +1 -0
  132. package/src/Tabs/Tab.svelte +88 -0
  133. package/src/Tabs/Tabs.svelte +130 -0
  134. package/src/Tags/Tag.svelte +43 -0
  135. package/src/Tags/Tags.svelte +17 -0
  136. package/src/Tooltip/Tooltip.svelte +34 -0
  137. package/src/Tooltip/TooltipWrapper.svelte +60 -0
  138. package/src/TreeView/Item.svelte +41 -0
  139. package/src/TreeView/Tree.svelte +16 -0
  140. package/src/Typography/Body.svelte +22 -0
  141. package/src/Typography/Code.svelte +10 -0
  142. package/src/Typography/Detail.svelte +19 -0
  143. package/src/Typography/Heading.svelte +24 -0
  144. package/src/bbui.css +118 -0
  145. package/src/context.js +4 -0
  146. package/src/helpers.js +116 -0
  147. package/src/index.js +109 -0
  148. package/src/spectrum-icons-rollup.js +31 -0
  149. package/src/spectrum-icons-vite.js +31 -0
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@budibase/bbui",
3
+ "description": "A UI solution used in the different Budibase projects.",
4
+ "version": "0.0.1",
5
+ "license": "MPL-2.0",
6
+ "svelte": "src/index.js",
7
+ "module": "dist/bbui.es.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/bbui.es.js"
11
+ },
12
+ "./package.json": "./package.json",
13
+ "./spectrum-icons-rollup.js": "./src/spectrum-icons-rollup.js",
14
+ "./spectrum-icons-vite.js": "./src/spectrum-icons-vite.js"
15
+ },
16
+ "scripts": {
17
+ "build": "rollup -c"
18
+ },
19
+ "devDependencies": {
20
+ "@rollup/plugin-commonjs": "^16.0.0",
21
+ "@rollup/plugin-json": "^4.1.0",
22
+ "@rollup/plugin-node-resolve": "^11.2.1",
23
+ "cross-env": "^7.0.2",
24
+ "nollup": "^0.14.1",
25
+ "postcss": "^8.2.9",
26
+ "rollup": "^2.45.2",
27
+ "rollup-plugin-postcss": "^4.0.0",
28
+ "rollup-plugin-svelte": "^7.1.0",
29
+ "rollup-plugin-terser": "^7.0.2",
30
+ "svelte": "^3.38.2"
31
+ },
32
+ "keywords": [
33
+ "svelte"
34
+ ],
35
+ "files": [
36
+ "src",
37
+ "dist"
38
+ ],
39
+ "dependencies": {
40
+ "@adobe/spectrum-css-workflow-icons": "1.2.1",
41
+ "@budibase/shared-core": "0.0.1",
42
+ "@budibase/string-templates": "0.0.1",
43
+ "@spectrum-css/accordion": "3.0.24",
44
+ "@spectrum-css/actionbutton": "1.0.1",
45
+ "@spectrum-css/actiongroup": "1.0.1",
46
+ "@spectrum-css/avatar": "3.0.2",
47
+ "@spectrum-css/button": "3.0.1",
48
+ "@spectrum-css/buttongroup": "3.0.2",
49
+ "@spectrum-css/checkbox": "3.0.2",
50
+ "@spectrum-css/dialog": "3.0.1",
51
+ "@spectrum-css/divider": "1.0.3",
52
+ "@spectrum-css/dropzone": "3.0.2",
53
+ "@spectrum-css/fieldgroup": "3.0.2",
54
+ "@spectrum-css/fieldlabel": "3.0.1",
55
+ "@spectrum-css/icon": "3.0.1",
56
+ "@spectrum-css/illustratedmessage": "3.0.2",
57
+ "@spectrum-css/inlinealert": "2.0.1",
58
+ "@spectrum-css/inputgroup": "3.0.2",
59
+ "@spectrum-css/label": "2.0.10",
60
+ "@spectrum-css/link": "3.1.1",
61
+ "@spectrum-css/menu": "3.0.1",
62
+ "@spectrum-css/modal": "3.0.1",
63
+ "@spectrum-css/pagination": "3.0.3",
64
+ "@spectrum-css/picker": "1.0.1",
65
+ "@spectrum-css/popover": "3.0.1",
66
+ "@spectrum-css/progressbar": "1.0.2",
67
+ "@spectrum-css/progresscircle": "1.0.2",
68
+ "@spectrum-css/radio": "3.0.2",
69
+ "@spectrum-css/search": "3.0.2",
70
+ "@spectrum-css/sidenav": "3.0.2",
71
+ "@spectrum-css/slider": "3.0.1",
72
+ "@spectrum-css/statuslight": "3.0.2",
73
+ "@spectrum-css/stepper": "3.0.3",
74
+ "@spectrum-css/switch": "1.0.2",
75
+ "@spectrum-css/table": "3.0.1",
76
+ "@spectrum-css/tabs": "3.2.12",
77
+ "@spectrum-css/tags": "3.0.2",
78
+ "@spectrum-css/textfield": "3.0.1",
79
+ "@spectrum-css/toast": "3.0.1",
80
+ "@spectrum-css/tooltip": "3.0.3",
81
+ "@spectrum-css/treeview": "3.0.2",
82
+ "@spectrum-css/typography": "3.0.1",
83
+ "@spectrum-css/underlay": "2.0.9",
84
+ "@spectrum-css/vars": "3.0.1",
85
+ "dayjs": "^1.10.4",
86
+ "easymde": "^2.16.1",
87
+ "svelte-flatpickr": "^3.3.2",
88
+ "svelte-portal": "^1.0.0"
89
+ },
90
+ "resolutions": {
91
+ "loader-utils": "1.4.1"
92
+ },
93
+ "gitHead": "365911a77f6365818fd82fe8e2fe7e2b5d9fc1b3"
94
+ }
@@ -0,0 +1,58 @@
1
+ <script>
2
+ import "@spectrum-css/accordion"
3
+
4
+ export let itemName
5
+ export let initialOpen
6
+ export let header
7
+
8
+ let isOpen
9
+
10
+ function getOpenClass(isOpen) {
11
+ if (isOpen === undefined) {
12
+ isOpen = initialOpen
13
+ }
14
+ return isOpen ? "is-open" : ""
15
+ }
16
+ </script>
17
+
18
+ <div class="spectrum-Accordion" role={itemName}>
19
+ <div class="spectrum-Accordion-item {getOpenClass(isOpen)}">
20
+ <h3 class="spectrum-Accordion-itemHeading">
21
+ <button
22
+ class="spectrum-Accordion-itemHeader"
23
+ type="button"
24
+ on:click={() => (isOpen = !isOpen)}
25
+ >
26
+ {header}
27
+ </button>
28
+ <svg
29
+ class="spectrum-Icon spectrum-UIIcon-ChevronRight100 spectrum-Accordion-itemIndicator"
30
+ focusable="false"
31
+ aria-hidden="true"
32
+ >
33
+ <use xlink:href="#spectrum-css-icon-Chevron100" />
34
+ </svg>
35
+ </h3>
36
+ <div class="spectrum-Accordion-itemContent" role={itemName}>
37
+ <slot />
38
+ </div>
39
+ </div>
40
+ </div>
41
+
42
+ <style>
43
+ .spectrum-Accordion {
44
+ margin-left: -20px;
45
+ }
46
+ .spectrum-Accordion-item {
47
+ border: none;
48
+ }
49
+ .spectrum-Accordion-itemContent {
50
+ width: 97%;
51
+ padding-left: 30px;
52
+ }
53
+ .spectrum-Accordion-itemHeader {
54
+ text-transform: none;
55
+ font-weight: bold;
56
+ font-size: 0.875rem;
57
+ }
58
+ </style>
@@ -0,0 +1,132 @@
1
+ <script>
2
+ import "@spectrum-css/actionbutton/dist/index-vars.css"
3
+ import { createEventDispatcher } from "svelte"
4
+ import Tooltip from "../Tooltip/Tooltip.svelte"
5
+ import { fade } from "svelte/transition"
6
+
7
+ const dispatch = createEventDispatcher()
8
+
9
+ export let quiet = false
10
+ export let emphasized = false
11
+ export let selected = false
12
+ export let longPressable = false
13
+ export let disabled = false
14
+ export let icon = ""
15
+ export let size = "M"
16
+ export let active = false
17
+ export let fullWidth = false
18
+ export let noPadding = false
19
+ export let tooltip = ""
20
+
21
+ let showTooltip = false
22
+
23
+ function longPress(element) {
24
+ if (!longPressable) return
25
+ let timer
26
+
27
+ const listener = () => {
28
+ timer = setTimeout(() => {
29
+ dispatch("longpress")
30
+ }, 700)
31
+ }
32
+
33
+ element.addEventListener("pointerdown", listener)
34
+
35
+ return {
36
+ destroy() {
37
+ clearTimeout(timer)
38
+ element.removeEventListener("pointerdown", longPress)
39
+ },
40
+ }
41
+ }
42
+ </script>
43
+
44
+ <span
45
+ class="btn-wrap"
46
+ on:mouseover={() => (showTooltip = true)}
47
+ on:mouseleave={() => (showTooltip = false)}
48
+ on:focus={() => (showTooltip = true)}
49
+ >
50
+ <button
51
+ use:longPress
52
+ class:spectrum-ActionButton--quiet={quiet}
53
+ class:spectrum-ActionButton--emphasized={emphasized}
54
+ class:is-selected={selected}
55
+ class:noPadding
56
+ class:fullWidth
57
+ class="spectrum-ActionButton spectrum-ActionButton--size{size}"
58
+ class:active
59
+ {disabled}
60
+ on:longPress
61
+ on:click|preventDefault
62
+ >
63
+ {#if longPressable}
64
+ <svg
65
+ class="spectrum-Icon spectrum-UIIcon-CornerTriangle100 spectrum-ActionButton-hold"
66
+ focusable="false"
67
+ aria-hidden="true"
68
+ >
69
+ <use xlink:href="#spectrum-css-icon-CornerTriangle100" />
70
+ </svg>
71
+ {/if}
72
+ {#if icon}
73
+ <svg
74
+ class="spectrum-Icon spectrum-Icon--sizeS"
75
+ focusable="false"
76
+ aria-hidden="true"
77
+ aria-label={icon}
78
+ >
79
+ <use xlink:href="#spectrum-icon-18-{icon}" />
80
+ </svg>
81
+ {/if}
82
+ {#if $$slots}
83
+ <span class="spectrum-ActionButton-label"><slot /></span>
84
+ {/if}
85
+ {#if tooltip && showTooltip}
86
+ <div class="tooltip" in:fade={{ duration: 130, delay: 250 }}>
87
+ <Tooltip textWrapping direction="bottom" text={tooltip} />
88
+ </div>
89
+ {/if}
90
+ </button>
91
+ </span>
92
+
93
+ <style>
94
+ .fullWidth {
95
+ width: 100%;
96
+ }
97
+ .active,
98
+ .active svg {
99
+ color: var(--spectrum-global-color-blue-600);
100
+ }
101
+ :global([dir="ltr"] .spectrum-ActionButton .spectrum-Icon) {
102
+ margin-left: 0;
103
+ transition: color ease-out 130ms;
104
+ }
105
+ .is-selected:not(.spectrum-ActionButton--emphasized):not(.spectrum-ActionButton--quiet) {
106
+ background: var(--spectrum-global-color-gray-300);
107
+ border-color: var(--spectrum-global-color-gray-500);
108
+ }
109
+ .noPadding {
110
+ padding: 0;
111
+ min-width: 0;
112
+ }
113
+ .spectrum-ActionButton--quiet {
114
+ padding: 0 8px;
115
+ }
116
+ .spectrum-ActionButton--quiet.is-selected {
117
+ color: var(--spectrum-global-color-gray-900);
118
+ }
119
+ .is-selected:not(.emphasized) .spectrum-Icon {
120
+ color: var(--spectrum-global-color-gray-900);
121
+ }
122
+ .tooltip {
123
+ position: absolute;
124
+ pointer-events: none;
125
+ left: 50%;
126
+ top: calc(100% + 4px);
127
+ width: 100vw;
128
+ max-width: 150px;
129
+ transform: translateX(-50%);
130
+ text-align: center;
131
+ }
132
+ </style>
@@ -0,0 +1,26 @@
1
+ <script>
2
+ import "@spectrum-css/actiongroup/dist/index-vars.css"
3
+ export let vertical = false
4
+ export let justified = false
5
+ export let quiet = false
6
+ export let compact = false
7
+
8
+ // Attaches a spectrum-ActionGroup-item class to buttons inside the div
9
+ function group(element) {
10
+ const buttons = Array.from(element.getElementsByTagName("button"))
11
+ buttons.forEach(button => {
12
+ button.classList.add("spectrum-ActionGroup-item")
13
+ })
14
+ }
15
+ </script>
16
+
17
+ <div
18
+ use:group
19
+ class:spectrum-ActionGroup--vertical={vertical}
20
+ class:spectrum-ActionGroup--justified={justified}
21
+ class:spectrum-ActionGroup--quiet={quiet}
22
+ class:spectrum-ActionGroup--compact={compact}
23
+ class="spectrum-ActionGroup"
24
+ >
25
+ <slot />
26
+ </div>
@@ -0,0 +1,43 @@
1
+ <script>
2
+ import { setContext } from "svelte"
3
+ import Popover from "../Popover/Popover.svelte"
4
+ import Menu from "../Menu/Menu.svelte"
5
+
6
+ export let disabled = false
7
+ export let align = "left"
8
+ export let portalTarget
9
+
10
+ let anchor
11
+ let dropdown
12
+
13
+ // This is needed because display: contents is considered "invisible".
14
+ // It should only ever be an action button, so should be fine.
15
+ function getAnchor(node) {
16
+ anchor = node.firstChild
17
+ }
18
+
19
+ export const hide = () => {
20
+ dropdown.hide()
21
+ }
22
+ export const show = () => {
23
+ dropdown.show()
24
+ }
25
+
26
+ const openMenu = event => {
27
+ if (!disabled) {
28
+ event.stopPropagation()
29
+ show()
30
+ }
31
+ }
32
+
33
+ setContext("actionMenu", { show, hide })
34
+ </script>
35
+
36
+ <div use:getAnchor on:click={openMenu}>
37
+ <slot name="control" />
38
+ </div>
39
+ <Popover bind:this={dropdown} {anchor} {align} {portalTarget}>
40
+ <Menu>
41
+ <slot />
42
+ </Menu>
43
+ </Popover>
@@ -0,0 +1,30 @@
1
+ <script>
2
+ import { View } from "svench";
3
+ import Multiselect from "../Form/Multiselect.svelte";
4
+ import DropdownMenu from "../DropdownMenu/DropdownMenu.svelte";
5
+ import Button from "../Button/Button.svelte";
6
+ import Icon from "../Icons/Icon.svelte";
7
+
8
+ const options = ["Red", "Blue", "Yellow", "Green", "Pink", "Very long color name to show text wrapping"];
9
+
10
+ let anchorLeft;
11
+ let dropdownLeft;
12
+ </script>
13
+
14
+ ### Click Outside Action
15
+
16
+ This action can be used to listen for when you want to call a function whenever a user clicks outside of the element it is applied toString.
17
+
18
+
19
+ ```html
20
+ <script>
21
+ import { clickOutside } from '@budibase/bbui'
22
+ const someFunction = () => {
23
+ // Some logic to close a dropdown or stop the user from doing something without saving?
24
+ }
25
+ </script>
26
+
27
+ <button use:clickOutside={someFunction} class="multiselect" bind:this={anchor}>
28
+ Clicking this opens the
29
+ </button>
30
+ ```
@@ -0,0 +1,81 @@
1
+ <script>
2
+ import { View } from "svench";
3
+ import Multiselect from "../Form/Multiselect.svelte";
4
+ import DropdownMenu from "../DropdownMenu/DropdownMenu.svelte";
5
+ import Button from "../Button/Button.svelte";
6
+ import Icon from "../Icons/Icon.svelte";
7
+
8
+ const options = ["Red", "Blue", "Yellow", "Green", "Pink", "Very long color name to show text wrapping"];
9
+
10
+ let anchorLeft;
11
+ let dropdownLeft;
12
+ </script>
13
+
14
+ ### Position Dropdown Action
15
+
16
+ This action positions an element close to it's anchor, either above or below it, depending on the amount of space that exists. There's also an option to align the dropdown to the right instead of the left of the anchor. An example of how to use it follows:
17
+
18
+ ```html
19
+ <script>
20
+ let visible = false;
21
+ let anchor;
22
+ let align = 'right';
23
+ </script>
24
+
25
+ <button on:click={() => visible = !visible} class="multiselect" bind:this={anchor}>
26
+ Clicking this opens the
27
+ </button>
28
+
29
+ {#if visible}
30
+ <Portal>
31
+ <div use:positionDropdown={{anchor, align}}>
32
+ Some content here.
33
+ </div>
34
+ </Portal>
35
+ {/if}
36
+ ```
37
+
38
+ Here are some components that currently use this action:
39
+
40
+ <View name="Multiselect Example">
41
+ <Multiselect name="Test" label="Colours" placeholder="Choose some colours">
42
+ {#each options as option}
43
+ <option value={option}>{option}</option>
44
+ {/each}
45
+ </Multiselect>
46
+ </View>
47
+
48
+ <View name="Dropdown on close event example">
49
+ <div bind:this={anchorLeft}>
50
+ <Button primary on:click={dropdownLeft.show}>
51
+ Field Name
52
+ <Icon name="arrowdown" />
53
+ </Button>
54
+ </div>
55
+ <DropdownMenu
56
+ on:close={() => alert('Closed!')}
57
+ bind:this={dropdownLeft}
58
+ width="175px"
59
+ borderColor="#d1d1d1ff"
60
+ anchor={anchorLeft}
61
+ align="left">
62
+ <ul>
63
+ <li>
64
+ <Icon name="edit" />
65
+ Edit
66
+ </li>
67
+ <li>
68
+ <Icon name="delete" />
69
+ Delete
70
+ </li>
71
+ <li>
72
+ <Icon name="sortascending" />
73
+ Sort A - Z
74
+ </li>
75
+ <li>
76
+ <Icon name="sortdescending" />
77
+ Sort Z - A
78
+ </li>
79
+ </ul>
80
+ </DropdownMenu>
81
+ </View>
@@ -0,0 +1,14 @@
1
+ function resize({ target }) {
2
+ target.style.height = "1px"
3
+ target.style.height = +target.scrollHeight + "px"
4
+ }
5
+
6
+ export default function text_area_resize(el) {
7
+ resize({ target: el })
8
+ el.style.overflow = "hidden"
9
+ el.addEventListener("input", resize)
10
+
11
+ return {
12
+ destroy: () => el.removeEventListener("input", resize),
13
+ }
14
+ }
@@ -0,0 +1,76 @@
1
+ const ignoredClasses = [".flatpickr-calendar", ".spectrum-Popover"]
2
+ let clickHandlers = []
3
+
4
+ /**
5
+ * Handle a body click event
6
+ */
7
+ const handleClick = event => {
8
+ // Ignore click if this is an ignored class
9
+ if (event.target.closest('[data-ignore-click-outside="true"]')) {
10
+ return
11
+ }
12
+ for (let className of ignoredClasses) {
13
+ if (event.target.closest(className)) {
14
+ return
15
+ }
16
+ }
17
+
18
+ // Process handlers
19
+ clickHandlers.forEach(handler => {
20
+ if (handler.element.contains(event.target)) {
21
+ return
22
+ }
23
+
24
+ // Ignore clicks for modals, unless the handler is registered from a modal
25
+ const sourceInModal = handler.anchor.closest(".spectrum-Modal") != null
26
+ const clickInModal = event.target.closest(".spectrum-Modal") != null
27
+ if (clickInModal && !sourceInModal) {
28
+ return
29
+ }
30
+
31
+ handler.callback?.(event)
32
+ })
33
+ }
34
+ document.documentElement.addEventListener("click", handleClick, true)
35
+ document.documentElement.addEventListener("contextmenu", handleClick, true)
36
+
37
+ /**
38
+ * Adds or updates a click handler
39
+ */
40
+ const updateHandler = (id, element, anchor, callback) => {
41
+ let existingHandler = clickHandlers.find(x => x.id === id)
42
+ if (!existingHandler) {
43
+ clickHandlers.push({ id, element, anchor, callback })
44
+ } else {
45
+ existingHandler.callback = callback
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Removes a click handler
51
+ */
52
+ const removeHandler = id => {
53
+ clickHandlers = clickHandlers.filter(x => x.id !== id)
54
+ }
55
+
56
+ /**
57
+ * Svelte action to apply a click outside handler for a certain element
58
+ * opts.anchor is an optional param specifying the real root source of the
59
+ * component being observed. This is required for things like popovers, where
60
+ * the element using the clickoutside action is the popover, but the popover is
61
+ * rendered at the root of the DOM somewhere, whereas the popover anchor is the
62
+ * element we actually want to consider when determining the source component.
63
+ */
64
+ export default (element, opts) => {
65
+ const id = Math.random()
66
+ const update = newOpts => {
67
+ const callback = newOpts?.callback || newOpts
68
+ const anchor = newOpts?.anchor || element
69
+ updateHandler(id, element, anchor, callback)
70
+ }
71
+ update(opts)
72
+ return {
73
+ update,
74
+ destroy: () => removeHandler(id),
75
+ }
76
+ }
@@ -0,0 +1,116 @@
1
+ export default function positionDropdown(element, opts) {
2
+ let resizeObserver
3
+ let latestOpts = opts
4
+
5
+ // We need a static reference to this function so that we can properly
6
+ // clean up the scroll listener.
7
+ const scrollUpdate = () => {
8
+ updatePosition(latestOpts)
9
+ }
10
+
11
+ // Updates the position of the dropdown
12
+ const updatePosition = opts => {
13
+ const {
14
+ anchor,
15
+ align,
16
+ maxHeight,
17
+ maxWidth,
18
+ useAnchorWidth,
19
+ offset = 5,
20
+ } = opts
21
+ if (!anchor) {
22
+ return
23
+ }
24
+
25
+ // Compute bounds
26
+ const anchorBounds = anchor.getBoundingClientRect()
27
+ const elementBounds = element.getBoundingClientRect()
28
+ let styles = {
29
+ maxHeight: null,
30
+ minWidth: null,
31
+ maxWidth,
32
+ left: null,
33
+ top: null,
34
+ }
35
+
36
+ // Determine vertical styles
37
+ if (align === "right-outside") {
38
+ styles.top = anchorBounds.top
39
+ } else if (window.innerHeight - anchorBounds.bottom < 100) {
40
+ styles.top = anchorBounds.top - elementBounds.height - offset
41
+ styles.maxHeight = maxHeight || 240
42
+ } else {
43
+ styles.top = anchorBounds.bottom + offset
44
+ styles.maxHeight =
45
+ maxHeight || window.innerHeight - anchorBounds.bottom - 20
46
+ }
47
+
48
+ // Determine horizontal styles
49
+ if (!maxWidth && useAnchorWidth) {
50
+ styles.maxWidth = anchorBounds.width
51
+ }
52
+ if (useAnchorWidth) {
53
+ styles.minWidth = anchorBounds.width
54
+ }
55
+ if (align === "right") {
56
+ styles.left = anchorBounds.left + anchorBounds.width - elementBounds.width
57
+ } else if (align === "right-outside") {
58
+ styles.left = anchorBounds.right + offset
59
+ } else {
60
+ styles.left = anchorBounds.left
61
+ }
62
+
63
+ // Apply styles
64
+ Object.entries(styles).forEach(([style, value]) => {
65
+ if (value) {
66
+ element.style[style] = `${value.toFixed(0)}px`
67
+ } else {
68
+ element.style[style] = null
69
+ }
70
+ })
71
+ }
72
+
73
+ // The actual svelte action callback which creates observers on the relevant
74
+ // DOM elements
75
+ const update = newOpts => {
76
+ latestOpts = newOpts
77
+
78
+ // Cleanup old state
79
+ if (resizeObserver) {
80
+ resizeObserver.disconnect()
81
+ }
82
+
83
+ // Do nothing if no anchor
84
+ const { anchor } = newOpts
85
+ if (!anchor) {
86
+ return
87
+ }
88
+
89
+ // Observe both anchor and element and resize the popover as appropriate
90
+ resizeObserver = new ResizeObserver(() => updatePosition(newOpts))
91
+ resizeObserver.observe(anchor)
92
+ resizeObserver.observe(element)
93
+ resizeObserver.observe(document.body)
94
+ }
95
+
96
+ // Apply initial styles which don't need to change
97
+ element.style.position = "absolute"
98
+ element.style.zIndex = "9999"
99
+
100
+ // Set up a scroll listener
101
+ document.addEventListener("scroll", scrollUpdate, true)
102
+
103
+ // Perform initial update
104
+ update(opts)
105
+
106
+ return {
107
+ update,
108
+ destroy() {
109
+ // Cleanup
110
+ if (resizeObserver) {
111
+ resizeObserver.disconnect()
112
+ }
113
+ document.removeEventListener("scroll", scrollUpdate, true)
114
+ },
115
+ }
116
+ }