@icij/murmur-next 4.0.17 → 4.1.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.
@@ -12,6 +12,7 @@
12
12
  :model-value="showTooltip"
13
13
  :placement="placement"
14
14
  :target="uniqComponentId"
15
+ teleport-to="body"
15
16
  manual
16
17
  >
17
18
  <div class="confirm-button__tooltip">
@@ -148,9 +149,19 @@ export default defineComponent({
148
149
  emit('toggled', showTooltip.value)
149
150
  }
150
151
 
152
+ function hideConfirmationTooltip(): void {
153
+ showTooltip.value = false
154
+ /**
155
+ * Emitted when the confirmation is toggled.
156
+ * @event toggled
157
+ * @param Boolean True if the button is shown.
158
+ */
159
+ emit('toggled', false)
160
+ }
161
+
151
162
  function cancel(): void {
152
- // showTooltip.value = false
153
- props.cancelled()
163
+ hideConfirmationTooltip()
164
+ props.cancelled?.()
154
165
  /**
155
166
  * Emitted when the confirmation is cancelled.
156
167
  * @event cancelled
@@ -159,8 +170,8 @@ export default defineComponent({
159
170
  }
160
171
 
161
172
  function confirm(): void {
162
- // showTooltip.value = false
163
- props.confirmed()
173
+ hideConfirmationTooltip()
174
+ props.confirmed?.()
164
175
  /**
165
176
  * Emitted when the confirmation is confirmed.
166
177
  * @event confirmed
@@ -141,11 +141,12 @@ export default defineComponent({
141
141
  const variantColorClass = computed(() => {
142
142
  return `btn-${props.variant}`
143
143
  })
144
+
144
145
  async function subscribe() {
145
146
  resetMessages()
146
147
  freeze()
147
148
  // Send the data, catch the result no matter what and unfreeze the form
148
- await send().then(done, done).finally(unfreeze)
149
+ await send().then(done, error).finally(unfreeze)
149
150
  }
150
151
 
151
152
  function done({ result, msg }: any): void {
@@ -153,18 +154,24 @@ export default defineComponent({
153
154
  email.value = ''
154
155
  successMessage.value = msg
155
156
  } else {
156
- // Mailchimp formats errors in list
157
- errorMessage.value =
158
- last((msg || "Something's wrong").split('0 -')) ?? null
157
+ error({ msg })
159
158
  }
160
159
  }
160
+
161
+ // Mailchimp formats errors in list
162
+ function error({ msg }: any): void {
163
+ errorMessage.value = last((msg || "Something's wrong").split('0 -')) ?? null
164
+ }
165
+
161
166
  function resetMessages() {
162
167
  errorMessage.value = null
163
168
  successMessage.value = null
164
169
  }
170
+
165
171
  function freeze() {
166
172
  frozen.value = true
167
173
  }
174
+
168
175
  function unfreeze() {
169
176
  frozen.value = false
170
177
  }
@@ -62,7 +62,7 @@
62
62
 
63
63
  <script lang="ts">
64
64
  import { faAngleLeft, faAngleRight } from '@fortawesome/free-solid-svg-icons'
65
- import { defineComponent, PropType, ref, computed, onBeforeMount } from 'vue'
65
+ import { defineComponent, PropType, ref, computed, onBeforeMount, watch } from 'vue'
66
66
  import { useI18n } from 'vue-i18n'
67
67
 
68
68
  import { library, default as Fa } from './Fa'
@@ -187,6 +187,10 @@ export default defineComponent({
187
187
 
188
188
  const currentPageInput = ref<number | string>(pageValue.value)
189
189
 
190
+ watch(() => props.modelValue, (value) => {
191
+ currentPageInput.value = value
192
+ })
193
+
190
194
  function applyPageForm(): void {
191
195
  if (!isNaN(currentPageInput.value as number)) {
192
196
  emit('update:modelValue', +currentPageInput.value)
@@ -4,14 +4,15 @@ import isObject from 'lodash/isObject'
4
4
  import isString from 'lodash/isString'
5
5
  import max from 'lodash/max'
6
6
  import some from 'lodash/some'
7
- import { ComponentPublicInstance, computed, onMounted, ref, watch } from 'vue'
7
+ import { ComponentPublicInstance, computed, toRef, toValue, ref, watch, onMounted, nextTick } from 'vue'
8
8
  import { isUrl } from '@/utils/strings'
9
9
  import { Ref, SetupContext } from '@vue/runtime-core'
10
10
  import useResizeObserver from '@/composables/resizeObserver'
11
- import { watchEffect } from 'vue'
12
11
 
13
12
  type ChartContext<T extends string[]> = SetupContext<[...T, ...string[]]>
13
+
14
14
  type ChartEmit = Pick<ChartContext<['resized', 'loaded']>, 'emit'>
15
+
15
16
  type ChartProps = {
16
17
  chartHeightRatio: { type: NumberConstructor }
17
18
  data: {
@@ -28,13 +29,13 @@ type ChartProps = {
28
29
  socialModeRatio: { default: number; type: NumberConstructor }
29
30
  }
30
31
 
31
- export function getChartProps(props: any): ChartProps {
32
+ export function getChartProps(props: any): any {
32
33
  return {
33
- chartHeightRatio: props.chartHeightRatio,
34
- data: props.data,
35
- dataUrlType: props.dataUrlType,
36
- socialMode: props.socialMode,
37
- socialModeRatio: props.socialModeRatio
34
+ chartHeightRatio: toRef(props, 'chartHeightRatio'),
35
+ data: toRef(props, 'data'),
36
+ dataUrlType: toRef(props, 'dataUrlType'),
37
+ socialMode: toRef(props, 'socialMode'),
38
+ socialModeRatio: toRef(props, 'socialModeRatio')
38
39
  }
39
40
  }
40
41
 
@@ -76,10 +77,13 @@ export const chartProps = (): ChartProps => ({
76
77
  default: 5 / 4
77
78
  }
78
79
  })
80
+
79
81
  export const chartEmits = ['resized', 'loaded']
82
+
80
83
  type Chart = {
81
84
  dataHasHighlights: any
82
85
  loadedData: any
86
+ mounted: Ref<boolean>,
83
87
  xAxisYearFormat: (year: number | string) => number | string
84
88
  elementsMaxBBox: ({
85
89
  selector,
@@ -93,42 +97,51 @@ type Chart = {
93
97
  d3Formatter: any
94
98
  baseHeightRatio: any
95
99
  }
100
+
96
101
  export function useChart(
97
102
  resizableRef: Ref<ComponentPublicInstance<HTMLElement> | null>,
98
- props: ChartProps,
103
+ props: any,
99
104
  { emit }: ChartEmit,
100
105
  isLoaded: Ref<boolean>,
101
106
  onResized?: ()=>void,
102
107
  afterLoaded?: () => Promise<any>
103
108
  ): Chart {
109
+
104
110
  const { resizeRef, resizeState } = useResizeObserver(resizableRef)
105
111
  const loadedData = ref<unknown|unknown[]>([])
112
+ const mounted = ref<boolean>(false)
113
+ const dataRef = toRef(props.data)
114
+ const dataUrlTypeRef = toRef(props.dataUrlType)
115
+
116
+ onMounted(() => {
117
+ nextTick(() => {
118
+ mounted.value = true
119
+ })
120
+ })
106
121
 
107
- onMounted(async () => {
122
+ watch([dataRef, dataUrlTypeRef], async () => {
108
123
  await document.fonts?.ready
124
+
125
+ const data = toValue(dataRef)
126
+ const dataUrlType = toValue(dataUrlTypeRef)
109
127
 
110
- watchEffect(async () => {
111
- if (!isLoaded.value) {
112
- if (isString(props.data)) {
113
- // @ts-expect-error introspection in typescript is tricky
114
- loadedData.value = await d3[props.dataUrlType](props.data)
115
- } else {
116
- loadedData.value = props.data as unknown as []
117
- }
118
-
119
- if (afterLoaded) {
120
- await afterLoaded()
121
- }
122
- isLoaded.value = true
123
- emit('loaded')
124
- }
125
-
126
- if (isLoaded.value && onResized) {
127
- onResized()
128
- emit('resized')
129
- }
130
- })
131
- })
128
+ if (isString(data)) {
129
+ // @ts-expect-error introspection in typescript is tricky
130
+ loadedData.value = await d3[dataUrlType](data)
131
+ } else {
132
+ loadedData.value = data as unknown as []
133
+ }
134
+
135
+ await afterLoaded?.()
136
+ isLoaded.value = true
137
+ emit('loaded')
138
+
139
+ if (onResized) {
140
+ onResized()
141
+ emit('resized')
142
+ }
143
+ }, { immediate: true })
144
+
132
145
  function elementsMaxBBox({
133
146
  selector = 'text',
134
147
  defaultWidth = null,
@@ -160,9 +173,11 @@ export function useChart(
160
173
  // previously using narrowWidth but it is automatically updated through resizeObserver state reactivity
161
174
  return resizeState.narrowWidth ? '’' + String(year).slice(2, 4) : year
162
175
  }
176
+
163
177
  function highlighted(datum: { highlight: boolean }) {
164
178
  return datum.highlight
165
179
  }
180
+
166
181
  function d3Formatter(value: any, formatter: any) {
167
182
  if (isFunction(formatter)) {
168
183
  return formatter(value)
@@ -171,15 +186,18 @@ export function useChart(
171
186
  }
172
187
  return value
173
188
  }
189
+
174
190
  const baseHeightRatio = computed(() => {
175
- return (
176
- props.chartHeightRatio ||
177
- (props.socialMode ? props.socialModeRatio : 9 / 16)
178
- )
191
+ const chartHeightRatio = toValue(props.chartHeightRatio)
192
+ const socialMode = toValue(props.socialMode)
193
+ const socialModeRatio = toValue(props.socialModeRatio)
194
+ return chartHeightRatio || (socialMode ? socialModeRatio : 9 / 16)
179
195
  })
196
+
180
197
  const dataHasHighlights = computed(() => {
181
- if (Array.isArray(props.data)) {
182
- return some(props.data, highlighted)
198
+ const data = toValue(dataRef)
199
+ if (Array.isArray(data)) {
200
+ return some(data, highlighted)
183
201
  }
184
202
  return false
185
203
  })
@@ -193,6 +211,7 @@ export function useChart(
193
211
 
194
212
  return {
195
213
  loadedData,
214
+ mounted,
196
215
  elementsMaxBBox,
197
216
  xAxisYearFormat,
198
217
  d3Formatter,
@@ -0,0 +1,66 @@
1
+ import get from 'lodash/get'
2
+ import first from 'lodash/first'
3
+
4
+ import { Ref, reactive } from 'vue'
5
+
6
+ type ElementMap = {
7
+ [selector: string]: HTMLElement[]
8
+ }
9
+
10
+ type ObserverMap = {
11
+ [selector: string]: MutationObserver
12
+ }
13
+
14
+ export function useQueryObserver(rootRef: Ref<HTMLElement | null>) {
15
+ const elements = reactive<ElementMap>({})
16
+ const observers = reactive<ObserverMap>({})
17
+
18
+ const hasElements = (selector: string): boolean => {
19
+ return get(elements, [selector, 'length'], 0) > 0
20
+ }
21
+
22
+ const hasObserver = (selector: string): boolean => {
23
+ return selector in observers
24
+ }
25
+
26
+ const updateElements = (selector: string): HTMLElement[] | null => {
27
+ // We search for the give selector until element are found
28
+ if (rootRef.value && !hasElements(selector)) {
29
+ elements[selector] = Array.from(rootRef.value.querySelectorAll(selector))
30
+ }
31
+ return elements[selector] ?? null
32
+ }
33
+
34
+ const observerCallback = (selector: string) => {
35
+ return () => {
36
+ updateElements(selector)
37
+ if (hasElements(selector)) {
38
+ observers[selector].disconnect()
39
+ }
40
+ }
41
+ }
42
+
43
+ const observe = (selector: string) => {
44
+ updateElements(selector)
45
+ // Wait for the root ref to exist and only create the observer once by selector
46
+ if (rootRef.value && !hasObserver(selector)) {
47
+ observers[selector] = new MutationObserver(observerCallback(selector))
48
+ observers[selector].observe(rootRef.value, { childList: true, subtree: true, })
49
+ }
50
+ return observers[selector]
51
+ }
52
+
53
+ const querySelector = (selector: string) => {
54
+ return first(querySelectorAll(selector))
55
+ }
56
+
57
+ const querySelectorAll = (selector: string) => {
58
+ observe(selector)
59
+ return get(elements, selector, [])
60
+ }
61
+
62
+ return {
63
+ querySelector,
64
+ querySelectorAll,
65
+ }
66
+ }
@@ -13,7 +13,6 @@ export function useSendEmail(
13
13
  referrer?: string | null,
14
14
  defaultGroups?: string[] | string
15
15
  ) {
16
- const emailValue = toValue(email)
17
16
 
18
17
  const groups = computed(() => {
19
18
  return flatten(castArray(defaultGroups).map((g) => g.split(',')))
@@ -22,6 +21,7 @@ export function useSendEmail(
22
21
  const urlFromAction = computed(() => {
23
22
  return action?.replace('/post?', '/post-json?').concat('&c=?')
24
23
  })
24
+
25
25
  const parentReferrer = computed(() => {
26
26
  if (referrer) {
27
27
  return referrer
@@ -36,6 +36,7 @@ export function useSendEmail(
36
36
  throw new Error('Missing url Info')
37
37
  }
38
38
 
39
+ const emailValue = toValue(email)
39
40
  const url = new URL(urlFromAction.value)
40
41
  url.searchParams.set('SIGNUP', tracker)
41
42
  url.searchParams.set('MMERGE24', parentReferrer.value)
@@ -44,6 +45,7 @@ export function useSendEmail(
44
45
 
45
46
  return url.href
46
47
  })
48
+
47
49
  function send() {
48
50
  return new Promise((resolve, reject) => {
49
51
  jsonp(
@@ -19,11 +19,9 @@ type ColumnBar = {
19
19
  x: number
20
20
  y: number
21
21
  }
22
- //import chart from '../mixins/chart'
23
22
 
24
23
  export default defineComponent({
25
24
  name: 'ColumnChart',
26
- //mixins: [chart],
27
25
  props: {
28
26
  /**
29
27
  * Color of each column (uses the CSS variable --column-color by default)
@@ -344,6 +342,9 @@ export default defineComponent({
344
342
  if (!el.value) {
345
343
  return
346
344
  }
345
+
346
+ d3.select('.column-chart__axis > *').remove()
347
+
347
348
  d3.select(el.value)
348
349
  .select('.column-chart__axis--x')
349
350
  .call(xAxis.value as any)
@@ -380,6 +381,7 @@ export default defineComponent({
380
381
  height,
381
382
  margin,
382
383
  padded,
384
+ isLoaded,
383
385
  shownTooltip,
384
386
  bars,
385
387
  select,
@@ -399,7 +401,8 @@ export default defineComponent({
399
401
  'column-chart--has-highlights': dataHasHighlights,
400
402
  'column-chart--hover': hover,
401
403
  'column-chart--stripped': stripped,
402
- 'column-chart--social-mode': socialMode
404
+ 'column-chart--social-mode': socialMode,
405
+ 'column-chart--loaded': isLoaded
403
406
  }"
404
407
  :style="{
405
408
  '--column-color': columnColor,
@@ -493,7 +496,7 @@ export default defineComponent({
493
496
 
494
497
  &__columns__item {
495
498
  fill: var(--column-color, var(--dark, $dark));
496
-
499
+
497
500
  &--highlight {
498
501
  fill: var(--column-highlight-color, var(--primary, $primary));
499
502
  }
@@ -529,6 +532,8 @@ export default defineComponent({
529
532
  left: 0;
530
533
 
531
534
  &__item {
535
+ $tooltip-bg: $body-emphasis-color;
536
+
532
537
  display: inline-flex;
533
538
  text-align: center;
534
539
  flex-direction: row;
@@ -545,6 +550,7 @@ export default defineComponent({
545
550
  color: $tooltip-color;
546
551
  margin: 0;
547
552
  padding: $tooltip-padding-y $tooltip-padding-x;
553
+
548
554
  &.fade-enter-active,
549
555
  &.fade-leave-active {
550
556
  transition: $transition-fade;