@magicfeedback/popup-sdk 1.1.8 → 1.3.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.
- package/README.md +113 -70
- package/dist/assets/assets/style.css +5 -1
- package/dist/chunk-R4B3K2ZW.mjs +176 -0
- package/dist/chunk-R4B3K2ZW.mjs.map +1 -0
- package/dist/chunk-XFA2Y6CK.mjs +197 -0
- package/dist/chunk-XFA2Y6CK.mjs.map +1 -0
- package/dist/index.d.mts +130 -14
- package/dist/index.d.ts +130 -14
- package/dist/index.js +426 -910
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +416 -44
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +60 -8
- package/dist/react-native.d.ts +60 -8
- package/dist/react-native.js +425 -911
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +415 -45
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-EZKDMLVZ.mjs → renderPopup-NJK7Z46E.mjs} +10 -870
- package/dist/renderPopup-NJK7Z46E.mjs.map +1 -0
- package/dist/{renderPopup-PS3NJDUR.mjs → renderPopup-ZYXOUBCP.mjs} +10 -870
- package/dist/renderPopup-ZYXOUBCP.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-4UXOEIAB.mjs +0 -172
- package/dist/chunk-4UXOEIAB.mjs.map +0 -1
- package/dist/chunk-XOTM5CHX.mjs +0 -193
- package/dist/chunk-XOTM5CHX.mjs.map +0 -1
- package/dist/renderPopup-EZKDMLVZ.mjs.map +0 -1
- package/dist/renderPopup-PS3NJDUR.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -6,10 +6,11 @@ Package name: `@magicfeedback/popup-sdk`
|
|
|
6
6
|
|
|
7
7
|
## What It Does
|
|
8
8
|
|
|
9
|
-
- Loads popup definitions from the Deepdots API
|
|
9
|
+
- Loads popup definitions from the Deepdots API
|
|
10
10
|
- Triggers popups by time on page, scroll depth, click, route exit, or host-driven events
|
|
11
11
|
- Emits lifecycle events so the host app can track popup activity
|
|
12
|
-
- Supports route targeting through `segments.path`
|
|
12
|
+
- Supports route and language targeting through `segments.path` / `segments.lang`
|
|
13
|
+
- Collects analytics (navigation, engagement, messaging, crashes) and sends them to Deepdots
|
|
13
14
|
- Falls back to a no-op renderer outside the browser
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
@@ -20,7 +21,7 @@ npm install @magicfeedback/popup-sdk
|
|
|
20
21
|
|
|
21
22
|
## Recommended Setup
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
Popup definitions are always managed remotely: the SDK fetches them from the Deepdots API at runtime.
|
|
24
25
|
|
|
25
26
|
```ts
|
|
26
27
|
import { DeepdotsPopups } from '@magicfeedback/popup-sdk';
|
|
@@ -28,7 +29,6 @@ import { DeepdotsPopups } from '@magicfeedback/popup-sdk';
|
|
|
28
29
|
const popups = new DeepdotsPopups();
|
|
29
30
|
|
|
30
31
|
popups.init({
|
|
31
|
-
mode: 'server',
|
|
32
32
|
nodeEnv: 'production',
|
|
33
33
|
apiKey: 'YOUR_PUBLIC_API_KEY',
|
|
34
34
|
userId: 'customer-123',
|
|
@@ -48,16 +48,10 @@ popups.autoLaunch();
|
|
|
48
48
|
|
|
49
49
|
Notes:
|
|
50
50
|
|
|
51
|
-
- `mode: 'server'` fetches popup definitions from Deepdots.
|
|
52
51
|
- `nodeEnv: 'production'` uses `https://api.deepdots.com`.
|
|
53
52
|
- `nodeEnv: 'development'` uses `https://api-dev.deepdots.com`.
|
|
54
53
|
- `userId` is optional, but recommended when popups are targeted per user.
|
|
55
|
-
- `autoLaunch()` can be called immediately after `init()
|
|
56
|
-
|
|
57
|
-
## When To Use Each Mode
|
|
58
|
-
|
|
59
|
-
- `server`: production integrations where popup definitions are managed remotely.
|
|
60
|
-
- `client`: local demos, QA, hardcoded fallback flows, and integration testing without API calls.
|
|
54
|
+
- `autoLaunch()` can be called immediately after `init()`: the SDK waits until the remote popups are loaded.
|
|
61
55
|
|
|
62
56
|
## Real Use Cases From `examples/`
|
|
63
57
|
|
|
@@ -69,7 +63,7 @@ Files:
|
|
|
69
63
|
- `examples/product.html`
|
|
70
64
|
- `examples/demo-sdk.js`
|
|
71
65
|
|
|
72
|
-
This flow initializes the SDK
|
|
66
|
+
This flow initializes the SDK against the API, auto-launches popups on page load, and logs popup events in the page UI. It is the closest example to a standard website integration.
|
|
73
67
|
|
|
74
68
|
### 2. Host-driven business event trigger
|
|
75
69
|
|
|
@@ -95,63 +89,13 @@ Files:
|
|
|
95
89
|
|
|
96
90
|
Exit triggers are queued when the user leaves a matching route and rendered on the next route after the configured delay. This is useful for "before you go" feedback flows that should appear after navigation, not before it.
|
|
97
91
|
|
|
98
|
-
### 4.
|
|
92
|
+
### 4. Local sandbox with debug logging
|
|
99
93
|
|
|
100
94
|
File:
|
|
101
95
|
|
|
102
96
|
- `examples/demo.html`
|
|
103
97
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
## Client Mode Example
|
|
107
|
-
|
|
108
|
-
Use `client` mode when you want to preload popup definitions yourself.
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
import { DeepdotsPopups } from '@magicfeedback/popup-sdk';
|
|
112
|
-
|
|
113
|
-
const popupDefinitions = [
|
|
114
|
-
{
|
|
115
|
-
id: 'popup-home-5s',
|
|
116
|
-
title: 'Help us improve',
|
|
117
|
-
message: '<p>Thanks for visiting our homepage.</p>',
|
|
118
|
-
triggers: [
|
|
119
|
-
{ type: 'time_on_page', value: 5 },
|
|
120
|
-
],
|
|
121
|
-
cooldown: [
|
|
122
|
-
{ answered: 'SHOWED', cooldownDays: 7 },
|
|
123
|
-
{ answered: 'COMPLETED', cooldownDays: 30 },
|
|
124
|
-
],
|
|
125
|
-
actions: {
|
|
126
|
-
accept: {
|
|
127
|
-
label: 'Open survey',
|
|
128
|
-
surveyId: 'survey-home-001',
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
surveyId: 'survey-home-001',
|
|
132
|
-
productId: 'product-main',
|
|
133
|
-
segments: {
|
|
134
|
-
path: ['/', '/pricing', '/#/home'],
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
];
|
|
138
|
-
|
|
139
|
-
const popups = new DeepdotsPopups();
|
|
140
|
-
|
|
141
|
-
popups.init({
|
|
142
|
-
mode: 'client',
|
|
143
|
-
debug: true,
|
|
144
|
-
popups: popupDefinitions,
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
popups.autoLaunch();
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Important:
|
|
151
|
-
|
|
152
|
-
- The property name is `triggers`, and it is now an array.
|
|
153
|
-
- `time_on_page` values are defined in seconds.
|
|
154
|
-
- `segments.path` can contain full URLs, path fragments such as `/pricing`, or hash routes such as `/#/home`.
|
|
98
|
+
Initializes the SDK against the API with `debug: true`, exposes the instance as `window.deepdots`, and logs every lifecycle event on the page. Useful for inspecting triggers, `getUserId()` / `getSessionId()`, and the analytics payload from the console.
|
|
155
99
|
|
|
156
100
|
## API Essentials
|
|
157
101
|
|
|
@@ -161,7 +105,6 @@ Initializes the SDK.
|
|
|
161
105
|
|
|
162
106
|
```ts
|
|
163
107
|
popups.init({
|
|
164
|
-
mode: 'server',
|
|
165
108
|
apiKey: 'YOUR_PUBLIC_API_KEY',
|
|
166
109
|
userId: 'customer-123',
|
|
167
110
|
nodeEnv: 'production',
|
|
@@ -174,9 +117,17 @@ Config fields:
|
|
|
174
117
|
- `apiKey?: string`
|
|
175
118
|
- `userId?: string`
|
|
176
119
|
- `nodeEnv?: 'development' | 'production'`
|
|
177
|
-
- `
|
|
178
|
-
- `
|
|
179
|
-
- `
|
|
120
|
+
- `debug?: boolean` — verbose logging
|
|
121
|
+
- `logger?: DeepdotsLogger` — replaces `console` for SDK logs
|
|
122
|
+
- `trackingEnabled?: boolean` — starts analytics on (default) or off, for consent flows
|
|
123
|
+
- `analytics?: { publicKey: string; integration: string }` — enables the real analytics
|
|
124
|
+
delivery. Without it the analytics channel stays in dry-run and only logs the payload.
|
|
125
|
+
- `appVersion?: string` — reported in the analytics device context
|
|
126
|
+
- `storage?: KeyValueStorage` — persistence override (React Native uses MMKV; the browser
|
|
127
|
+
defaults to `localStorage`)
|
|
128
|
+
- `platform?: 'web' | 'android' | 'ios'` — defaults to `'web'`
|
|
129
|
+
- `device?: DeviceInfo` — device context override, for hosts where the SDK cannot detect it
|
|
130
|
+
- `contactAttributes?: Record<string, string | number | boolean>`
|
|
180
131
|
- `language?: string` — BCP-47 tag (`'en'`, `'en-US'`, …) used for popup language
|
|
181
132
|
segmentation (`segments.lang`) and for the analytics context. If omitted it is
|
|
182
133
|
auto-detected from `navigator.language`, falling back to the `Intl` locale (which is
|
|
@@ -205,6 +156,14 @@ Example:
|
|
|
205
156
|
popups.triggerEvent('search');
|
|
206
157
|
```
|
|
207
158
|
|
|
159
|
+
### `triggerSurvey(surveyId, popupId?)`
|
|
160
|
+
|
|
161
|
+
Opens a specific survey directly, bypassing trigger evaluation.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
popups.triggerSurvey('survey-home-001');
|
|
165
|
+
```
|
|
166
|
+
|
|
208
167
|
### `markSurveyAnswered(surveyId)`
|
|
209
168
|
|
|
210
169
|
Marks a survey as answered so cooldown rules like `{ answered: 'COMPLETED', cooldownDays: 30 }` start applying.
|
|
@@ -224,6 +183,83 @@ popups.on('popup_shown', onShown);
|
|
|
224
183
|
popups.off('popup_shown', onShown);
|
|
225
184
|
```
|
|
226
185
|
|
|
186
|
+
## Analytics & Tracking
|
|
187
|
+
|
|
188
|
+
Beyond popups, the SDK collects analytics and sends it to Deepdots, linked by a persistent
|
|
189
|
+
anonymous `user_id`. Navigation (`page_view`), active time (`user_engagement`), device context,
|
|
190
|
+
session start/end and unhandled errors are automatic. The rest is instrumented by the host:
|
|
191
|
+
|
|
192
|
+
| Method | What it records |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `track(name, params?)` | Any custom event |
|
|
195
|
+
| `trackMessage(stage, options)` | Message funnel — push / in-app notifications |
|
|
196
|
+
| `trackSearch(query, resultsCount, params?)` | Searches, with `has_results` derived |
|
|
197
|
+
| `trackFunnelStep(funnel, step, taskId, params?)` | A funnel step, correlated by `taskId` |
|
|
198
|
+
| `trackFindabilityFriction(topic, params?)` | Friction signal |
|
|
199
|
+
| `enterMiniService(name, entryPointType?)` / `exitMiniService(name)` | Mini-service, with duration |
|
|
200
|
+
| `setUserAttributes(map)` / `setMetric(key, value)` | Breakdown dimensions / measurable values |
|
|
201
|
+
| `setUserId(userId?)` | Login, logout or account switch |
|
|
202
|
+
| `endSession()` | Closes the session explicitly |
|
|
203
|
+
| `setTrackingEnabled(enabled)` | Kill switch (consent) |
|
|
204
|
+
| `reportError(error, options?)` | A handled error |
|
|
205
|
+
| `previewAnalytics()` / `flushAnalytics()` | Inspect the pending payload / send it now |
|
|
206
|
+
|
|
207
|
+
### Messaging — `trackMessage(stage, options)`
|
|
208
|
+
|
|
209
|
+
Records one stage of a notification funnel. The SDK cannot observe the host's notification
|
|
210
|
+
system, so all three stages are instrumented by the host:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
popups.trackMessage('delivered', {
|
|
214
|
+
id: 'msg-42', // same id across the three stages
|
|
215
|
+
title: 'Summer sale',
|
|
216
|
+
channel: 'push', // 'push' | 'in_app'
|
|
217
|
+
campaign: 'summer_sale', // optional
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
popups.trackMessage('clicked', { id: 'msg-42', title: 'Summer sale', channel: 'push' });
|
|
221
|
+
|
|
222
|
+
popups.trackMessage('converted', {
|
|
223
|
+
id: 'msg-42', title: 'Summer sale', channel: 'push',
|
|
224
|
+
value: 49.9, currency: 'EUR', // optional
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Rules to follow when instrumenting it:
|
|
229
|
+
|
|
230
|
+
- **Send the three stages.** `delivered` is the denominator of CTR and conversion rate. Without
|
|
231
|
+
it those metrics cannot be computed. Send it when the message reaches the device, before the
|
|
232
|
+
user opens it — for in-app messages, when it is rendered.
|
|
233
|
+
- **The same `message_id` across the three stages.** It is what correlates the funnel. It must be
|
|
234
|
+
unique per send, not per campaign.
|
|
235
|
+
- **One `message_id` = one channel.** If a campaign goes out as a push *and* as an in-app
|
|
236
|
+
message, use two different `message_id` values sharing the same `campaign`.
|
|
237
|
+
- **One call per stage.** If your click handler can run through two paths (opening the
|
|
238
|
+
notification plus a deep link), make sure only one of them emits `clicked`.
|
|
239
|
+
|
|
240
|
+
Since **1.2.0** the SDK discards events that break these rules instead of forwarding them, and
|
|
241
|
+
warns on the console:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
[DeepdotsPopups] trackMessage discarded (channel_conflict): message_id "msg-42" was already reported on channel "push"; discarding "in_app"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
| Rule | What is discarded | `reason` |
|
|
248
|
+
|---|---|---|
|
|
249
|
+
| `channel` must be `push` or `in_app` | any other value | `invalid_channel` |
|
|
250
|
+
| Each `(message_id, stage)` is sent once | the 2nd call to the same stage of the same message | `duplicate_stage` |
|
|
251
|
+
| A `message_id` keeps its channel | events on a channel other than the first one seen | `channel_conflict` |
|
|
252
|
+
|
|
253
|
+
These checks last for the session and are per device, and they cap at 500 tracked `message_id`
|
|
254
|
+
values (oldest evicted first). A rejected event does not consume state: after a
|
|
255
|
+
`channel_conflict` on `in_app`, the same stage on the correct channel is still sent.
|
|
256
|
+
|
|
257
|
+
> **`delivered` on push has a structural limit.** On the device it is only observable if the app
|
|
258
|
+
> process receives the notification: a *data* push on Android, a `UNNotificationServiceExtension`
|
|
259
|
+
> with `mutable-content` on iOS. Notifications arriving while the app is killed, or with
|
|
260
|
+
> restricted permissions, will not fire it — so a client-side `delivered` count sits below the
|
|
261
|
+
> real one. For a reliable denominator, take `delivered` from your sending provider.
|
|
262
|
+
|
|
227
263
|
## Supported Trigger Types
|
|
228
264
|
|
|
229
265
|
Popup definitions use these trigger types:
|
|
@@ -236,7 +272,9 @@ Popup definitions use these trigger types:
|
|
|
236
272
|
|
|
237
273
|
## Popup Definition Shape
|
|
238
274
|
|
|
239
|
-
|
|
275
|
+
The API returns popup definitions with this structure. `triggers` is an array, `time_on_page`
|
|
276
|
+
values are in seconds, and `segments.path` accepts full URLs, path fragments such as `/pricing`,
|
|
277
|
+
or hash routes such as `/#/home`:
|
|
240
278
|
|
|
241
279
|
```ts
|
|
242
280
|
interface PopupDefinition {
|
|
@@ -279,9 +317,14 @@ interface PopupDefinition {
|
|
|
279
317
|
theme: 'light' | 'dark';
|
|
280
318
|
position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'center';
|
|
281
319
|
imageUrl: string | null;
|
|
320
|
+
font?: {
|
|
321
|
+
family: string; // clean family name, not a stack
|
|
322
|
+
url?: string; // woff2/ttf/otf, turned into an @font-face
|
|
323
|
+
};
|
|
282
324
|
};
|
|
283
325
|
segments?: {
|
|
284
326
|
path?: string[];
|
|
327
|
+
lang?: string[];
|
|
285
328
|
[key: string]: unknown;
|
|
286
329
|
};
|
|
287
330
|
}
|
|
@@ -316,7 +359,7 @@ interface DeepdotsEvent {
|
|
|
316
359
|
|
|
317
360
|
## Important Behavior Notes
|
|
318
361
|
|
|
319
|
-
- `segments.path`
|
|
362
|
+
- `segments.path` and `segments.lang` are the segments currently evaluated by the SDK runtime. If no language can be resolved, popups with `segments.lang` are shown rather than filtered out.
|
|
320
363
|
- `exit` triggers work across anchor navigation, hash navigation, `history.pushState()`, and `history.replaceState()`.
|
|
321
364
|
- Pending `exit` popups are stored in `sessionStorage` until they are shown or discarded.
|
|
322
365
|
- The default browser renderer uses Deepdots forms and applies button labels from `actions.accept`, `actions.start`, `actions.back`, and `actions.complete`.
|
|
@@ -78,7 +78,10 @@
|
|
|
78
78
|
margin-top: 5px;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
/* Cualificado con `div`: @magicfeedback/native reutiliza la clase "magicfeedback-radio"
|
|
82
|
+
también en el <input> de cada opción (rating), y sin el tag este selector lo capturaba
|
|
83
|
+
también, rompiendo su tamaño (el input pasaba a display:flex; width:100%). */
|
|
84
|
+
div.magicfeedback-radio {
|
|
82
85
|
display: flex;
|
|
83
86
|
flex-direction: column;
|
|
84
87
|
justify-content: flex-start;
|
|
@@ -86,6 +89,7 @@
|
|
|
86
89
|
width: 100%;
|
|
87
90
|
margin-right: 5px;
|
|
88
91
|
padding: 5px;
|
|
92
|
+
box-sizing: border-box;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
.magicfeedback-checkbox-container,
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/tracking/tracking-manager.ts
|
|
9
|
+
var InMemoryStorage = class {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.map = /* @__PURE__ */ new Map();
|
|
12
|
+
}
|
|
13
|
+
getItem(key) {
|
|
14
|
+
return this.map.has(key) ? this.map.get(key) : null;
|
|
15
|
+
}
|
|
16
|
+
setItem(key, value) {
|
|
17
|
+
this.map.set(key, value);
|
|
18
|
+
}
|
|
19
|
+
removeItem(key) {
|
|
20
|
+
this.map.delete(key);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
function createDefaultStorage() {
|
|
24
|
+
try {
|
|
25
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
26
|
+
const ls = window.localStorage;
|
|
27
|
+
const probe = "__deepdots_probe__";
|
|
28
|
+
ls.setItem(probe, "1");
|
|
29
|
+
ls.removeItem(probe);
|
|
30
|
+
return {
|
|
31
|
+
getItem: (k) => ls.getItem(k),
|
|
32
|
+
setItem: (k, v) => ls.setItem(k, v),
|
|
33
|
+
removeItem: (k) => ls.removeItem(k)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
return new InMemoryStorage();
|
|
39
|
+
}
|
|
40
|
+
var STORAGE_KEYS = {
|
|
41
|
+
userId: "deepdots.user_id",
|
|
42
|
+
firstSeen: "deepdots.user.first_seen",
|
|
43
|
+
contactAttributes: "deepdots.contact.attributes"
|
|
44
|
+
};
|
|
45
|
+
function buildSurveyIdentity(userId, sessionId, miniService = null, analyticsFeedbackSessionId = null) {
|
|
46
|
+
const profile = userId ? [{ key: "external-user-id", value: [userId] }] : [];
|
|
47
|
+
const metadata = [];
|
|
48
|
+
if (sessionId) metadata.push({ key: "session_id", value: [sessionId] });
|
|
49
|
+
if (userId) metadata.push({ key: "user_id", value: [userId] });
|
|
50
|
+
if (miniService) metadata.push({ key: "mini_service", value: [miniService] });
|
|
51
|
+
if (analyticsFeedbackSessionId) metadata.push({ key: "deepdots_analytics_feedback_session_id", value: [analyticsFeedbackSessionId] });
|
|
52
|
+
return { profile, metadata };
|
|
53
|
+
}
|
|
54
|
+
function defaultUuid() {
|
|
55
|
+
const g = typeof globalThis !== "undefined" ? globalThis : {};
|
|
56
|
+
if (g.crypto?.randomUUID) return g.crypto.randomUUID();
|
|
57
|
+
const bytes = new Uint8Array(16);
|
|
58
|
+
if (g.crypto?.getRandomValues) {
|
|
59
|
+
g.crypto.getRandomValues(bytes);
|
|
60
|
+
} else {
|
|
61
|
+
for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
62
|
+
}
|
|
63
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
64
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
65
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
|
|
66
|
+
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
|
|
67
|
+
}
|
|
68
|
+
var TrackingManager = class {
|
|
69
|
+
constructor(options) {
|
|
70
|
+
/** session_id cacheado, provisto por el backend (no se genera ni se persiste). */
|
|
71
|
+
this.sessionId = null;
|
|
72
|
+
this.userWasJustCreated = false;
|
|
73
|
+
this.storage = options.storage;
|
|
74
|
+
this.clientUserId = options.clientUserId;
|
|
75
|
+
this.now = options.now ?? (() => Date.now());
|
|
76
|
+
this.uuid = options.uuid ?? defaultUuid;
|
|
77
|
+
this.enabled = options.enabled ?? true;
|
|
78
|
+
}
|
|
79
|
+
isTrackingEnabled() {
|
|
80
|
+
return this.enabled;
|
|
81
|
+
}
|
|
82
|
+
setTrackingEnabled(enabled) {
|
|
83
|
+
this.enabled = enabled;
|
|
84
|
+
}
|
|
85
|
+
/** Resuelve el user_id según la regla: cliente > persistido > generar+persistir. */
|
|
86
|
+
getUserId() {
|
|
87
|
+
if (!this.enabled) return null;
|
|
88
|
+
if (this.clientUserId) return this.clientUserId;
|
|
89
|
+
const persisted = this.storage.getItem(STORAGE_KEYS.userId);
|
|
90
|
+
if (persisted) return persisted;
|
|
91
|
+
const id = this.uuid();
|
|
92
|
+
this.storage.setItem(STORAGE_KEYS.userId, id);
|
|
93
|
+
this.storage.setItem(STORAGE_KEYS.firstSeen, String(this.now()));
|
|
94
|
+
this.userWasJustCreated = true;
|
|
95
|
+
return id;
|
|
96
|
+
}
|
|
97
|
+
/** `true` solo en la primera sesión tras crear el user_id propio del SDK. */
|
|
98
|
+
isNewUser() {
|
|
99
|
+
return this.userWasJustCreated;
|
|
100
|
+
}
|
|
101
|
+
/** session_id actual provisto por el backend, o null si aún no llegó. */
|
|
102
|
+
getSessionId() {
|
|
103
|
+
if (!this.enabled) return null;
|
|
104
|
+
return this.sessionId;
|
|
105
|
+
}
|
|
106
|
+
/** Cachea el session_id devuelto por el backend (respuesta de POST /sdk/popups). */
|
|
107
|
+
setSessionId(sessionId) {
|
|
108
|
+
if (!this.enabled) return;
|
|
109
|
+
this.sessionId = sessionId;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/ui/font.ts
|
|
114
|
+
var FORMAT_BY_EXT = {
|
|
115
|
+
woff2: "woff2",
|
|
116
|
+
woff: "woff",
|
|
117
|
+
ttf: "truetype",
|
|
118
|
+
otf: "opentype"
|
|
119
|
+
};
|
|
120
|
+
function fontFormatFromUrl(url) {
|
|
121
|
+
const clean = url.split("?")[0].split("#")[0];
|
|
122
|
+
const ext = clean.slice(clean.lastIndexOf(".") + 1).toLowerCase();
|
|
123
|
+
return FORMAT_BY_EXT[ext];
|
|
124
|
+
}
|
|
125
|
+
function sanitizeFamily(family) {
|
|
126
|
+
return family.replace(/[^A-Za-z0-9 ._-]/g, "").trim();
|
|
127
|
+
}
|
|
128
|
+
function isSafeFontUrl(url) {
|
|
129
|
+
if (!/^(https?:|data:)/i.test(url)) return false;
|
|
130
|
+
return !/[\x00-\x20"<>\\]/.test(url);
|
|
131
|
+
}
|
|
132
|
+
function buildFontFamilyValue(family) {
|
|
133
|
+
const safe = sanitizeFamily(family);
|
|
134
|
+
return `"${safe}", -apple-system, system-ui, sans-serif`;
|
|
135
|
+
}
|
|
136
|
+
function buildFontFaceCss(family, url) {
|
|
137
|
+
if (!url || !isSafeFontUrl(url)) return "";
|
|
138
|
+
const safe = sanitizeFamily(family);
|
|
139
|
+
const fmt = fontFormatFromUrl(url);
|
|
140
|
+
const src = fmt ? `url("${url}") format("${fmt}")` : `url("${url}")`;
|
|
141
|
+
return `@font-face{font-family:"${safe}";src:${src};font-display:swap;}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/assets/style.css
|
|
145
|
+
var style_default = '.magicfeedback-container {\n width: 100%;\n}\n\n\n.magicfeedback-success-message,\n.magicfeedback-info-message,\n.magicfeedback-start-message,\n.magicfeedback-container{\n animation-duration: 0.5s;\n animation-name: animate-fade;\n animation-delay: 0.25s;\n animation-fill-mode: backwards;\n\n font-style: normal;\n text-align: justify;\n font-weight: 400;\n font-size: 1.2rem;\n color: #475569;\n line-height: 1.5;\n}\n\n@keyframes animate-fade {\n 0% { opacity: 0; }\n 100% { opacity: 1; }\n}\n\n.magicfeedback-form {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n}\n.magicfeedback-questions {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: flex-start;\n width: 100%;\n}\n\n.magicfeedback-div {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: flex-start;\n margin-bottom: 10px;\n width: 100%;\n}\n\n.magicfeedback-label {\n margin-bottom: 25px;\n margin-top: 20px;\n font-weight: 400;\n font-family: inherit;\n font-size: 1.2rem;\n color: #212529;\n text-align: start;\n word-wrap: break-word;\n white-space: normal;\n\n}\n\n.magicfeedback-checkbox {\n display: flex;\n flex-direction: column;\n flex-wrap: wrap;\n width: 100%;\n}\n\n.magicfeedback-checkbox-container {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n margin-bottom: 5px;\n margin-top: 5px;\n}\n\n/* Cualificado con `div`: @magicfeedback/native reutiliza la clase "magicfeedback-radio"\n tambi\xE9n en el <input> de cada opci\xF3n (rating), y sin el tag este selector lo capturaba\n tambi\xE9n, rompiendo su tama\xF1o (el input pasaba a display:flex; width:100%). */\ndiv.magicfeedback-radio {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n flex-wrap: wrap;\n width: 100%;\n margin-right: 5px;\n padding: 5px;\n box-sizing: border-box;\n}\n\n.magicfeedback-checkbox-container,\n.magicfeedback-radio-container {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n margin: 5px;\n padding: 5px 0;\n background-color: #fff;\n border-radius: 5px;\n box-shadow: 0 0 3px #ccc;\n}\n\n.deepdots-popup input::-webkit-outer-spin-button,\n.deepdots-popup input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n -moz-appearance: textfield;\n}\n\n.magicfeedback-checkbox input[type="checkbox"] {\n margin-right: 5px;\n margin-left: 5px;\n width: 20px;\n height: 20px;\n cursor: pointer;\n}\n\n\n.magicfeedback-checkbox label,\n.magicfeedback-radio-container label {\n margin-left: 5px;\n font-weight: 400;\n font-family: inherit;\n font-size: 16px;\n color: #212529;\n text-align: start;\n width: 100%;\n}\n\n.deepdots-popup p {\n margin-bottom: 0px;\n font-size: 12px;\n}\n\n.deepdots-popup h2 {\n line-height: 1.66;\n margin: 0;\n padding: 0;\n font-weight: 900;\n color: #222;\n font-family: var(--deepdots-font, \'Montserrat\');\n font-size: 18px;\n text-transform: uppercase;\n text-align: center;\n margin-bottom: 40px;\n}\n\n/* Los controles de formulario no heredan font-family por defecto: cuando hay\n fuente custom (clase deepdots-has-font), los forzamos a usar la del contenedor. */\n.deepdots-popup.deepdots-has-font button,\n.deepdots-popup.deepdots-has-font input,\n.deepdots-popup.deepdots-has-font textarea,\n.deepdots-popup.deepdots-has-font select {\n font-family: var(--deepdots-font, inherit);\n}\n\n.deepdots-popup h6 {\n margin-bottom: 1rem;\n}\n\n.deepdots-popup input[type="date"],\n.deepdots-popup input[type="text"],\n.deepdots-popup input[type="number"],\n.deepdots-popup input[type="email"],\n.deepdots-popup input[type="password"],\n.deepdots-popup textarea,\n.deepdots-popup select {\n box-sizing: border-box;\n padding: 10px;\n margin: 10px 0;\n width: 100%;\n border: 2px solid #ccc; /* Default border color - you can customize this */\n border-radius: 5px;\n font-size: 18px;\n outline: none;\n transition: border-color 0.3s ease;\n min-height: 50px;\n font-family: inherit;\n -webkit-appearance: none;\n}\n\n.deepdots-popup select {\n background-image: linear-gradient(45deg, transparent 50%, gray 50%),\n linear-gradient(135deg, gray 50%, transparent 50%),\n linear-gradient(to right, #ccc, #ccc);\n background-position: calc(100% - 20px) calc(1em + 2px),\n calc(100% - 15px) calc(1em + 2px),\n calc(100% - 2.5em) 0.5em;\n background-size: 5px 5px,\n 5px 5px,\n 1px 1.5em;\n background-repeat: no-repeat;\n}\n\n.magicfeedback-boolean-container {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n margin: 10px 0;\n width: 100%;\n}\n\n.magicfeedback-boolean-container label {\n margin-left: 10px;\n font-weight: 400;\n font-size: 16px;\n width: 100%;\n padding-top: 5px;\n}\n\n.magicfeedback-boolean-option {\n margin-right: 10px;\n margin-left: 10px;\n width: 100%;\n height: 38px;\n border-radius: 8px;\n font-size: 16px;\n cursor: pointer;\n border: 1px solid #ced4da !important;\n}\n\n.magicfeedback-boolean-option label {\n font-weight: 400;\n font-size: 16px;\n color: #475569;\n cursor: pointer;\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n border-radius: inherit;\n}\n\n.magicfeedback-boolean-option input[type=radio] + label:hover {\n color: #007bff;\n}\n\n.magicfeedback-boolean-option input[type=radio]:checked + label {\n color: #007bff;\n background-color: #cadff8;\n}\n\n.deepdots-popup input[type="radio"] {\n padding: 20px;\n margin: 10px 0 10px 15px;\n width: 40px;\n background-color: #fff;\n box-shadow: none;\n}\n\n/* Style for focused input/select */\n.deepdots-popup input:focus, .deepdots-popup select:focus {\n border-color: #4CAF50; /* Highlight color when focused - you can customize this */\n}\n\n/* Style for invalid input (example: validation error) */\n.deepdots-popup input.error, .deepdots-popup select.error {\n border-color: #ff3333; /* Red color for error state - you can customize this */\n}\n\n.magicfeedback-action-container {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n margin-top: 10px;\n width: 100%;\n}\n\n.magicfeedback-action-container .magicfeedback-submit {\n cursor: pointer;\n padding: 10px;\n margin: 10px 0;\n width: 100px;\n border-radius: 8px;\n font-size: 16px;\n outline: none;\n transition: border-color 0.3s ease;\n background-color: #1E293B;\n color: white;\n}\n\n.magicfeedback-action-container .magicfeedback-back {\n cursor: pointer;\n padding: 10px;\n margin: 10px 0;\n width: 100px;\n border: 0;\n border-radius: 8px;\n font-size: 16px;\n outline: none;\n transition: border-color 0.3s ease;\n background-color: transparent;\n color: #212529;\n}\n\n\n\n/* Rating styles */\n.magicfeedback-rating {\n width: 100%;\n}\n\n.magicfeedback-rating-container {\n display: flex;\n flex-direction: row;\n justify-content: space-around;\n flex-wrap: wrap;\n align-items: center;\n\n margin: 15px 5px;\n padding: 0;\n\n list-style: none;\n font-size: 1.5rem;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n}\n\n.magicfeedback-rating-placeholder-value,\n.magicfeedback-rating-placeholder{\n text-align: center;\n align-content: center;\n justify-content: center;\n text-wrap: wrap;\n}\n\n.magicfeedback-rating-option {\n margin: 0 0.5rem;\n padding: 0;\n}\n\n@media (max-width: 1024px) {\n .magicfeedback-rating-option {\n margin: 10px 0;\n padding: 0;\n width: 20%;\n display: flex;\n justify-content: center;\n }\n\n .magicfeedback-rating-placeholder-value {\n font-size: 0.75rem;\n margin-bottom: 1px;\n }\n .magicfeedback-rating-placeholder{\n justify-content: center;\n }\n\n .magicfeedback-rating-container {\n flex-wrap: wrap;\n justify-content: flex-start;\n }\n}\n\n\n.magicfeedback-rating-option-label-container {\n display: flex;\n flex-direction: column;\n}\n\n\n.magicfeedback-rating-option-label-container label {\n display: inline-block;\n text-align: center;\n margin-bottom: 0;\n font-weight: 600;\n font-size: 18px;\n color: #475569;\n}\n\n.magicfeedback-rating-option-label-container input[type=radio] {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n/* IMAGE STYLES */\n.magicfeedback-rating-option-label-container input[type=radio] + img {\n cursor: pointer;\n height: 48px;\n width: 48px;\n}\n\n/* LABEL STYLES */\n.magicfeedback-rating-option-label-container input[type=radio] + label {\n cursor: pointer;\n}\n\n/* CHECKED STYLES */\n.magicfeedback-rating-option-label-container input[type=radio]:checked + img {\n box-shadow: #007bff 0px 1px 4px, #007BFFA0 0px 0px 0px 2px;\n border-radius: 50%;\n}\n\n.magicfeedback-rating-number {\n width: 100%;\n}\n.magicfeedback-rating-container,\n.magicfeedback-rating-number-container {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n\n margin: 15px 0;\n padding: 0;\n\n list-style: none;\n font-size: 1.5rem;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n}\n\n/* Cuando hay placeholders (Very bad / Very good), el placeholder ocupa\n el 100% de la primera l\xEDnea y los boxes bajan a la siguiente */\n.magicfeedback-rating-number-container--with-placeholder {\n flex-wrap: wrap;\n align-items: flex-start;\n}\n\n.magicfeedback-rating-number-container--with-placeholder .magicfeedback-rating-placeholder {\n width: 100%;\n margin-bottom: 8px;\n}\n\n.magicfeedback-rating-number-container--with-placeholder .magicfeedback-rating-number-option {\n width: auto;\n flex: 1;\n}\n\n.magicfeedback-rating-number-option {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n margin: 5px;\n padding: 0;\n list-style: none;\n font-size: 1.5rem;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n width: 100%;\n}\n\n.magicfeedback-rating-number-option-row{\n max-width: 100% !important;\n}\n\n.magicfeedback-rating-number-top-placeholder,\n.magicfeedback-rating-number-bottom-placeholder{\n text-align: center !important;\n align-content: center;\n justify-content: center;\n text-wrap: wrap;\n\n font-size: 15px;\n color: #475569;\n\n width: 100%;\n margin: auto;\n}\n\n@media (min-width: 650px) {\n .magicfeedback-rating-number-option-row +.magicfeedback-rating-number-container-ltr:first-child,\n .magicfeedback-rating-number-option-row + +.magicfeedback-rating-number-container-rtl:last-child {\n margin-left: 0;\n width: calc(100% - 10px);\n }\n\n .magicfeedback-rating-number-option-row +.magicfeedback-rating-number-container-rtl:first-child,\n .magicfeedback-rating-number-option-row + +.magicfeedback-rating-number-container-ltr:last-child {\n margin-right: 0;\n width: calc(100% - 10px);\n }\n}\n\n\n.magicfeedback-rating-number-option-label-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n}\n\n.magicfeedback-rating-number-option-label-container label {\n display: flex;\n align-items: center;\n justify-content: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin: 0 10px;\n font-weight: 600;\n font-size: 14px;\n color: #475569;\n cursor: pointer;\n width: 100%;\n height: 100%;\n min-height: 50px;\n text-align: center;\n padding: 0;\n border-radius: 4px;\n border: 1px solid #ced4da;\n background-color: rgba(246, 246, 246, 0.3);\n transition: background-color 0.15s ease-in-out;\n}\n\n.magicfeedback-rating-number-option-label-container input[type=radio] {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.magicfeedback-rating-number-option-label-container input[type=radio] + label {\n cursor: pointer;\n}\n\n.magicfeedback-rating-number-option-label-container input[type=radio] + label:hover,\n.magicfeedback-rating-number-option-label-container input[type=radio]:checked + label {\n color: #007bff;\n border: 1px solid #007bff;\n background-color: rgba(86, 168, 253, 0.29);\n}\n\n.magicfeedback-rating-number-container-column{\n align-items: flex-start !important;\n}\n\n@media (max-width: 650px) {\n .magicfeedback-rating-number-container-ltr {\n justify-content: center;\n flex-wrap: wrap;\n }\n\n .magicfeedback-rating-number-container-rtl {\n justify-content: center;\n flex-wrap: wrap-reverse;\n }\n\n .magicfeedback-rating-number-option {\n max-width: 100%;\n height: 55px;\n margin: 5px 0;\n }\n}\n\n.magicfeedback-rating-star {\n width: 100%;\n}\n\n.magicfeedback-rating-star-container {\n display: flex !important;\n flex-direction: row !important;\n justify-content: center !important;\n align-items: center !important;\n flex-wrap: wrap;\n margin: 15px 5px;\n padding: 0;\n list-style: none;\n font-size: 1.5rem;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n}\n\n.magicfeedback-rating-star-option:not(:checked) > .rating__star {\n float: right;\n width: 1em;\n overflow: hidden;\n white-space: nowrap;\n cursor: pointer;\n font-size: 30px;\n color: #ccc;\n margin: 0 10px;\n}\n\n.magicfeedback-rating-star-option:not(:checked) > .rating__star:hover,\n.magicfeedback-rating-star-option:not(:checked) > .rating__star:hover ~ span {\n color: #deb217 !important;\n}\n\n.magicfeedback-rating-star-selected {\n color: #ffc700 !important;\n}\n\n\n/* Style for multiple choice image */\n.magicfeedback-image-option-label-container:hover {\n cursor: pointer;\n border: 0 !important;\n}\n\n.magicfeedback-image-option-label-container input[type=radio]:checked + img,\n.magicfeedback-image-option-label-container input[type=checkbox]:checked + img {\n border: 1px solid #007bff !important;\n box-shadow: 0 0 3px #007bff !important;\n}\n\n.magicfeedback-image-option-label-container input[type=radio] + img,\n.magicfeedback-image-option-label-container input[type=checkbox] + img {\n border: 1px solid transparent !important;\n}\n\n.magicfeedback-image-option-label-container{\n margin: 10px !important;\n height: calc(100% - 20px) !important;\n width: calc(100% - 20px) !important;\n}\n\n.magicfeedback-multiple-choice-image-option {\n display: flex;\n height: auto !important;\n box-shadow: 0 0 3px #ccc;\n}\n\n.magicfeedback-multiple-choice-image-label {\n font-size: 1rem;\n}\n\n.magicfeedback-consent-container {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n margin: 10px 0;\n width: 100%;\n}\n\n.magicfeedback-consent-container label {\n margin-left: 10px;\n}\n\n.magicfeedback-start-message-container {\n /* ... add your start message container styles here ... */\n}\n\n.magicfeedback-success-message,\n.magicfeedback-info-message,\n.magicfeedback-start-message {\n margin: 35px 5px;\n}\n\n.magicfeedback-success-message p,\n.magicfeedback-info-message p,\n.magicfeedback-start-message p {\n font-style: normal;\n text-align: start;\n font-weight: 400;\n font-size: 16px;\n color: #475569;\n line-height: 1.5;\n}\n\n.magicfeedback-success-message img,\n.magicfeedback-info-message img,\n.magicfeedback-start-message img {\n width: 400px;\n max-width: 80%;\n height: auto;\n margin: 10px auto;\n}\n\n.magicfeedback-counter,\n.magicfeedback-point-system-total {\n margin: 10px 0 0 0 !important;\n display: flex !important;\n justify-content: right !important;\n align-items: center !important;\n width: 100%;\n}\n\n.magicfeedback-skip-container {\n background-color: transparent !important;\n border: 0 !important;\n color: #999999FF !important;\n padding: 0 !important;\n margin: 0 !important;\n cursor: pointer !important;\n font-size: 0.5em !important;\n font-weight: 400 !important;\n box-shadow: 0 0 0 !important;\n\n &:hover {\n color: #0066ff;\n }\n}\n\n.magicfeedback-skip-container input[type=checkbox] {\n border-color: #999999FF !important;\n border: 0 !important;\n cursor: pointer !important;\n\n}\n\n.magicfeedback-multi-question-matrix,\n.magicfeedback-point-system,\n.magicfeedback-priority-list {\n width: 100% !important;\n}\n\n.magicfeedback-multi-question-matrix-container,\n.magicfeedback-point-system-container,\n.magicfeedback-priority-list-container {\n width: 100% !important;\n overflow-x: auto;\n}\n\n.magicfeedback-point-system-list,\n.magicfeedback-priority-list-list,\n.magicfeedback-multi-question-matrix-table {\n width: 100% !important;\n padding: 0 !important;\n overflow-x: auto;\n}\n\n.magicfeedback-multi-question-matrix {\n position: relative !important;\n}\n\n.magicfeedback-multi-question-matrix-container {\n overflow: auto !important;\n padding-bottom: 10px;\n}\n\n.magicfeedback-multi-question-matrix-label{\n text-align: left !important;\n}\n.magicfeedback-priority-list-item {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n padding: 10px 0 10px 10px;\n\n border: 1px solid #ced4da;\n border-radius: 5px;\n margin: 10px 0 !important;\n}\n\n.magicfeedback-point-system-item {\n height: 45px;\n margin: 5px 0 !important;\n}\n\n.magicfeedback-point-system-item label,\n.magicfeedback-priority-list-item-label {\n font-family: inherit;\n font-size: 18px !important;\n color: #212529;\n text-align: start;\n word-wrap: break-word;\n white-space: normal;\n padding: 0 10px 0 0;\n}\n\n.magicfeedback-point-system-input-container {\n padding: 0 10px 0 0;\n border-radius: 5px;\n font-size: 14px;\n border: 1px solid #ced4da;\n height: 42px;\n margin: auto 0;\n}\n\n.magicfeedback-multi-question-matrix-table thead {\n margin-bottom: 20px;\n}\n\n.magicfeedback-multi-question-matrix-table th{\n text-align: center;\n font-weight: 400 !important;\n font-size: 1rem !important;\n padding: 0 10px !important;\n}\n\n.magicfeedback-multi-question-matrix-table th,\n.magicfeedback-multi-question-matrix-table td label,\n.magicfeedback-priority-list-arrow-up,\n.magicfeedback-priority-list-arrow-down {\n font-style: normal !important;\n font-weight: 400 !important;\n font-size: 1rem !important;\n color: #475569 !important;\n cursor: pointer !important;\n margin: 0 !important;\n line-height: inherit;\n}\n\n.magicfeedback-multi-question-matrix-table tr {\n align-items: center;\n margin: 5px 0;\n}\n\n.magicfeedback-multi-question-matrix-row-tr{\n border-radius: 5px;\n margin: 10px 0 !important;\n}\n\n.magicfeedback-multi-question-matrix-row-tr:nth-child(odd) {\n background-color: #f1f6f6; /* Light gray background for odd rows */\n}\n\n.magicfeedback-multi-question-matrix-row-tr:nth-child(even) {\n background-color: #ffffff; /* White background for even rows */\n}\n\n.magicfeedback-multi-question-matrix-row-label {\n text-align: left !important;\n}\n\n.magicfeedback-point-system-item span {\n color: #475569 !important;\n}\n\n.magicfeedback-point-system-item input {\n padding: 0;\n margin: 0 !important;\n width: auto !important;\n min-width: 100px !important;\n height: auto !important;\n min-height: 42px;\n cursor: pointer;\n font-size: 14px;\n text-align: center !important;\n}\n\n.magicfeedback-multi-question-matrix-table input {\n margin: auto !important;\n padding: 20px !important;\n width: 100% !important;\n height: 15px;\n border: 1px solid #ced4da;\n cursor: pointer;\n}\n\n.magicfeedback-image{\n max-height: 400px !important;\n height: auto !important;\n max-width: 100% !important;\n margin: 10px auto !important;\n}\n\n.magicfeedback-error{\n margin-top: 5px;\n}';
|
|
146
|
+
|
|
147
|
+
// src/util/logger.ts
|
|
148
|
+
var active = console;
|
|
149
|
+
function setLogger(logger) {
|
|
150
|
+
active = logger ?? console;
|
|
151
|
+
}
|
|
152
|
+
function sdkLog(...args) {
|
|
153
|
+
active.log(...args);
|
|
154
|
+
}
|
|
155
|
+
function sdkWarn(...args) {
|
|
156
|
+
(active.warn ?? active.log)(...args);
|
|
157
|
+
}
|
|
158
|
+
function sdkError(...args) {
|
|
159
|
+
(active.error ?? active.log)(...args);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export {
|
|
163
|
+
__require,
|
|
164
|
+
createDefaultStorage,
|
|
165
|
+
STORAGE_KEYS,
|
|
166
|
+
buildSurveyIdentity,
|
|
167
|
+
TrackingManager,
|
|
168
|
+
buildFontFamilyValue,
|
|
169
|
+
buildFontFaceCss,
|
|
170
|
+
style_default,
|
|
171
|
+
setLogger,
|
|
172
|
+
sdkLog,
|
|
173
|
+
sdkWarn,
|
|
174
|
+
sdkError
|
|
175
|
+
};
|
|
176
|
+
//# sourceMappingURL=chunk-R4B3K2ZW.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tracking/tracking-manager.ts","../src/ui/font.ts","../src/assets/style.css","../src/util/logger.ts"],"sourcesContent":["/**\n * Identidad + sesión para el tracking de navegación (Fase 1).\n * Implementa el \"Contrato común Web+KMP\".\n *\n * Modelo: el SDK gestiona el `user_id` (persistente). El `session_id` es propiedad\n * del BACKEND: llega en la respuesta de `POST /sdk/popups` y se cachea; el backend\n * cose sesiones por `user_id` + ventana. El SDK NO genera ni expira sesiones.\n *\n * Sin dependencias de DOM: recibe el storage y (opcionalmente) el reloj y el\n * generador de uuid por inyección. El binding a `localStorage` vive en `createDefaultStorage`.\n */\n\n/** Store clave/valor mínimo (espejo conceptual del `KeyValueStorage` de KMP). */\nexport interface KeyValueStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/** Impl en memoria — usada en tests y como fallback sin `localStorage`. */\nexport class InMemoryStorage implements KeyValueStorage {\n private map = new Map<string, string>();\n getItem(key: string): string | null {\n return this.map.has(key) ? (this.map.get(key) as string) : null;\n }\n setItem(key: string, value: string): void {\n this.map.set(key, value);\n }\n removeItem(key: string): void {\n this.map.delete(key);\n }\n}\n\n/** Storage por defecto en Web: `localStorage` si está disponible, si no in-memory. */\nexport function createDefaultStorage(): KeyValueStorage {\n try {\n if (typeof window !== 'undefined' && window.localStorage) {\n const ls = window.localStorage;\n const probe = '__deepdots_probe__';\n ls.setItem(probe, '1');\n ls.removeItem(probe);\n return {\n getItem: (k) => ls.getItem(k),\n setItem: (k, v) => ls.setItem(k, v),\n removeItem: (k) => ls.removeItem(k),\n };\n }\n } catch {\n // cae a in-memory\n }\n return new InMemoryStorage();\n}\n\n/** Claves de storage — namespace `deepdots.*`. Solo identidad (la sesión no se persiste). */\nexport const STORAGE_KEYS = {\n userId: 'deepdots.user_id',\n firstSeen: 'deepdots.user.first_seen',\n contactAttributes: 'deepdots.contact.attributes',\n} as const;\n\n/** Par clave/valor con el formato `NativeAnswer` que espera `@magicfeedback/native`. */\nexport interface IdentityAnswer {\n key: string;\n value: string[];\n}\n\n/**\n * Construye el `profile` y el `metadata` de identidad para inyectar en el survey\n * (`magicfeedback.form(appId, publicKey, profile, metadata)`), según el contrato §5.\n * `session_id` solo se incluye si el backend ya lo proveyó.\n */\nexport function buildSurveyIdentity(\n userId: string | null,\n sessionId: string | null,\n miniService: string | null = null,\n analyticsFeedbackSessionId: string | null = null,\n): { profile: IdentityAnswer[]; metadata: IdentityAnswer[] } {\n const profile: IdentityAnswer[] = userId ? [{ key: 'external-user-id', value: [userId] }] : [];\n const metadata: IdentityAnswer[] = [];\n if (sessionId) metadata.push({ key: 'session_id', value: [sessionId] });\n if (userId) metadata.push({ key: 'user_id', value: [userId] });\n if (miniService) metadata.push({ key: 'mini_service', value: [miniService] });\n if (analyticsFeedbackSessionId) metadata.push({ key: 'deepdots_analytics_feedback_session_id', value: [analyticsFeedbackSessionId] });\n return { profile, metadata };\n}\n\nexport interface TrackingManagerOptions {\n storage: KeyValueStorage;\n /** Id provisto por el cliente; si existe se usa y NO se persiste. */\n clientUserId?: string;\n /** Reloj inyectable (default `Date.now`). */\n now?: () => number;\n /** Generador de uuid inyectable (default uuid v4). */\n uuid?: () => string;\n /** Estado inicial del tracking (default `true`). */\n enabled?: boolean;\n}\n\nfunction defaultUuid(): string {\n const g = (typeof globalThis !== 'undefined' ? globalThis : {}) as {\n crypto?: { randomUUID?: () => string; getRandomValues?: (a: Uint8Array) => Uint8Array };\n };\n if (g.crypto?.randomUUID) return g.crypto.randomUUID();\n const bytes = new Uint8Array(16);\n if (g.crypto?.getRandomValues) {\n g.crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0'));\n return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex\n .slice(6, 8)\n .join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`;\n}\n\nexport class TrackingManager {\n private storage: KeyValueStorage;\n private clientUserId?: string;\n private now: () => number;\n private uuid: () => string;\n private enabled: boolean;\n\n /** session_id cacheado, provisto por el backend (no se genera ni se persiste). */\n private sessionId: string | null = null;\n private userWasJustCreated = false;\n\n constructor(options: TrackingManagerOptions) {\n this.storage = options.storage;\n this.clientUserId = options.clientUserId;\n this.now = options.now ?? (() => Date.now());\n this.uuid = options.uuid ?? defaultUuid;\n this.enabled = options.enabled ?? true;\n }\n\n isTrackingEnabled(): boolean {\n return this.enabled;\n }\n\n setTrackingEnabled(enabled: boolean): void {\n this.enabled = enabled;\n }\n\n /** Resuelve el user_id según la regla: cliente > persistido > generar+persistir. */\n getUserId(): string | null {\n if (!this.enabled) return null;\n if (this.clientUserId) return this.clientUserId;\n\n const persisted = this.storage.getItem(STORAGE_KEYS.userId);\n if (persisted) return persisted;\n\n const id = this.uuid();\n this.storage.setItem(STORAGE_KEYS.userId, id);\n this.storage.setItem(STORAGE_KEYS.firstSeen, String(this.now()));\n this.userWasJustCreated = true;\n return id;\n }\n\n /** `true` solo en la primera sesión tras crear el user_id propio del SDK. */\n isNewUser(): boolean {\n return this.userWasJustCreated;\n }\n\n /** session_id actual provisto por el backend, o null si aún no llegó. */\n getSessionId(): string | null {\n if (!this.enabled) return null;\n return this.sessionId;\n }\n\n /** Cachea el session_id devuelto por el backend (respuesta de POST /sdk/popups). */\n setSessionId(sessionId: string | null): void {\n if (!this.enabled) return;\n this.sessionId = sessionId;\n }\n}\n","/**\n * Helpers puros (sin DOM) para la fuente personalizable del popup/survey.\n * Espejo exacto en KMP: `ui/Font.kt`. Cualquier cambio aquí se replica allí.\n */\n\nconst FORMAT_BY_EXT: Record<string, string> = {\n woff2: 'woff2',\n woff: 'woff',\n ttf: 'truetype',\n otf: 'opentype',\n};\n\n/** Deriva el `format()` del `@font-face` a partir de la extensión de la URL. */\nexport function fontFormatFromUrl(url: string): string | undefined {\n const clean = url.split('?')[0].split('#')[0];\n const ext = clean.slice(clean.lastIndexOf('.') + 1).toLowerCase();\n return FORMAT_BY_EXT[ext];\n}\n\n// family/url provienen de la API y se interpolan en CSS y en el <style> del HTML del WebView.\n// Saneamos para evitar romper el string/rule (CSS injection) o cerrar </style> (script injection).\nfunction sanitizeFamily(family: string): string {\n return family.replace(/[^A-Za-z0-9 ._-]/g, '').trim();\n}\n\nfunction isSafeFontUrl(url: string): boolean {\n // Solo http(s)/data y sin caracteres que rompan url(\"...\") o el <style>:\n // comillas, <>, backslash, y cualquier whitespace o carácter de control.\n if (!/^(https?:|data:)/i.test(url)) return false;\n // eslint-disable-next-line no-control-regex\n return !/[\\x00-\\x20\"<>\\\\]/.test(url);\n}\n\n/** Valor a aplicar en `font-family`: nombre custom + fallback de sistema. */\nexport function buildFontFamilyValue(family: string): string {\n const safe = sanitizeFamily(family);\n return `\"${safe}\", -apple-system, system-ui, sans-serif`;\n}\n\n/** CSS del `@font-face`. Devuelve \"\" si no hay url (o no es segura). */\nexport function buildFontFaceCss(family: string, url?: string): string {\n if (!url || !isSafeFontUrl(url)) return '';\n const safe = sanitizeFamily(family);\n const fmt = fontFormatFromUrl(url);\n const src = fmt ? `url(\"${url}\") format(\"${fmt}\")` : `url(\"${url}\")`;\n return `@font-face{font-family:\"${safe}\";src:${src};font-display:swap;}`;\n}\n",".magicfeedback-container {\n width: 100%;\n}\n\n\n.magicfeedback-success-message,\n.magicfeedback-info-message,\n.magicfeedback-start-message,\n.magicfeedback-container{\n animation-duration: 0.5s;\n animation-name: animate-fade;\n animation-delay: 0.25s;\n animation-fill-mode: backwards;\n\n font-style: normal;\n text-align: justify;\n font-weight: 400;\n font-size: 1.2rem;\n color: #475569;\n line-height: 1.5;\n}\n\n@keyframes animate-fade {\n 0% { opacity: 0; }\n 100% { opacity: 1; }\n}\n\n.magicfeedback-form {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n}\n.magicfeedback-questions {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: flex-start;\n width: 100%;\n}\n\n.magicfeedback-div {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: flex-start;\n margin-bottom: 10px;\n width: 100%;\n}\n\n.magicfeedback-label {\n margin-bottom: 25px;\n margin-top: 20px;\n font-weight: 400;\n font-family: inherit;\n font-size: 1.2rem;\n color: #212529;\n text-align: start;\n word-wrap: break-word;\n white-space: normal;\n\n}\n\n.magicfeedback-checkbox {\n display: flex;\n flex-direction: column;\n flex-wrap: wrap;\n width: 100%;\n}\n\n.magicfeedback-checkbox-container {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n margin-bottom: 5px;\n margin-top: 5px;\n}\n\n/* Cualificado con `div`: @magicfeedback/native reutiliza la clase \"magicfeedback-radio\"\n también en el <input> de cada opción (rating), y sin el tag este selector lo capturaba\n también, rompiendo su tamaño (el input pasaba a display:flex; width:100%). */\ndiv.magicfeedback-radio {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n flex-wrap: wrap;\n width: 100%;\n margin-right: 5px;\n padding: 5px;\n box-sizing: border-box;\n}\n\n.magicfeedback-checkbox-container,\n.magicfeedback-radio-container {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n margin: 5px;\n padding: 5px 0;\n background-color: #fff;\n border-radius: 5px;\n box-shadow: 0 0 3px #ccc;\n}\n\n.deepdots-popup input::-webkit-outer-spin-button,\n.deepdots-popup input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n -moz-appearance: textfield;\n}\n\n.magicfeedback-checkbox input[type=\"checkbox\"] {\n margin-right: 5px;\n margin-left: 5px;\n width: 20px;\n height: 20px;\n cursor: pointer;\n}\n\n\n.magicfeedback-checkbox label,\n.magicfeedback-radio-container label {\n margin-left: 5px;\n font-weight: 400;\n font-family: inherit;\n font-size: 16px;\n color: #212529;\n text-align: start;\n width: 100%;\n}\n\n.deepdots-popup p {\n margin-bottom: 0px;\n font-size: 12px;\n}\n\n.deepdots-popup h2 {\n line-height: 1.66;\n margin: 0;\n padding: 0;\n font-weight: 900;\n color: #222;\n font-family: var(--deepdots-font, 'Montserrat');\n font-size: 18px;\n text-transform: uppercase;\n text-align: center;\n margin-bottom: 40px;\n}\n\n/* Los controles de formulario no heredan font-family por defecto: cuando hay\n fuente custom (clase deepdots-has-font), los forzamos a usar la del contenedor. */\n.deepdots-popup.deepdots-has-font button,\n.deepdots-popup.deepdots-has-font input,\n.deepdots-popup.deepdots-has-font textarea,\n.deepdots-popup.deepdots-has-font select {\n font-family: var(--deepdots-font, inherit);\n}\n\n.deepdots-popup h6 {\n margin-bottom: 1rem;\n}\n\n.deepdots-popup input[type=\"date\"],\n.deepdots-popup input[type=\"text\"],\n.deepdots-popup input[type=\"number\"],\n.deepdots-popup input[type=\"email\"],\n.deepdots-popup input[type=\"password\"],\n.deepdots-popup textarea,\n.deepdots-popup select {\n box-sizing: border-box;\n padding: 10px;\n margin: 10px 0;\n width: 100%;\n border: 2px solid #ccc; /* Default border color - you can customize this */\n border-radius: 5px;\n font-size: 18px;\n outline: none;\n transition: border-color 0.3s ease;\n min-height: 50px;\n font-family: inherit;\n -webkit-appearance: none;\n}\n\n.deepdots-popup select {\n background-image: linear-gradient(45deg, transparent 50%, gray 50%),\n linear-gradient(135deg, gray 50%, transparent 50%),\n linear-gradient(to right, #ccc, #ccc);\n background-position: calc(100% - 20px) calc(1em + 2px),\n calc(100% - 15px) calc(1em + 2px),\n calc(100% - 2.5em) 0.5em;\n background-size: 5px 5px,\n 5px 5px,\n 1px 1.5em;\n background-repeat: no-repeat;\n}\n\n.magicfeedback-boolean-container {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n margin: 10px 0;\n width: 100%;\n}\n\n.magicfeedback-boolean-container label {\n margin-left: 10px;\n font-weight: 400;\n font-size: 16px;\n width: 100%;\n padding-top: 5px;\n}\n\n.magicfeedback-boolean-option {\n margin-right: 10px;\n margin-left: 10px;\n width: 100%;\n height: 38px;\n border-radius: 8px;\n font-size: 16px;\n cursor: pointer;\n border: 1px solid #ced4da !important;\n}\n\n.magicfeedback-boolean-option label {\n font-weight: 400;\n font-size: 16px;\n color: #475569;\n cursor: pointer;\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n border-radius: inherit;\n}\n\n.magicfeedback-boolean-option input[type=radio] + label:hover {\n color: #007bff;\n}\n\n.magicfeedback-boolean-option input[type=radio]:checked + label {\n color: #007bff;\n background-color: #cadff8;\n}\n\n.deepdots-popup input[type=\"radio\"] {\n padding: 20px;\n margin: 10px 0 10px 15px;\n width: 40px;\n background-color: #fff;\n box-shadow: none;\n}\n\n/* Style for focused input/select */\n.deepdots-popup input:focus, .deepdots-popup select:focus {\n border-color: #4CAF50; /* Highlight color when focused - you can customize this */\n}\n\n/* Style for invalid input (example: validation error) */\n.deepdots-popup input.error, .deepdots-popup select.error {\n border-color: #ff3333; /* Red color for error state - you can customize this */\n}\n\n.magicfeedback-action-container {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n margin-top: 10px;\n width: 100%;\n}\n\n.magicfeedback-action-container .magicfeedback-submit {\n cursor: pointer;\n padding: 10px;\n margin: 10px 0;\n width: 100px;\n border-radius: 8px;\n font-size: 16px;\n outline: none;\n transition: border-color 0.3s ease;\n background-color: #1E293B;\n color: white;\n}\n\n.magicfeedback-action-container .magicfeedback-back {\n cursor: pointer;\n padding: 10px;\n margin: 10px 0;\n width: 100px;\n border: 0;\n border-radius: 8px;\n font-size: 16px;\n outline: none;\n transition: border-color 0.3s ease;\n background-color: transparent;\n color: #212529;\n}\n\n\n\n/* Rating styles */\n.magicfeedback-rating {\n width: 100%;\n}\n\n.magicfeedback-rating-container {\n display: flex;\n flex-direction: row;\n justify-content: space-around;\n flex-wrap: wrap;\n align-items: center;\n\n margin: 15px 5px;\n padding: 0;\n\n list-style: none;\n font-size: 1.5rem;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n}\n\n.magicfeedback-rating-placeholder-value,\n.magicfeedback-rating-placeholder{\n text-align: center;\n align-content: center;\n justify-content: center;\n text-wrap: wrap;\n}\n\n.magicfeedback-rating-option {\n margin: 0 0.5rem;\n padding: 0;\n}\n\n@media (max-width: 1024px) {\n .magicfeedback-rating-option {\n margin: 10px 0;\n padding: 0;\n width: 20%;\n display: flex;\n justify-content: center;\n }\n\n .magicfeedback-rating-placeholder-value {\n font-size: 0.75rem;\n margin-bottom: 1px;\n }\n .magicfeedback-rating-placeholder{\n justify-content: center;\n }\n\n .magicfeedback-rating-container {\n flex-wrap: wrap;\n justify-content: flex-start;\n }\n}\n\n\n.magicfeedback-rating-option-label-container {\n display: flex;\n flex-direction: column;\n}\n\n\n.magicfeedback-rating-option-label-container label {\n display: inline-block;\n text-align: center;\n margin-bottom: 0;\n font-weight: 600;\n font-size: 18px;\n color: #475569;\n}\n\n.magicfeedback-rating-option-label-container input[type=radio] {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n/* IMAGE STYLES */\n.magicfeedback-rating-option-label-container input[type=radio] + img {\n cursor: pointer;\n height: 48px;\n width: 48px;\n}\n\n/* LABEL STYLES */\n.magicfeedback-rating-option-label-container input[type=radio] + label {\n cursor: pointer;\n}\n\n/* CHECKED STYLES */\n.magicfeedback-rating-option-label-container input[type=radio]:checked + img {\n box-shadow: #007bff 0px 1px 4px, #007BFFA0 0px 0px 0px 2px;\n border-radius: 50%;\n}\n\n.magicfeedback-rating-number {\n width: 100%;\n}\n.magicfeedback-rating-container,\n.magicfeedback-rating-number-container {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n\n margin: 15px 0;\n padding: 0;\n\n list-style: none;\n font-size: 1.5rem;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n}\n\n/* Cuando hay placeholders (Very bad / Very good), el placeholder ocupa\n el 100% de la primera línea y los boxes bajan a la siguiente */\n.magicfeedback-rating-number-container--with-placeholder {\n flex-wrap: wrap;\n align-items: flex-start;\n}\n\n.magicfeedback-rating-number-container--with-placeholder .magicfeedback-rating-placeholder {\n width: 100%;\n margin-bottom: 8px;\n}\n\n.magicfeedback-rating-number-container--with-placeholder .magicfeedback-rating-number-option {\n width: auto;\n flex: 1;\n}\n\n.magicfeedback-rating-number-option {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n margin: 5px;\n padding: 0;\n list-style: none;\n font-size: 1.5rem;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n width: 100%;\n}\n\n.magicfeedback-rating-number-option-row{\n max-width: 100% !important;\n}\n\n.magicfeedback-rating-number-top-placeholder,\n.magicfeedback-rating-number-bottom-placeholder{\n text-align: center !important;\n align-content: center;\n justify-content: center;\n text-wrap: wrap;\n\n font-size: 15px;\n color: #475569;\n\n width: 100%;\n margin: auto;\n}\n\n@media (min-width: 650px) {\n .magicfeedback-rating-number-option-row +.magicfeedback-rating-number-container-ltr:first-child,\n .magicfeedback-rating-number-option-row + +.magicfeedback-rating-number-container-rtl:last-child {\n margin-left: 0;\n width: calc(100% - 10px);\n }\n\n .magicfeedback-rating-number-option-row +.magicfeedback-rating-number-container-rtl:first-child,\n .magicfeedback-rating-number-option-row + +.magicfeedback-rating-number-container-ltr:last-child {\n margin-right: 0;\n width: calc(100% - 10px);\n }\n}\n\n\n.magicfeedback-rating-number-option-label-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n}\n\n.magicfeedback-rating-number-option-label-container label {\n display: flex;\n align-items: center;\n justify-content: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin: 0 10px;\n font-weight: 600;\n font-size: 14px;\n color: #475569;\n cursor: pointer;\n width: 100%;\n height: 100%;\n min-height: 50px;\n text-align: center;\n padding: 0;\n border-radius: 4px;\n border: 1px solid #ced4da;\n background-color: rgba(246, 246, 246, 0.3);\n transition: background-color 0.15s ease-in-out;\n}\n\n.magicfeedback-rating-number-option-label-container input[type=radio] {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.magicfeedback-rating-number-option-label-container input[type=radio] + label {\n cursor: pointer;\n}\n\n.magicfeedback-rating-number-option-label-container input[type=radio] + label:hover,\n.magicfeedback-rating-number-option-label-container input[type=radio]:checked + label {\n color: #007bff;\n border: 1px solid #007bff;\n background-color: rgba(86, 168, 253, 0.29);\n}\n\n.magicfeedback-rating-number-container-column{\n align-items: flex-start !important;\n}\n\n@media (max-width: 650px) {\n .magicfeedback-rating-number-container-ltr {\n justify-content: center;\n flex-wrap: wrap;\n }\n\n .magicfeedback-rating-number-container-rtl {\n justify-content: center;\n flex-wrap: wrap-reverse;\n }\n\n .magicfeedback-rating-number-option {\n max-width: 100%;\n height: 55px;\n margin: 5px 0;\n }\n}\n\n.magicfeedback-rating-star {\n width: 100%;\n}\n\n.magicfeedback-rating-star-container {\n display: flex !important;\n flex-direction: row !important;\n justify-content: center !important;\n align-items: center !important;\n flex-wrap: wrap;\n margin: 15px 5px;\n padding: 0;\n list-style: none;\n font-size: 1.5rem;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n}\n\n.magicfeedback-rating-star-option:not(:checked) > .rating__star {\n float: right;\n width: 1em;\n overflow: hidden;\n white-space: nowrap;\n cursor: pointer;\n font-size: 30px;\n color: #ccc;\n margin: 0 10px;\n}\n\n.magicfeedback-rating-star-option:not(:checked) > .rating__star:hover,\n.magicfeedback-rating-star-option:not(:checked) > .rating__star:hover ~ span {\n color: #deb217 !important;\n}\n\n.magicfeedback-rating-star-selected {\n color: #ffc700 !important;\n}\n\n\n/* Style for multiple choice image */\n.magicfeedback-image-option-label-container:hover {\n cursor: pointer;\n border: 0 !important;\n}\n\n.magicfeedback-image-option-label-container input[type=radio]:checked + img,\n.magicfeedback-image-option-label-container input[type=checkbox]:checked + img {\n border: 1px solid #007bff !important;\n box-shadow: 0 0 3px #007bff !important;\n}\n\n.magicfeedback-image-option-label-container input[type=radio] + img,\n.magicfeedback-image-option-label-container input[type=checkbox] + img {\n border: 1px solid transparent !important;\n}\n\n.magicfeedback-image-option-label-container{\n margin: 10px !important;\n height: calc(100% - 20px) !important;\n width: calc(100% - 20px) !important;\n}\n\n.magicfeedback-multiple-choice-image-option {\n display: flex;\n height: auto !important;\n box-shadow: 0 0 3px #ccc;\n}\n\n.magicfeedback-multiple-choice-image-label {\n font-size: 1rem;\n}\n\n.magicfeedback-consent-container {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n margin: 10px 0;\n width: 100%;\n}\n\n.magicfeedback-consent-container label {\n margin-left: 10px;\n}\n\n.magicfeedback-start-message-container {\n /* ... add your start message container styles here ... */\n}\n\n.magicfeedback-success-message,\n.magicfeedback-info-message,\n.magicfeedback-start-message {\n margin: 35px 5px;\n}\n\n.magicfeedback-success-message p,\n.magicfeedback-info-message p,\n.magicfeedback-start-message p {\n font-style: normal;\n text-align: start;\n font-weight: 400;\n font-size: 16px;\n color: #475569;\n line-height: 1.5;\n}\n\n.magicfeedback-success-message img,\n.magicfeedback-info-message img,\n.magicfeedback-start-message img {\n width: 400px;\n max-width: 80%;\n height: auto;\n margin: 10px auto;\n}\n\n.magicfeedback-counter,\n.magicfeedback-point-system-total {\n margin: 10px 0 0 0 !important;\n display: flex !important;\n justify-content: right !important;\n align-items: center !important;\n width: 100%;\n}\n\n.magicfeedback-skip-container {\n background-color: transparent !important;\n border: 0 !important;\n color: #999999FF !important;\n padding: 0 !important;\n margin: 0 !important;\n cursor: pointer !important;\n font-size: 0.5em !important;\n font-weight: 400 !important;\n box-shadow: 0 0 0 !important;\n\n &:hover {\n color: #0066ff;\n }\n}\n\n.magicfeedback-skip-container input[type=checkbox] {\n border-color: #999999FF !important;\n border: 0 !important;\n cursor: pointer !important;\n\n}\n\n.magicfeedback-multi-question-matrix,\n.magicfeedback-point-system,\n.magicfeedback-priority-list {\n width: 100% !important;\n}\n\n.magicfeedback-multi-question-matrix-container,\n.magicfeedback-point-system-container,\n.magicfeedback-priority-list-container {\n width: 100% !important;\n overflow-x: auto;\n}\n\n.magicfeedback-point-system-list,\n.magicfeedback-priority-list-list,\n.magicfeedback-multi-question-matrix-table {\n width: 100% !important;\n padding: 0 !important;\n overflow-x: auto;\n}\n\n.magicfeedback-multi-question-matrix {\n position: relative !important;\n}\n\n.magicfeedback-multi-question-matrix-container {\n overflow: auto !important;\n padding-bottom: 10px;\n}\n\n.magicfeedback-multi-question-matrix-label{\n text-align: left !important;\n}\n.magicfeedback-priority-list-item {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n padding: 10px 0 10px 10px;\n\n border: 1px solid #ced4da;\n border-radius: 5px;\n margin: 10px 0 !important;\n}\n\n.magicfeedback-point-system-item {\n height: 45px;\n margin: 5px 0 !important;\n}\n\n.magicfeedback-point-system-item label,\n.magicfeedback-priority-list-item-label {\n font-family: inherit;\n font-size: 18px !important;\n color: #212529;\n text-align: start;\n word-wrap: break-word;\n white-space: normal;\n padding: 0 10px 0 0;\n}\n\n.magicfeedback-point-system-input-container {\n padding: 0 10px 0 0;\n border-radius: 5px;\n font-size: 14px;\n border: 1px solid #ced4da;\n height: 42px;\n margin: auto 0;\n}\n\n.magicfeedback-multi-question-matrix-table thead {\n margin-bottom: 20px;\n}\n\n.magicfeedback-multi-question-matrix-table th{\n text-align: center;\n font-weight: 400 !important;\n font-size: 1rem !important;\n padding: 0 10px !important;\n}\n\n.magicfeedback-multi-question-matrix-table th,\n.magicfeedback-multi-question-matrix-table td label,\n.magicfeedback-priority-list-arrow-up,\n.magicfeedback-priority-list-arrow-down {\n font-style: normal !important;\n font-weight: 400 !important;\n font-size: 1rem !important;\n color: #475569 !important;\n cursor: pointer !important;\n margin: 0 !important;\n line-height: inherit;\n}\n\n.magicfeedback-multi-question-matrix-table tr {\n align-items: center;\n margin: 5px 0;\n}\n\n.magicfeedback-multi-question-matrix-row-tr{\n border-radius: 5px;\n margin: 10px 0 !important;\n}\n\n.magicfeedback-multi-question-matrix-row-tr:nth-child(odd) {\n background-color: #f1f6f6; /* Light gray background for odd rows */\n}\n\n.magicfeedback-multi-question-matrix-row-tr:nth-child(even) {\n background-color: #ffffff; /* White background for even rows */\n}\n\n.magicfeedback-multi-question-matrix-row-label {\n text-align: left !important;\n}\n\n.magicfeedback-point-system-item span {\n color: #475569 !important;\n}\n\n.magicfeedback-point-system-item input {\n padding: 0;\n margin: 0 !important;\n width: auto !important;\n min-width: 100px !important;\n height: auto !important;\n min-height: 42px;\n cursor: pointer;\n font-size: 14px;\n text-align: center !important;\n}\n\n.magicfeedback-multi-question-matrix-table input {\n margin: auto !important;\n padding: 20px !important;\n width: 100% !important;\n height: 15px;\n border: 1px solid #ced4da;\n cursor: pointer;\n}\n\n.magicfeedback-image{\n max-height: 400px !important;\n height: auto !important;\n max-width: 100% !important;\n margin: 10px auto !important;\n}\n\n.magicfeedback-error{\n margin-top: 5px;\n}","import type { DeepdotsLogger } from '../types';\n\n/**\n * Logger de módulo compartido: el core lo fija en init() con el logger inyectado\n * por el host (o `console` por defecto), y los módulos que no reciben `this`\n * (renderPopup, renderers) enrutan por aquí. `warn`/`error` caen a `log` si el\n * logger del host no los provee (solo `log` es obligatorio en DeepdotsLogger).\n */\nlet active: DeepdotsLogger = console;\n\nexport function setLogger(logger: DeepdotsLogger | undefined): void {\n active = logger ?? console;\n}\n\nexport function sdkLog(...args: unknown[]): void {\n active.log(...args);\n}\n\nexport function sdkWarn(...args: unknown[]): void {\n (active.warn ?? active.log)(...args);\n}\n\nexport function sdkError(...args: unknown[]): void {\n (active.error ?? active.log)(...args);\n}\n"],"mappings":"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","names":[]}
|