@icij/murmur-next 4.0.0

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 (296) hide show
  1. package/.github/workflows/deploy-github-pages.yaml +50 -0
  2. package/.storybook/app.scss +14 -0
  3. package/.storybook/doc_variables.scss +20 -0
  4. package/.storybook/main.ts +35 -0
  5. package/.storybook/preview-head.html +2 -0
  6. package/.storybook/preview.ts +32 -0
  7. package/README.md +71 -0
  8. package/deploy.js +15 -0
  9. package/docs/components/ApiTable.vue +171 -0
  10. package/docs/components/App.vue +146 -0
  11. package/docs/components/CollapsibleBlock.vue +122 -0
  12. package/docs/components/DocsHeader.vue +68 -0
  13. package/docs/components/DocsMenu.vue +201 -0
  14. package/docs/components/DocsMenuSection.vue +109 -0
  15. package/docs/components/EditLink.vue +49 -0
  16. package/docs/components/OutboundLink.vue +13 -0
  17. package/docs/components/PalettePresenter.vue +96 -0
  18. package/docs/components/RepositoryLink.vue +28 -0
  19. package/docs/components/SampleCard.vue +119 -0
  20. package/docs/main.js +42 -0
  21. package/docs/pages/components/accordion/doc.md +96 -0
  22. package/docs/pages/components/active-text-truncate/doc.md +44 -0
  23. package/docs/pages/components/advanced-link-form/doc.md +105 -0
  24. package/docs/pages/components/brand/doc.md +30 -0
  25. package/docs/pages/components/brand-expansion/doc.md +70 -0
  26. package/docs/pages/components/confirm-button/doc.md +91 -0
  27. package/docs/pages/components/content-placeholder/doc.md +16 -0
  28. package/docs/pages/components/custom-pagination/doc.md +61 -0
  29. package/docs/pages/components/digits-input/doc.md +28 -0
  30. package/docs/pages/components/donate-form/doc.md +20 -0
  31. package/docs/pages/components/embed-form/doc.md +22 -0
  32. package/docs/pages/components/embeddable-footer/doc.md +60 -0
  33. package/docs/pages/components/follow-us-popover/doc.md +5 -0
  34. package/docs/pages/components/generic-footer/doc.md +21 -0
  35. package/docs/pages/components/generic-header/doc.md +24 -0
  36. package/docs/pages/components/haptic-copy/doc.md +27 -0
  37. package/docs/pages/components/imddb-header/doc.md +23 -0
  38. package/docs/pages/components/ordinal-legend/doc.md +44 -0
  39. package/docs/pages/components/range-picker/doc.md +86 -0
  40. package/docs/pages/components/responsive-iframe/doc.md +13 -0
  41. package/docs/pages/components/scale-legend/doc.md +65 -0
  42. package/docs/pages/components/secret-input/doc.md +12 -0
  43. package/docs/pages/components/selectable-dropdown/doc.md +156 -0
  44. package/docs/pages/components/sharing-options/doc.md +13 -0
  45. package/docs/pages/components/sharing-options-link/doc.md +36 -0
  46. package/docs/pages/components/sign-up-form/doc.md +13 -0
  47. package/docs/pages/components/slide-up-down/doc.md +28 -0
  48. package/docs/pages/components/textured-deck/doc.md +78 -0
  49. package/docs/pages/components/tiny-pagination/doc.md +92 -0
  50. package/docs/pages/datavisualisation/bars/doc.md +110 -0
  51. package/docs/pages/datavisualisation/columns/doc.md +165 -0
  52. package/docs/pages/datavisualisation/lines/doc.md +139 -0
  53. package/docs/pages/datavisualisation/stacked-bar/doc.md +160 -0
  54. package/docs/pages/datavisualisation/stacked-column/doc.md +191 -0
  55. package/docs/pages/getting-started/about-icij/doc.md +13 -0
  56. package/docs/pages/getting-started/custom-bootstrap/doc.md +36 -0
  57. package/docs/pages/getting-started/installation-guide/doc.md +59 -0
  58. package/docs/pages/getting-started/internationalization/doc.md +74 -0
  59. package/docs/pages/maps/choropleth-map/doc.md +420 -0
  60. package/docs/pages/maps/choropleth-map-annotation/doc.md +373 -0
  61. package/docs/pages/maps/symbol-map/doc.md +203 -0
  62. package/docs/pages/structure/breakpoints/doc.md +3 -0
  63. package/docs/pages/structure/grid/doc.md +3 -0
  64. package/docs/pages/utilities/assets/doc.md +138 -0
  65. package/docs/pages/utilities/config/doc.md +52 -0
  66. package/docs/pages/utilities/iframes/doc.md +3 -0
  67. package/docs/pages/visual/colors/doc.md +31 -0
  68. package/docs/pages/visual/iconography/doc.md +56 -0
  69. package/docs/pages/visual/states/doc.md +77 -0
  70. package/docs/pages/visual/themes/doc.md +3 -0
  71. package/docs/pages/visual/typography/doc.md +71 -0
  72. package/docs/routes.js +25 -0
  73. package/docs/store/index.js +21 -0
  74. package/docs/styles/app.scss +36 -0
  75. package/docs/styles/variables.scss +20 -0
  76. package/lib/assets/images/icij-full-white.svg +6 -0
  77. package/lib/assets/images/icij-full.svg +6 -0
  78. package/lib/assets/images/icij.png +0 -0
  79. package/lib/assets/images/icij.svg +46 -0
  80. package/lib/assets/images/icij@2x.png +0 -0
  81. package/lib/assets/images/murmur-dark.png +0 -0
  82. package/lib/assets/images/murmur-dark.svg +79 -0
  83. package/lib/assets/images/murmur-white.png +0 -0
  84. package/lib/assets/images/murmur-white.svg +68 -0
  85. package/lib/components/AccordionStep.vue +128 -0
  86. package/lib/components/AccordionWrapper.vue +138 -0
  87. package/lib/components/ActiveTextTruncate.vue +258 -0
  88. package/lib/components/AdvancedLinkForm.vue +273 -0
  89. package/lib/components/Brand.vue +150 -0
  90. package/lib/components/BrandExpansion.vue +237 -0
  91. package/lib/components/ConfirmButton.vue +204 -0
  92. package/lib/components/ContentPlaceholder.vue +100 -0
  93. package/lib/components/CustomPagination.vue +225 -0
  94. package/lib/components/DigitsInput.vue +180 -0
  95. package/lib/components/DonateForm.vue +367 -0
  96. package/lib/components/EmbedForm.vue +173 -0
  97. package/lib/components/EmbeddableFooter.vue +201 -0
  98. package/lib/components/Fa.js +3 -0
  99. package/lib/components/FollowUsPopover.vue +117 -0
  100. package/lib/components/GenericFooter.vue +218 -0
  101. package/lib/components/GenericHeader.vue +259 -0
  102. package/lib/components/HapticCopy.vue +256 -0
  103. package/lib/components/ImddbHeader.vue +336 -0
  104. package/lib/components/OrdinalLegend.vue +164 -0
  105. package/lib/components/RangePicker.vue +430 -0
  106. package/lib/components/ResponsiveIframe.vue +48 -0
  107. package/lib/components/ScaleLegend.vue +230 -0
  108. package/lib/components/SecretInput.vue +132 -0
  109. package/lib/components/SelectableDropdown.vue +368 -0
  110. package/lib/components/SharingOptions.vue +230 -0
  111. package/lib/components/SharingOptionsLink.vue +259 -0
  112. package/lib/components/SignUpForm.vue +181 -0
  113. package/lib/components/SlideUpDown.vue +131 -0
  114. package/lib/components/TexturedDeck.vue +101 -0
  115. package/lib/components/TinyPagination.vue +268 -0
  116. package/lib/components/index.js +31 -0
  117. package/lib/composables/chart.ts +182 -0
  118. package/lib/composables/resizeObserver.ts +37 -0
  119. package/lib/composables/sendEmail.ts +50 -0
  120. package/lib/config.default.ts +33 -0
  121. package/lib/config.ts +70 -0
  122. package/lib/d3-geo-projection.d.ts +1 -0
  123. package/lib/datavisualisations/BarChart.vue +275 -0
  124. package/lib/datavisualisations/ColumnChart.vue +527 -0
  125. package/lib/datavisualisations/LineChart.vue +274 -0
  126. package/lib/datavisualisations/StackedBarChart.vue +614 -0
  127. package/lib/datavisualisations/StackedColumnChart.vue +640 -0
  128. package/lib/datavisualisations/index.js +5 -0
  129. package/lib/enums.ts +25 -0
  130. package/lib/i18n.ts +16 -0
  131. package/lib/keys.ts +2 -0
  132. package/lib/locales/en.json +140 -0
  133. package/lib/locales/fr.json +117 -0
  134. package/lib/locales/locales/en.json +140 -0
  135. package/lib/locales/locales/fr.json +117 -0
  136. package/lib/main.ts +87 -0
  137. package/lib/maps/ChoroplethMap.vue +825 -0
  138. package/lib/maps/ChoroplethMapAnnotation.vue +336 -0
  139. package/lib/maps/SymbolMap.vue +628 -0
  140. package/lib/maps/index.js +3 -0
  141. package/lib/querystring-es3.d.ts +1 -0
  142. package/lib/shims-bootstrap-vue.d.ts +5 -0
  143. package/lib/shims-tsx.d.ts +11 -0
  144. package/lib/shims-vue.d.ts +14 -0
  145. package/lib/styles/functions.scss +20 -0
  146. package/lib/styles/lib.scss +19 -0
  147. package/lib/styles/mixins.scss +37 -0
  148. package/lib/styles/utilities.scss +18 -0
  149. package/lib/styles/variables.scss +94 -0
  150. package/lib/styles/variables_dark.scss +1 -0
  151. package/lib/types.ts +46 -0
  152. package/lib/utils/animation.ts +24 -0
  153. package/lib/utils/assets.ts +46 -0
  154. package/lib/utils/clipboard.ts +41 -0
  155. package/lib/utils/iframe-resizer.ts +49 -0
  156. package/lib/utils/placeholder.ts +66 -0
  157. package/lib/utils/placeholderTypes.ts +21 -0
  158. package/lib/utils/strings.ts +8 -0
  159. package/loaders/highlight-loader.js +13 -0
  160. package/loaders/markdown-loader.js +91 -0
  161. package/loaders/metadata-loader.js +18 -0
  162. package/loaders/sass-extract-loader.js +14 -0
  163. package/loaders/vue-docgen-loader.js +14 -0
  164. package/package.json +96 -0
  165. package/plugins/MdPluginTypes.ts +10 -0
  166. package/plugins/docs.ts +50 -0
  167. package/plugins/front-matter.ts +36 -0
  168. package/plugins/highlight.ts +27 -0
  169. package/plugins/markdown-it/api-table.ts +25 -0
  170. package/plugins/markdown-it/sample-card.ts +31 -0
  171. package/plugins/plugin-delete.ts +47 -0
  172. package/plugins/plugin-docgen.ts +23 -0
  173. package/plugins/sass-vars.ts +25 -0
  174. package/plugins/vue-docgen.ts +29 -0
  175. package/public/android-chrome-192x192.png +0 -0
  176. package/public/android-chrome-512x512.png +0 -0
  177. package/public/apple-touch-icon.png +0 -0
  178. package/public/assets/img/arrow-bottom.svg +3 -0
  179. package/public/assets/img/texture-brick-black.jpg +0 -0
  180. package/public/assets/img/texture-brick.jpg +0 -0
  181. package/public/assets/img/texture-carbon-black.jpg +0 -0
  182. package/public/assets/img/texture-carbon.jpg +0 -0
  183. package/public/assets/img/texture-crack-black.jpg +0 -0
  184. package/public/assets/img/texture-crack.jpg +0 -0
  185. package/public/assets/img/texture-rock-black.jpg +0 -0
  186. package/public/assets/img/texture-rock.jpg +0 -0
  187. package/public/assets/img/texture-sand-black.jpg +0 -0
  188. package/public/assets/img/texture-sand.jpg +0 -0
  189. package/public/assets/img/texture-silk-black.jpg +0 -0
  190. package/public/assets/img/texture-silk.jpg +0 -0
  191. package/public/assets/topojson/france-departments.json +1 -0
  192. package/public/assets/topojson/paris-arrondissements.json +1 -0
  193. package/public/assets/topojson/world-countries-sans-antarctica.json +1 -0
  194. package/public/favicon-16x16.png +0 -0
  195. package/public/favicon-32x32.png +0 -0
  196. package/public/favicon.ico +0 -0
  197. package/public/site.webmanifest +1 -0
  198. package/stories/assets/code-brackets.svg +1 -0
  199. package/stories/assets/colors.svg +1 -0
  200. package/stories/assets/comments.svg +1 -0
  201. package/stories/assets/direction.svg +1 -0
  202. package/stories/assets/flow.svg +1 -0
  203. package/stories/assets/plugin.svg +1 -0
  204. package/stories/assets/repo.svg +1 -0
  205. package/stories/assets/stackalt.svg +1 -0
  206. package/stories/getting-started/about-icij.mdx +14 -0
  207. package/stories/getting-started/custom-bootstrap.mdx +23 -0
  208. package/stories/getting-started/installation-guide.mdx +62 -0
  209. package/stories/getting-started/internationalization.mdx +63 -0
  210. package/stories/murmur/components/AccordionStep.stories.ts +33 -0
  211. package/stories/murmur/components/AccordionWrapper.stories.ts +69 -0
  212. package/stories/murmur/components/ActiveTextTruncate.stories.ts +32 -0
  213. package/stories/murmur/components/AdvancedLinkForm.stories.ts +77 -0
  214. package/stories/murmur/components/Brand.stories.ts +30 -0
  215. package/stories/murmur/components/BrandExpansion.stories.ts +41 -0
  216. package/stories/murmur/components/ConfirmButton.stories.ts +40 -0
  217. package/stories/murmur/components/ContentPlaceholder.stories.ts +41 -0
  218. package/stories/murmur/components/CustomPagination.stories.ts +42 -0
  219. package/stories/murmur/components/DigitsInput.stories.ts +29 -0
  220. package/stories/murmur/components/DonateForm.stories.ts +29 -0
  221. package/stories/murmur/components/EmbedForm.stories.ts +35 -0
  222. package/stories/murmur/components/EmbeddableFooter.stories.ts +59 -0
  223. package/stories/murmur/components/FollowUsPopover.stories.ts +24 -0
  224. package/stories/murmur/components/GenericFooter.stories.ts +27 -0
  225. package/stories/murmur/components/GenericHeader.stories.ts +27 -0
  226. package/stories/murmur/components/HapticCopy.stories.ts +40 -0
  227. package/stories/murmur/components/ImddbHeader.stories.ts +27 -0
  228. package/stories/murmur/components/OrdinalLegend.stories.ts +49 -0
  229. package/stories/murmur/components/RangePicker.stories.ts +98 -0
  230. package/stories/murmur/components/ResponsiveIframe.stories.ts +24 -0
  231. package/stories/murmur/components/ScaleLegend.stories.ts +65 -0
  232. package/stories/murmur/components/SecretInput.stories.ts +60 -0
  233. package/stories/murmur/components/SelectableDropdown.stories.ts +143 -0
  234. package/stories/murmur/components/SharingOptions.stories.ts +32 -0
  235. package/stories/murmur/components/SharingOptionsLink.stories.ts +53 -0
  236. package/stories/murmur/components/SignUpForm.stories.ts +51 -0
  237. package/stories/murmur/components/SlideUpDown.stories.ts +32 -0
  238. package/stories/murmur/components/TexturedDeck.stories.ts +83 -0
  239. package/stories/murmur/components/TinyPagination.stories.ts +65 -0
  240. package/stories/murmur/datavisualisations/BarChart.stories.ts +54 -0
  241. package/stories/murmur/datavisualisations/ColumnChart.stories.ts +88 -0
  242. package/stories/murmur/datavisualisations/LineChart.stories.ts +139 -0
  243. package/stories/murmur/datavisualisations/StackedBarChart.stories.ts +199 -0
  244. package/stories/murmur/datavisualisations/StackedColumnChart.stories.ts +136 -0
  245. package/stories/murmur/decorators.ts +108 -0
  246. package/stories/murmur/maps/ChoroplethMap.stories.ts +440 -0
  247. package/stories/murmur/maps/ChoroplethMapAnnotation.stories.ts +26 -0
  248. package/stories/murmur/maps/SymbolMap.stories.ts +24 -0
  249. package/stories/murmur/utils.ts +7 -0
  250. package/tests/unit/components/AccordionStep.spec.ts +157 -0
  251. package/tests/unit/components/AccordionWrapper.spec.ts +57 -0
  252. package/tests/unit/components/ActiveTextTruncate.spec.js +30 -0
  253. package/tests/unit/components/AdvancedLinkForm.spec.js +124 -0
  254. package/tests/unit/components/Brand.spec.js +50 -0
  255. package/tests/unit/components/ContentPlaceholder.spec.js +29 -0
  256. package/tests/unit/components/CustomPagination.spec.js +72 -0
  257. package/tests/unit/components/DigitsInput.spec.ts +157 -0
  258. package/tests/unit/components/DonateForm.spec.js +149 -0
  259. package/tests/unit/components/EmbedForm.spec.js +108 -0
  260. package/tests/unit/components/EmbeddableFooter.spec.js +11 -0
  261. package/tests/unit/components/Fa.spec.js +18 -0
  262. package/tests/unit/components/FollowUsPopover.spec.js +29 -0
  263. package/tests/unit/components/GenericFooter.spec.js +29 -0
  264. package/tests/unit/components/GenericHeader.spec.js +104 -0
  265. package/tests/unit/components/HapticCopy.spec.js +123 -0
  266. package/tests/unit/components/ImddbHeader.spec.js +96 -0
  267. package/tests/unit/components/OrdinalLegend.spec.js +120 -0
  268. package/tests/unit/components/RangePicker.spec.ts +87 -0
  269. package/tests/unit/components/ResponsiveIframe.spec.js +20 -0
  270. package/tests/unit/components/ScaleLegend.spec.js +139 -0
  271. package/tests/unit/components/SecretInput.spec.js +81 -0
  272. package/tests/unit/components/SelectableDropdown.spec.js +160 -0
  273. package/tests/unit/components/SharingOptions.spec.js +125 -0
  274. package/tests/unit/components/SharingOptionsLink.spec.js +184 -0
  275. package/tests/unit/components/SignUpForm.spec.js +145 -0
  276. package/tests/unit/components/SlideUpDown.spec.js +59 -0
  277. package/tests/unit/components/TinyPagination.spec.js +46 -0
  278. package/tests/unit/config.spec.js +136 -0
  279. package/tests/unit/datavisualisations/BarChart.spec.js +63 -0
  280. package/tests/unit/datavisualisations/ColumnChart.spec.js +344 -0
  281. package/tests/unit/datavisualisations/LineChart.spec.js +155 -0
  282. package/tests/unit/datavisualisations/StackedBarChart.spec.js +294 -0
  283. package/tests/unit/datavisualisations/StackedColumnChart.spec.js +443 -0
  284. package/tests/unit/i18n.spec.ts +19 -0
  285. package/tests/unit/main.spec.js +82 -0
  286. package/tests/unit/maps/ChoroplethMap.spec.js +214 -0
  287. package/tests/unit/maps/ChoroplethMapAnnotation.spec.ts +186 -0
  288. package/tests/unit/maps/SymbolMap.spec.js +92 -0
  289. package/tests/unit/require.spec.js +22 -0
  290. package/tests/unit/setup.js +13 -0
  291. package/tests/unit/utils/assets.spec.js +61 -0
  292. package/tests/unit/utils/clipboard.spec.js +18 -0
  293. package/tests/unit/utils/iframe-resizer.spec.js +71 -0
  294. package/tsconfig.json +35 -0
  295. package/vite.config.ts +79 -0
  296. package/vitest.config.ts +19 -0
@@ -0,0 +1,132 @@
1
+ <script lang="ts">
2
+ import { faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons'
3
+
4
+ import { computed,ref,onBeforeMount, watch,defineComponent} from 'vue'
5
+ import { library, default as Fa } from './Fa'
6
+ import HapticCopy from './HapticCopy.vue'
7
+
8
+ export default defineComponent({
9
+ name: 'SecretInput',
10
+ components: { Fa, HapticCopy },
11
+ props: {
12
+ /**
13
+ * If true the value is visible by default
14
+ */
15
+ visible: {
16
+ type: Boolean
17
+ },
18
+ /**
19
+ * Value of the input
20
+ */
21
+ value: {
22
+ type: [String, Number],
23
+ default: ''
24
+ },
25
+ /**
26
+ * Size of the input form
27
+ */
28
+ size: {
29
+ type: String,
30
+ default: 'md'
31
+ },
32
+ /**
33
+ * Bootstrap variant of the haptic copy button
34
+ */
35
+ hapticCopyVariant: {
36
+ type: String,
37
+ default: 'primary'
38
+ },
39
+ /**
40
+ * Hide toggler button
41
+ */
42
+ noToggler: {
43
+ type: Boolean
44
+ },
45
+ /**
46
+ * Hide haptic copy button
47
+ */
48
+ noHapticCopy: {
49
+ type: Boolean
50
+ }
51
+ },
52
+ emits:['update:visible'],
53
+ setup(props,{emit}){
54
+ onBeforeMount(() =>{
55
+ library.add(faEye, faEyeSlash)
56
+ })
57
+ const secretInput = ref<HTMLInputElement|null>(null)
58
+ const inputType = computed(() => {
59
+ return props.visible ? 'text' : 'password'
60
+ })
61
+ const togglerIcon = computed(() => {
62
+ return props.visible ? ['far', 'eye-slash'] : ['far', 'eye']
63
+ })
64
+ const hapticCopyClassList = computed(() => {
65
+ return `btn-${props.hapticCopyVariant}`
66
+ })
67
+
68
+
69
+ function toggle() {
70
+ /**
71
+ * Emitted when the visibility of the input changes.
72
+ *
73
+ * @event update:visible
74
+ * @type {Boolean}
75
+ */
76
+ emit("update:visible", !props.visible)
77
+ }
78
+ function selectInput() {
79
+ if (props.visible) {
80
+ secretInput.value?.select()
81
+ }
82
+ }
83
+ return {
84
+ selectInput,
85
+ toggle,
86
+ togglerIcon,
87
+ inputType,
88
+ hapticCopyClassList
89
+ }
90
+ }
91
+ })
92
+ </script>
93
+
94
+ <template>
95
+ <b-input-group :size="size" class="secret-input">
96
+ <b-input-group-prepend v-if="!noToggler">
97
+ <b-button variant="link" class="secret-input__toggler" @click="toggle">
98
+ <fa fixed-width :icon="togglerIcon" />
99
+ </b-button>
100
+ </b-input-group-prepend>
101
+ <b-form-input
102
+ ref="secretInput"
103
+ class="text-monospace secret-input__input"
104
+ readonly
105
+ :type="inputType"
106
+ :modelValue="value"
107
+ @click="selectInput"
108
+ />
109
+ <b-input-group-append v-if="!noHapticCopy">
110
+ <haptic-copy
111
+ class="secret-input__copy"
112
+ hide-label
113
+ :class="hapticCopyClassList"
114
+ :text="value"
115
+ @success="selectInput"
116
+ @error="selectInput"
117
+ />
118
+ </b-input-group-append>
119
+ </b-input-group>
120
+ </template>
121
+
122
+ <style scoped lang="scss">
123
+ @import '../styles/lib.scss';
124
+
125
+ .secret-input {
126
+ &__toggler {
127
+ background: $input-disabled-bg;
128
+ border: $input-border-width solid $input-border-color;
129
+ border-right: 0;
130
+ }
131
+ }
132
+ </style>
@@ -0,0 +1,368 @@
1
+ <script lang="ts">
2
+ import castArray from 'lodash/castArray'
3
+ import eq from 'lodash/eq'
4
+ import findIndex from 'lodash/findIndex'
5
+ import filter from 'lodash/filter'
6
+ import identity from 'lodash/identity'
7
+ import isEqual from 'lodash/isEqual'
8
+ import uniqueId from 'lodash/uniqueId'
9
+ import { faCheckSquare, faSquare } from '@fortawesome/free-regular-svg-icons'
10
+ import { RecycleScroller } from 'vue-virtual-scroller'
11
+
12
+ import Fa from './Fa'
13
+ import {defineComponent, ref, computed, watch, onMounted, onUnmounted,PropType } from 'vue'
14
+
15
+ const KEY_ESC_CODE = 27
16
+ const KEY_UP_CODE = 38
17
+ const KEY_DOWN_CODE = 40
18
+ type Item = any;
19
+ export default defineComponent({
20
+ name: 'SelectableDropdown',
21
+ components: {
22
+ Fa,
23
+ RecycleScroller
24
+ },
25
+ props: {
26
+ /**
27
+ * The items of the list.
28
+ */
29
+ items: {
30
+ type: Array as PropType<Item[]>,
31
+ default() {
32
+ return []
33
+ }
34
+ },
35
+ /**
36
+ * The actual selected item.
37
+ */
38
+ modelValue: {
39
+ type: [String, Object, Array, Number] as PropType<Item>,
40
+ default: null
41
+ },
42
+ /**
43
+ * If true, the dropdown is hidden and deactivated.
44
+ */
45
+ hide: {
46
+ type: Boolean
47
+ },
48
+ /**
49
+ * If true, the key events won't be propagated.
50
+ */
51
+ propagate: {
52
+ type: Boolean
53
+ },
54
+ /**
55
+ * The user can select values.
56
+ */
57
+ multiple: {
58
+ type: Boolean
59
+ },
60
+ /**
61
+ * A function to change the label rendering.
62
+ */
63
+ serializer: {
64
+ type: Function,
65
+ default: identity
66
+ },
67
+ /**
68
+ * The class to apply to the list.
69
+ */
70
+ listClass: {
71
+ type: String,
72
+ default: 'dropdown-menu'
73
+ },
74
+ /**
75
+ * The class to apply to each item.
76
+ */
77
+ itemClass: {
78
+ type: String,
79
+ default: 'dropdown-item'
80
+ },
81
+ /**
82
+ * Set to true to deactivate action when arrow keys are pressed
83
+ */
84
+ deactivateKeys: {
85
+ type: Boolean
86
+ },
87
+ /**
88
+ * Comparison function to verify equality between selected items.
89
+ */
90
+ eq: {
91
+ type: Function,
92
+ default: eq
93
+ },
94
+ /**
95
+ * Display height of the items in pixels used to calculate the scroll size and position
96
+ * Default value is 32 (32px)
97
+ */
98
+ itemSize: {
99
+ type: Number,
100
+ default: 32
101
+ },
102
+ /**
103
+ * Height of the scroll container to specify especially if using the virtual scroll feature
104
+ * Default value is 'inherit'
105
+ */
106
+ scrollerHeight: {
107
+ type: String,
108
+ default: 'inherit'
109
+ }
110
+ },
111
+ emits:['click','update:modelValue', 'deactivate'],
112
+ setup(props,{emit}){
113
+ onMounted(()=> {
114
+ activateItemOrItems()
115
+ toggleKeys()
116
+ })
117
+ onUnmounted(() => {
118
+ unbindKeys()
119
+ })
120
+ const scroller=ref(null)
121
+ const activeItems = ref<Item[]>([])
122
+ const cssProps = computed(()=>{
123
+ return {
124
+ '--scroller-height': props.scrollerHeight
125
+ }
126
+ })
127
+ const keyField = computed(()=>{
128
+ return typeof items_.value[0] === 'string' ? null : 'recycle_scroller_id'
129
+ })
130
+ const items_ = computed(():Item[]=>{
131
+ if (typeof props.items[0] === 'string') {
132
+ return props.items
133
+ }
134
+ return props.items.map((item:Item) => ({ ...item, recycle_scroller_id: `id-${uniqueId()}` }))
135
+ })
136
+ const firstActiveItemIndex = computed(()=>{
137
+ return activeItems.value.length ? items_.value.indexOf(activeItems.value[0]) : -1
138
+ })
139
+ const lastActiveItemIndex = computed(()=>{
140
+ return activeItems.value.length ? items_.value.indexOf(activeItems.value.slice(-1)) : -1
141
+ })
142
+ const keysMap = computed(():{[key:string]:Function }=>{
143
+ return {
144
+ [KEY_UP_CODE]: activatePreviousItem,
145
+ [KEY_DOWN_CODE]: activateNextItem,
146
+ [KEY_ESC_CODE]: deactivateItems
147
+ }
148
+ }
149
+ )
150
+ watch(()=>props.hide,()=> {
151
+ toggleKeys()
152
+ })
153
+ watch(()=>activeItems.value,
154
+ ()=> {
155
+ /**
156
+ * Fired when the selected value change. It will pass a canonical value
157
+ * or an array of values if the property `multiple` is set to true.
158
+ *
159
+ * @event input
160
+ * @type {String, Object, Array, Number}
161
+ */
162
+ emit('update:modelValue', props.multiple ? activeItems.value : activeItems.value[0])
163
+ })
164
+ watch(()=>props.modelValue,
165
+ (itemOrItems)=>{
166
+ const items = castArray(itemOrItems)
167
+ if (!isEqual(activeItems.value, items)) {
168
+ activateItemOrItems(items)
169
+ }
170
+ },
171
+ {deep: true})
172
+ function indexIcon(item:Item){
173
+ return itemActivated(item) ? faCheckSquare : faSquare
174
+ }
175
+ function itemActivated(item:Item) {
176
+ return findIndex(activeItems.value, (i) => props.eq(item, i)) > -1
177
+ }
178
+ function clickToSelectItem(item:Item) {
179
+ /**
180
+ * Fired when user click on an item
181
+ *
182
+ * @event click
183
+ * @type {String, Object, Array, Number}
184
+ */
185
+ emit('click', item)
186
+ if (props.multiple) {
187
+ addItem(item)
188
+ } else {
189
+ selectItem(item)
190
+ }
191
+ }
192
+ function clickToAddItem(item:Item) {
193
+ /**
194
+ * Fired when user click on an item
195
+ *
196
+ * @event click
197
+ * @type {String, Object, Array, Number}
198
+ */
199
+ emit('click', item)
200
+ addItem(item)
201
+ }
202
+ function clickToSelectRangeToItem(item:Item) {
203
+ /**
204
+ * Fired when user click on an item
205
+ *
206
+ * @event click
207
+ * @type {String, Object, Array, Number}
208
+ */
209
+ emit('click', item)
210
+ selectRangeToItem(item)
211
+ }
212
+ function emitEventOnItem(name:"click" | "update:modelValue" | "deactivate", item:Item) {
213
+ emit(name, item)
214
+ }
215
+ function selectItem(item:Item) {
216
+ if (itemActivated(item) && activeItems.value.length === 1) {
217
+ activeItems.value = filter(activeItems.value, (i) => !props.eq(item, i))
218
+ } else {
219
+ activeItems.value = [item]
220
+ }
221
+ }
222
+ function addItem(item:Item) {
223
+ if (itemActivated(item)) {
224
+ activeItems.value = filter(activeItems.value, (i) => !props.eq(item, i))
225
+ } else {
226
+ activeItems.value.push(item)
227
+ }
228
+ }
229
+ function selectRangeToItem(item:Item) {
230
+ // No activated items
231
+ if (!activeItems.value.length || !props.multiple) {
232
+ selectItem(item)
233
+ } else {
234
+ const index = items_.value.indexOf(item)
235
+ if (index > firstActiveItemIndex.value) {
236
+ activeItems.value = items_.value.slice(firstActiveItemIndex.value, index + 1)
237
+ } else {
238
+ activeItems.value = items_.value.slice(index, firstActiveItemIndex.value + 1)
239
+ }
240
+ }
241
+ }
242
+ function activateItemOrItems(itemOrItems = props.modelValue) {
243
+ const items = castArray(itemOrItems)
244
+ activeItems.value = [...items]
245
+ }
246
+ function activatePreviousItem() {
247
+ activeItems.value = [items_.value[Math.max(firstActiveItemIndex.value - 1, -1)]]
248
+ }
249
+ function activateNextItem() {
250
+ activeItems.value = [items_.value[Math.min(firstActiveItemIndex.value + 1, items_.value.length - 1)]]
251
+ }
252
+ function deactivateItems() {
253
+ activeItems.value = []
254
+ /**
255
+ * Fired when items selection is deactivated
256
+ *
257
+ * @event deactivate
258
+ */
259
+ emit('deactivate')
260
+ }
261
+ function keyDown(event:KeyboardEvent) {
262
+ const keyCode = event.keyCode || event.which
263
+ // The dropdown must be active
264
+ if (props.deactivateKeys || props.hide || !isKnownKey(keyCode)) return
265
+ // Should we stop the event propagation?
266
+ if (!props.propagate && event.stopPropagation) {
267
+ event.stopPropagation()
268
+ event.preventDefault()
269
+ }
270
+ // Then call the right method
271
+ keysMap.value[keyCode]()
272
+ }
273
+ function isKnownKey(keycode:number) {
274
+ return Object.keys(keysMap.value).map(Number).indexOf(keycode) > -1
275
+ }
276
+ function unbindKeys() {
277
+ window.removeEventListener('keydown', keyDown)
278
+ }
279
+ function bindKeys() {
280
+ window.addEventListener('keydown', keyDown)
281
+ }
282
+ function toggleKeys() {
283
+ if (props.hide) {
284
+ unbindKeys()
285
+ } else {
286
+ bindKeys()
287
+ }
288
+ }
289
+ function itemId(item:Item){
290
+ return `dropdown-item-${item.recycle_scroller_id ?? item.toLowerCase()}`
291
+ }
292
+
293
+ return {
294
+ cssProps,
295
+ items_,
296
+ keyField,
297
+ lastActiveItemIndex,
298
+ itemActivated,
299
+ clickToSelectItem,
300
+ clickToAddItem,
301
+ clickToSelectRangeToItem,
302
+ indexIcon,
303
+ scroller,
304
+ activeItems,
305
+ itemId
306
+ }
307
+ }
308
+ })
309
+ </script>
310
+
311
+ <template>
312
+ <div
313
+ v-if="!hide"
314
+ class="selectable-dropdown show"
315
+ :class="{ 'selectable-dropdown--multiple': multiple, [listClass]: true }"
316
+ >
317
+ <recycle-scroller
318
+ v-slot="{ item, active }"
319
+ :style="cssProps"
320
+ class="scroller"
321
+ :items="items_"
322
+ :key-field="keyField"
323
+ :item-size="itemSize"
324
+ >
325
+ <span
326
+ :id="itemId(item)"
327
+ :class="{ 'recycle_scroller-item--active': active, active: itemActivated(item), [itemClass]: true }"
328
+ class="selectable-dropdown__item px-3 d-flex"
329
+ @click.exact="clickToSelectItem(item)"
330
+ @click.ctrl="clickToAddItem(item)"
331
+ @click.shift="clickToSelectRangeToItem(item)"
332
+ >
333
+ <!-- @slot Item content -->
334
+ <slot name="item" :item="item">
335
+ <div v-if="multiple" class="selectable-dropdown__item__check">
336
+ <fa :icon="indexIcon(item)" class="me-2" />
337
+ </div>
338
+ <div class="flex-grow-1 text-truncate selectable-dropdown__item__label">
339
+ <!-- @slot Item's label content -->
340
+ <slot name="item-label" :item="item">
341
+ {{ serializer(item) }}
342
+ </slot>
343
+ </div>
344
+ </slot>
345
+ </span>
346
+ </recycle-scroller>
347
+ </div>
348
+ </template>
349
+
350
+ <style lang="scss">
351
+ @import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
352
+
353
+ .selectable-dropdown {
354
+ --scroller-height: 'inherit';
355
+ user-select: none;
356
+
357
+ &.dropdown-menu {
358
+ position: relative;
359
+ top: 0;
360
+ left: 0;
361
+ float: none;
362
+ }
363
+
364
+ & .scroller {
365
+ height: var(--scroller-height);
366
+ }
367
+ }
368
+ </style>