@tekkare/auriga 0.2.229 → 0.2.233

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.
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.0.0"
6
6
  },
7
- "version": "0.2.229"
7
+ "version": "0.2.233"
8
8
  }
@@ -44,6 +44,21 @@ export default defineComponent({
44
44
  type: Boolean,
45
45
  default: false
46
46
  },
47
+ // Drops the legend without touching the axes, the labels or the tooltip,
48
+ // which `small` also turns off. ApexCharts stops reserving the strip it
49
+ // sits in, so the plot takes the whole height.
50
+ hideLegend: {
51
+ type: Boolean,
52
+ default: false
53
+ },
54
+ // A stepline holds its value until the next change, so a series repeats
55
+ // the same number at every category in between and the chart ends up
56
+ // paved with identical labels. With this on, a point keeps its label only
57
+ // when it is the first, the last, or an actual change.
58
+ hideUnchangedLabels: {
59
+ type: Boolean,
60
+ default: false
61
+ },
47
62
  height: {
48
63
  type: String,
49
64
  default: "auto"
@@ -227,7 +242,7 @@ export default defineComponent({
227
242
  showForSingleSeries: true,
228
243
  showForNullSeries: true,
229
244
  showForZeroSeries: true,
230
- show: true,
245
+ show: !props.hideLegend,
231
246
  position: "bottom",
232
247
  fontFamily: "Figtree, sans-serif",
233
248
  fontSize: "14px",
@@ -253,6 +268,20 @@ export default defineComponent({
253
268
  },
254
269
  formatter: function(value, opt) {
255
270
  const seriesData = props.series;
271
+ if (props.hideUnchangedLabels && value !== null) {
272
+ const data = seriesData[opt.seriesIndex]?.data ?? [];
273
+ const isLast = opt.dataPointIndex === data.length - 1;
274
+ let previous = null;
275
+ for (let i = opt.dataPointIndex - 1; i >= 0; i -= 1) {
276
+ if (data[i] !== null && data[i] !== void 0) {
277
+ previous = data[i];
278
+ break;
279
+ }
280
+ }
281
+ if (previous !== null && !isLast && previous === value) {
282
+ return "";
283
+ }
284
+ }
256
285
  if (props.hasPercentages && value !== null) {
257
286
  const formatValue = new Intl.NumberFormat("fr-FR", {
258
287
  maximumFractionDigits: 1
@@ -12,6 +12,14 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
12
12
  type: BooleanConstructor;
13
13
  default: boolean;
14
14
  };
15
+ hideLegend: {
16
+ type: BooleanConstructor;
17
+ default: boolean;
18
+ };
19
+ hideUnchangedLabels: {
20
+ type: BooleanConstructor;
21
+ default: boolean;
22
+ };
15
23
  height: {
16
24
  type: StringConstructor;
17
25
  default: string;
@@ -74,6 +82,14 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
74
82
  type: BooleanConstructor;
75
83
  default: boolean;
76
84
  };
85
+ hideLegend: {
86
+ type: BooleanConstructor;
87
+ default: boolean;
88
+ };
89
+ hideUnchangedLabels: {
90
+ type: BooleanConstructor;
91
+ default: boolean;
92
+ };
77
93
  height: {
78
94
  type: StringConstructor;
79
95
  default: string;
@@ -123,8 +139,10 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
123
139
  options: Record<string, any>;
124
140
  colors: unknown[];
125
141
  french: boolean;
142
+ hideLegend: boolean;
126
143
  tooltipOptions: Record<string, any>;
127
144
  formatDate: boolean;
145
+ hideUnchangedLabels: boolean;
128
146
  dataType: string;
129
147
  hasPercentages: boolean;
130
148
  currency: string;
@@ -0,0 +1,176 @@
1
+ <template>
2
+ <div v-if="open" class="arg_terms_gate">
3
+ <div class="arg_terms_gate_card">
4
+ <span class="arg_terms_gate_eyebrow">
5
+ {{ updated ? "Terms of use updated" : "Terms of use" }}
6
+ </span>
7
+
8
+ <h2 class="arg_terms_gate_title">
9
+ {{ updated ? "Please review the new terms" : "Before you continue" }}
10
+ </h2>
11
+
12
+ <p class="arg_terms_gate_intro">
13
+ {{
14
+ updated
15
+ ? "Our terms of use have changed. Please read and accept the new version to keep using the platform."
16
+ : "Please read and accept our terms of use to start using the platform."
17
+ }}
18
+ </p>
19
+
20
+ <div class="arg_terms_gate_doc">
21
+ <div class="arg_terms_gate_doc_head">
22
+ <strong>{{ terms?.title || "Terms of use" }}</strong>
23
+ <span class="arg_terms_gate_version">
24
+ Version {{ terms?.version }}<template v-if="publishedLabel">, {{ publishedLabel }}</template>
25
+ </span>
26
+ </div>
27
+ <p v-if="terms?.summary" class="arg_terms_gate_summary">{{ terms.summary }}</p>
28
+ <a
29
+ v-if="terms?.url"
30
+ :href="terms.url"
31
+ target="_blank"
32
+ rel="noopener"
33
+ class="arg_terms_gate_link"
34
+ >
35
+ Read the full document
36
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
37
+ <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" stroke-linecap="round" />
38
+ <path d="M15 3h6v6M10 14 21 3" stroke-linecap="round" />
39
+ </svg>
40
+ </a>
41
+ </div>
42
+
43
+ <label class="arg_terms_gate_check">
44
+ <input v-model="checked" type="checkbox" :disabled="saving" />
45
+ <span>I have read and I accept the terms of use.</span>
46
+ </label>
47
+
48
+ <p v-if="error" class="arg_terms_gate_error">{{ error }}</p>
49
+
50
+ <button
51
+ type="button"
52
+ class="arg_terms_gate_button"
53
+ :disabled="!checked || saving"
54
+ @click="accept"
55
+ >
56
+ {{ saving ? "Saving..." : "Accept and continue" }}
57
+ </button>
58
+
59
+ <button type="button" class="arg_terms_gate_decline" :disabled="saving" @click="decline">
60
+ Decline and sign out
61
+ </button>
62
+ </div>
63
+ </div>
64
+ </template>
65
+
66
+ <script>
67
+ import { computed, defineComponent, onBeforeUnmount, onMounted, ref, watch } from "vue";
68
+ import { useRoute } from "#app";
69
+ const CACHE_KEY = "terms_accepted";
70
+ const IGNORED_PREFIX = "/auth";
71
+ export default defineComponent({
72
+ name: "argTermsGate",
73
+ props: {
74
+ api: {
75
+ type: Object,
76
+ required: true
77
+ },
78
+ /** Only check once the user is authenticated. */
79
+ enabled: {
80
+ type: Boolean,
81
+ default: true
82
+ }
83
+ },
84
+ emits: ["accepted", "declined"],
85
+ setup(props, { emit }) {
86
+ const route = useRoute();
87
+ const open = ref(false);
88
+ const terms = ref(null);
89
+ const updated = ref(false);
90
+ const checked = ref(false);
91
+ const saving = ref(false);
92
+ const error = ref("");
93
+ const publishedLabel = computed(() => {
94
+ if (!terms.value?.publishedAt) return "";
95
+ const date = new Date(terms.value.publishedAt);
96
+ return Number.isNaN(date.getTime()) ? "" : date.toLocaleDateString();
97
+ });
98
+ function cache(value) {
99
+ try {
100
+ if (value) sessionStorage.setItem(CACHE_KEY, "1");
101
+ else sessionStorage.removeItem(CACHE_KEY);
102
+ } catch {
103
+ }
104
+ }
105
+ function cached() {
106
+ try {
107
+ return !!sessionStorage.getItem(CACHE_KEY);
108
+ } catch {
109
+ return false;
110
+ }
111
+ }
112
+ async function check() {
113
+ if (!import.meta.client || !props.enabled || open.value || cached()) return;
114
+ if (route.path.startsWith(IGNORED_PREFIX)) return;
115
+ try {
116
+ const status = await props.api.get();
117
+ if (status?.accepted) {
118
+ cache(true);
119
+ return;
120
+ }
121
+ if (!status?.current) return;
122
+ terms.value = status.current;
123
+ updated.value = !!status.previouslyAccepted;
124
+ open.value = true;
125
+ } catch {
126
+ }
127
+ }
128
+ async function accept() {
129
+ if (!terms.value || !checked.value) return;
130
+ error.value = "";
131
+ saving.value = true;
132
+ try {
133
+ await props.api.accept(terms.value.id);
134
+ cache(true);
135
+ open.value = false;
136
+ emit("accepted", { version: terms.value.version });
137
+ } catch (e) {
138
+ const code = e?.statusCode || e?.response?.status;
139
+ if (code === 409) {
140
+ error.value = "A newer version was just published. Reloading...";
141
+ setTimeout(() => window.location.reload(), 1200);
142
+ } else {
143
+ error.value = e?.data?.message || "Could not record your acceptance. Please try again.";
144
+ }
145
+ } finally {
146
+ saving.value = false;
147
+ }
148
+ }
149
+ function decline() {
150
+ cache(false);
151
+ emit("declined");
152
+ }
153
+ let previousOverflow = "";
154
+ watch(open, (value) => {
155
+ if (!import.meta.client) return;
156
+ if (value) {
157
+ previousOverflow = document.body.style.overflow;
158
+ document.body.style.overflow = "hidden";
159
+ } else {
160
+ document.body.style.overflow = previousOverflow;
161
+ }
162
+ });
163
+ watch(() => props.enabled, check);
164
+ watch(() => route.path, check);
165
+ onMounted(check);
166
+ onBeforeUnmount(() => {
167
+ if (import.meta.client && open.value) document.body.style.overflow = previousOverflow;
168
+ });
169
+ return { open, terms, updated, checked, saving, error, publishedLabel, accept, decline };
170
+ }
171
+ });
172
+ </script>
173
+
174
+ <style scoped>
175
+ .arg_terms_gate{align-items:center;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);background-color:rgba(0,0,0,.6);display:flex;inset:0;justify-content:center;padding:24px;position:fixed;z-index:1000}.arg_terms_gate_card{background-color:var(--white,#fff);border-radius:16px;box-shadow:0 24px 60px rgba(0,0,0,.25);font-family:inherit;max-height:calc(100vh - 48px);max-width:480px;overflow-y:auto;padding:28px;width:100%}.arg_terms_gate_eyebrow{color:var(--primary);display:inline-block;font-size:13px;font-weight:600;margin-bottom:10px}.arg_terms_gate_title{color:var(--darkest);font-size:22px;font-weight:700;margin:0 0 8px}.arg_terms_gate_intro{color:var(--dark);font-size:14px;line-height:1.6;margin:0 0 20px}.arg_terms_gate_doc{border:1px solid var(--light);border-radius:10px;display:flex;flex-direction:column;gap:10px;margin-bottom:18px;padding:16px}.arg_terms_gate_doc_head{color:var(--darkest);display:flex;flex-direction:column;font-size:15px;gap:2px}.arg_terms_gate_version{color:var(--dark);font-size:12px;font-weight:400}.arg_terms_gate_summary{color:var(--dark);font-size:14px;line-height:1.6;margin:0;white-space:pre-line}.arg_terms_gate_link{align-items:center;color:var(--primary);display:inline-flex;font-size:14px;font-weight:600;gap:6px;text-decoration:none}.arg_terms_gate_link:hover{text-decoration:underline}.arg_terms_gate_check{align-items:flex-start;color:var(--darkest);cursor:pointer;display:flex;font-size:14px;gap:10px;line-height:1.5;margin-bottom:16px}.arg_terms_gate_check input{accent-color:var(--primary);cursor:pointer;flex-shrink:0;height:18px;margin:2px 0 0;width:18px}.arg_terms_gate_error{color:#d92d20;font-size:13px;margin:0 0 12px}.arg_terms_gate_button{background-color:var(--primary);border:none;border-radius:8px;color:var(--white,#fff);cursor:pointer;font-family:inherit;font-size:15px;font-weight:600;height:48px;transition:filter .15s,transform .1s;width:100%}.arg_terms_gate_button:hover:not(:disabled){filter:brightness(1.08);transform:translateY(-1px)}.arg_terms_gate_button:disabled{cursor:not-allowed;opacity:.5}.arg_terms_gate_decline{background-color:transparent;border:none;color:var(--dark);cursor:pointer;font-family:inherit;font-size:13px;height:40px;margin-top:10px;width:100%}.arg_terms_gate_decline:hover:not(:disabled){color:var(--darkest);text-decoration:underline}
176
+ </style>
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Blocking terms of use gate.
3
+ *
4
+ * Shows a modal the user cannot dismiss until they accept the latest published
5
+ * terms of use, on first login and again after each new version. Presentation
6
+ * and flow live here; the host app only supplies its own API client, so this
7
+ * component stays free of any auth or fetch wiring.
8
+ *
9
+ * Usage (in the app, once, next to <arg-style />):
10
+ * <arg-terms-gate
11
+ * :api="termsApi"
12
+ * :enabled="status === 'authenticated'"
13
+ * @accepted="onTermsAccepted"
14
+ * @declined="signOut({ callbackUrl: '/' })"
15
+ * />
16
+ *
17
+ * The /auth pages are skipped on purpose: the first-login onboarding lives
18
+ * there, and two blocking screens at once would stack on top of each other.
19
+ *
20
+ * `api` must expose:
21
+ * get(): Promise<{ current: TermsVersion | null, accepted: boolean, previouslyAccepted: boolean }>
22
+ * accept(termsVersionId: string): Promise<unknown>
23
+ */
24
+ interface TermsVersion {
25
+ id: string;
26
+ version: string;
27
+ title: string | null;
28
+ url: string;
29
+ summary: string | null;
30
+ publishedAt: string;
31
+ }
32
+ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
33
+ api: {
34
+ type: ObjectConstructor;
35
+ required: true;
36
+ };
37
+ /** Only check once the user is authenticated. */
38
+ enabled: {
39
+ type: BooleanConstructor;
40
+ default: boolean;
41
+ };
42
+ }>, {
43
+ open: import("vue").Ref<boolean, boolean>;
44
+ terms: import("vue").Ref<{
45
+ id: string;
46
+ version: string;
47
+ title: string | null;
48
+ url: string;
49
+ summary: string | null;
50
+ publishedAt: string;
51
+ } | null, TermsVersion | {
52
+ id: string;
53
+ version: string;
54
+ title: string | null;
55
+ url: string;
56
+ summary: string | null;
57
+ publishedAt: string;
58
+ } | null>;
59
+ updated: import("vue").Ref<boolean, boolean>;
60
+ checked: import("vue").Ref<boolean, boolean>;
61
+ saving: import("vue").Ref<boolean, boolean>;
62
+ error: import("vue").Ref<string, string>;
63
+ publishedLabel: import("vue").ComputedRef<string>;
64
+ accept: () => Promise<void>;
65
+ decline: () => void;
66
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("accepted" | "declined")[], "accepted" | "declined", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
67
+ api: {
68
+ type: ObjectConstructor;
69
+ required: true;
70
+ };
71
+ /** Only check once the user is authenticated. */
72
+ enabled: {
73
+ type: BooleanConstructor;
74
+ default: boolean;
75
+ };
76
+ }>> & Readonly<{
77
+ onAccepted?: ((...args: any[]) => any) | undefined;
78
+ onDeclined?: ((...args: any[]) => any) | undefined;
79
+ }>, {
80
+ enabled: boolean;
81
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
82
+ export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekkare/auriga",
3
- "version": "0.2.229",
3
+ "version": "0.2.233",
4
4
  "description": "Aurgia is a UI kit developped by Tekkare",
5
5
  "license": "MIT",
6
6
  "type": "module",