@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.
@@ -178,6 +178,14 @@ var init_font = __esm({
178
178
  }
179
179
  });
180
180
 
181
+ // src/assets/style.css
182
+ var style_default;
183
+ var init_style = __esm({
184
+ "src/assets/style.css"() {
185
+ 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}';
186
+ }
187
+ });
188
+
181
189
  // src/util/logger.ts
182
190
  function setLogger(logger) {
183
191
  active = logger ?? console;
@@ -199,876 +207,6 @@ var init_logger = __esm({
199
207
  }
200
208
  });
201
209
 
202
- // src/assets/style.css
203
- var style_default;
204
- var init_style = __esm({
205
- "src/assets/style.css"() {
206
- style_default = `.magicfeedback-container {
207
- width: 100%;
208
- }
209
-
210
-
211
- .magicfeedback-success-message,
212
- .magicfeedback-info-message,
213
- .magicfeedback-start-message,
214
- .magicfeedback-container{
215
- animation-duration: 0.5s;
216
- animation-name: animate-fade;
217
- animation-delay: 0.25s;
218
- animation-fill-mode: backwards;
219
-
220
- font-style: normal;
221
- text-align: justify;
222
- font-weight: 400;
223
- font-size: 1.2rem;
224
- color: #475569;
225
- line-height: 1.5;
226
- }
227
-
228
- @keyframes animate-fade {
229
- 0% { opacity: 0; }
230
- 100% { opacity: 1; }
231
- }
232
-
233
- .magicfeedback-form {
234
- display: flex;
235
- flex-direction: column;
236
- justify-content: flex-start;
237
- align-items: center;
238
- width: 100%;
239
- }
240
- .magicfeedback-questions {
241
- display: flex;
242
- flex-direction: column;
243
- justify-content: flex-start;
244
- align-items: flex-start;
245
- width: 100%;
246
- }
247
-
248
- .magicfeedback-div {
249
- display: flex;
250
- flex-direction: column;
251
- justify-content: flex-start;
252
- align-items: flex-start;
253
- margin-bottom: 10px;
254
- width: 100%;
255
- }
256
-
257
- .magicfeedback-label {
258
- margin-bottom: 25px;
259
- margin-top: 20px;
260
- font-weight: 400;
261
- font-family: inherit;
262
- font-size: 1.2rem;
263
- color: #212529;
264
- text-align: start;
265
- word-wrap: break-word;
266
- white-space: normal;
267
-
268
- }
269
-
270
- .magicfeedback-checkbox {
271
- display: flex;
272
- flex-direction: column;
273
- flex-wrap: wrap;
274
- width: 100%;
275
- }
276
-
277
- .magicfeedback-checkbox-container {
278
- display: flex;
279
- flex-direction: row;
280
- justify-content: flex-start;
281
- align-items: center;
282
- margin-bottom: 5px;
283
- margin-top: 5px;
284
- }
285
-
286
- .magicfeedback-radio {
287
- display: flex;
288
- flex-direction: column;
289
- justify-content: flex-start;
290
- flex-wrap: wrap;
291
- width: 100%;
292
- margin-right: 5px;
293
- padding: 5px;
294
- }
295
-
296
- .magicfeedback-checkbox-container,
297
- .magicfeedback-radio-container {
298
- display: flex;
299
- flex-direction: row;
300
- justify-content: flex-start;
301
- align-items: center;
302
- margin: 5px;
303
- padding: 5px 0;
304
- background-color: #fff;
305
- border-radius: 5px;
306
- box-shadow: 0 0 3px #ccc;
307
- }
308
-
309
- .deepdots-popup input::-webkit-outer-spin-button,
310
- .deepdots-popup input::-webkit-inner-spin-button {
311
- -webkit-appearance: none;
312
- -moz-appearance: textfield;
313
- }
314
-
315
- .magicfeedback-checkbox input[type="checkbox"] {
316
- margin-right: 5px;
317
- margin-left: 5px;
318
- width: 20px;
319
- height: 20px;
320
- cursor: pointer;
321
- }
322
-
323
-
324
- .magicfeedback-checkbox label,
325
- .magicfeedback-radio-container label {
326
- margin-left: 5px;
327
- font-weight: 400;
328
- font-family: inherit;
329
- font-size: 16px;
330
- color: #212529;
331
- text-align: start;
332
- width: 100%;
333
- }
334
-
335
- .deepdots-popup p {
336
- margin-bottom: 0px;
337
- font-size: 12px;
338
- }
339
-
340
- .deepdots-popup h2 {
341
- line-height: 1.66;
342
- margin: 0;
343
- padding: 0;
344
- font-weight: 900;
345
- color: #222;
346
- font-family: var(--deepdots-font, 'Montserrat');
347
- font-size: 18px;
348
- text-transform: uppercase;
349
- text-align: center;
350
- margin-bottom: 40px;
351
- }
352
-
353
- /* Los controles de formulario no heredan font-family por defecto: cuando hay
354
- fuente custom (clase deepdots-has-font), los forzamos a usar la del contenedor. */
355
- .deepdots-popup.deepdots-has-font button,
356
- .deepdots-popup.deepdots-has-font input,
357
- .deepdots-popup.deepdots-has-font textarea,
358
- .deepdots-popup.deepdots-has-font select {
359
- font-family: var(--deepdots-font, inherit);
360
- }
361
-
362
- .deepdots-popup h6 {
363
- margin-bottom: 1rem;
364
- }
365
-
366
- .deepdots-popup input[type="date"],
367
- .deepdots-popup input[type="text"],
368
- .deepdots-popup input[type="number"],
369
- .deepdots-popup input[type="email"],
370
- .deepdots-popup input[type="password"],
371
- .deepdots-popup textarea,
372
- .deepdots-popup select {
373
- box-sizing: border-box;
374
- padding: 10px;
375
- margin: 10px 0;
376
- width: 100%;
377
- border: 2px solid #ccc; /* Default border color - you can customize this */
378
- border-radius: 5px;
379
- font-size: 18px;
380
- outline: none;
381
- transition: border-color 0.3s ease;
382
- min-height: 50px;
383
- font-family: inherit;
384
- -webkit-appearance: none;
385
- }
386
-
387
- .deepdots-popup select {
388
- background-image: linear-gradient(45deg, transparent 50%, gray 50%),
389
- linear-gradient(135deg, gray 50%, transparent 50%),
390
- linear-gradient(to right, #ccc, #ccc);
391
- background-position: calc(100% - 20px) calc(1em + 2px),
392
- calc(100% - 15px) calc(1em + 2px),
393
- calc(100% - 2.5em) 0.5em;
394
- background-size: 5px 5px,
395
- 5px 5px,
396
- 1px 1.5em;
397
- background-repeat: no-repeat;
398
- }
399
-
400
- .magicfeedback-boolean-container {
401
- display: flex;
402
- flex-direction: row;
403
- justify-content: flex-start;
404
- align-items: center;
405
- margin: 10px 0;
406
- width: 100%;
407
- }
408
-
409
- .magicfeedback-boolean-container label {
410
- margin-left: 10px;
411
- font-weight: 400;
412
- font-size: 16px;
413
- width: 100%;
414
- padding-top: 5px;
415
- }
416
-
417
- .magicfeedback-boolean-option {
418
- margin-right: 10px;
419
- margin-left: 10px;
420
- width: 100%;
421
- height: 38px;
422
- border-radius: 8px;
423
- font-size: 16px;
424
- cursor: pointer;
425
- border: 1px solid #ced4da !important;
426
- }
427
-
428
- .magicfeedback-boolean-option label {
429
- font-weight: 400;
430
- font-size: 16px;
431
- color: #475569;
432
- cursor: pointer;
433
- width: 100%;
434
- height: 100%;
435
- display: flex;
436
- justify-content: center;
437
- align-items: center;
438
- border-radius: inherit;
439
- }
440
-
441
- .magicfeedback-boolean-option input[type=radio] + label:hover {
442
- color: #007bff;
443
- }
444
-
445
- .magicfeedback-boolean-option input[type=radio]:checked + label {
446
- color: #007bff;
447
- background-color: #cadff8;
448
- }
449
-
450
- .deepdots-popup input[type="radio"] {
451
- padding: 20px;
452
- margin: 10px 0 10px 15px;
453
- width: 40px;
454
- background-color: #fff;
455
- box-shadow: none;
456
- }
457
-
458
- /* Style for focused input/select */
459
- .deepdots-popup input:focus, .deepdots-popup select:focus {
460
- border-color: #4CAF50; /* Highlight color when focused - you can customize this */
461
- }
462
-
463
- /* Style for invalid input (example: validation error) */
464
- .deepdots-popup input.error, .deepdots-popup select.error {
465
- border-color: #ff3333; /* Red color for error state - you can customize this */
466
- }
467
-
468
- .magicfeedback-action-container {
469
- display: flex;
470
- flex-direction: row;
471
- justify-content: space-between;
472
- align-items: center;
473
- margin-top: 10px;
474
- width: 100%;
475
- }
476
-
477
- .magicfeedback-action-container .magicfeedback-submit {
478
- cursor: pointer;
479
- padding: 10px;
480
- margin: 10px 0;
481
- width: 100px;
482
- border-radius: 8px;
483
- font-size: 16px;
484
- outline: none;
485
- transition: border-color 0.3s ease;
486
- background-color: #1E293B;
487
- color: white;
488
- }
489
-
490
- .magicfeedback-action-container .magicfeedback-back {
491
- cursor: pointer;
492
- padding: 10px;
493
- margin: 10px 0;
494
- width: 100px;
495
- border: 0;
496
- border-radius: 8px;
497
- font-size: 16px;
498
- outline: none;
499
- transition: border-color 0.3s ease;
500
- background-color: transparent;
501
- color: #212529;
502
- }
503
-
504
-
505
-
506
- /* Rating styles */
507
- .magicfeedback-rating {
508
- width: 100%;
509
- }
510
-
511
- .magicfeedback-rating-container {
512
- display: flex;
513
- flex-direction: row;
514
- justify-content: space-around;
515
- flex-wrap: wrap;
516
- align-items: center;
517
-
518
- margin: 15px 5px;
519
- padding: 0;
520
-
521
- list-style: none;
522
- font-size: 1.5rem;
523
- cursor: pointer;
524
- user-select: none;
525
- -webkit-user-select: none;
526
- -moz-user-select: none;
527
- -ms-user-select: none;
528
- }
529
-
530
- .magicfeedback-rating-placeholder-value,
531
- .magicfeedback-rating-placeholder{
532
- text-align: center;
533
- align-content: center;
534
- justify-content: center;
535
- text-wrap: wrap;
536
- }
537
-
538
- .magicfeedback-rating-option {
539
- margin: 0 0.5rem;
540
- padding: 0;
541
- }
542
-
543
- @media (max-width: 1024px) {
544
- .magicfeedback-rating-option {
545
- margin: 10px 0;
546
- padding: 0;
547
- width: 20%;
548
- display: flex;
549
- justify-content: center;
550
- }
551
-
552
- .magicfeedback-rating-placeholder-value {
553
- font-size: 0.75rem;
554
- margin-bottom: 1px;
555
- }
556
- .magicfeedback-rating-placeholder{
557
- justify-content: center;
558
- }
559
-
560
- .magicfeedback-rating-container {
561
- flex-wrap: wrap;
562
- justify-content: flex-start;
563
- }
564
- }
565
-
566
-
567
- .magicfeedback-rating-option-label-container {
568
- display: flex;
569
- flex-direction: column;
570
- }
571
-
572
-
573
- .magicfeedback-rating-option-label-container label {
574
- display: inline-block;
575
- text-align: center;
576
- margin-bottom: 0;
577
- font-weight: 600;
578
- font-size: 18px;
579
- color: #475569;
580
- }
581
-
582
- .magicfeedback-rating-option-label-container input[type=radio] {
583
- position: absolute;
584
- opacity: 0;
585
- width: 0;
586
- height: 0;
587
- }
588
-
589
- /* IMAGE STYLES */
590
- .magicfeedback-rating-option-label-container input[type=radio] + img {
591
- cursor: pointer;
592
- height: 48px;
593
- width: 48px;
594
- }
595
-
596
- /* LABEL STYLES */
597
- .magicfeedback-rating-option-label-container input[type=radio] + label {
598
- cursor: pointer;
599
- }
600
-
601
- /* CHECKED STYLES */
602
- .magicfeedback-rating-option-label-container input[type=radio]:checked + img {
603
- box-shadow: #007bff 0px 1px 4px, #007BFFA0 0px 0px 0px 2px;
604
- border-radius: 50%;
605
- }
606
-
607
- .magicfeedback-rating-number {
608
- width: 100%;
609
- }
610
- .magicfeedback-rating-container,
611
- .magicfeedback-rating-number-container {
612
- display: flex;
613
- flex-direction: row;
614
- justify-content: space-between;
615
- align-items: center;
616
-
617
- margin: 15px 0;
618
- padding: 0;
619
-
620
- list-style: none;
621
- font-size: 1.5rem;
622
- cursor: pointer;
623
- user-select: none;
624
- -webkit-user-select: none;
625
- -moz-user-select: none;
626
- -ms-user-select: none;
627
- }
628
-
629
- /* Cuando hay placeholders (Very bad / Very good), el placeholder ocupa
630
- el 100% de la primera l\xEDnea y los boxes bajan a la siguiente */
631
- .magicfeedback-rating-number-container--with-placeholder {
632
- flex-wrap: wrap;
633
- align-items: flex-start;
634
- }
635
-
636
- .magicfeedback-rating-number-container--with-placeholder .magicfeedback-rating-placeholder {
637
- width: 100%;
638
- margin-bottom: 8px;
639
- }
640
-
641
- .magicfeedback-rating-number-container--with-placeholder .magicfeedback-rating-number-option {
642
- width: auto;
643
- flex: 1;
644
- }
645
-
646
- .magicfeedback-rating-number-option {
647
- display: flex;
648
- flex-direction: row;
649
- justify-content: flex-start;
650
- align-items: center;
651
- margin: 5px;
652
- padding: 0;
653
- list-style: none;
654
- font-size: 1.5rem;
655
- cursor: pointer;
656
- user-select: none;
657
- -webkit-user-select: none;
658
- -moz-user-select: none;
659
- -ms-user-select: none;
660
- width: 100%;
661
- }
662
-
663
- .magicfeedback-rating-number-option-row{
664
- max-width: 100% !important;
665
- }
666
-
667
- .magicfeedback-rating-number-top-placeholder,
668
- .magicfeedback-rating-number-bottom-placeholder{
669
- text-align: center !important;
670
- align-content: center;
671
- justify-content: center;
672
- text-wrap: wrap;
673
-
674
- font-size: 15px;
675
- color: #475569;
676
-
677
- width: 100%;
678
- margin: auto;
679
- }
680
-
681
- @media (min-width: 650px) {
682
- .magicfeedback-rating-number-option-row +.magicfeedback-rating-number-container-ltr:first-child,
683
- .magicfeedback-rating-number-option-row + +.magicfeedback-rating-number-container-rtl:last-child {
684
- margin-left: 0;
685
- width: calc(100% - 10px);
686
- }
687
-
688
- .magicfeedback-rating-number-option-row +.magicfeedback-rating-number-container-rtl:first-child,
689
- .magicfeedback-rating-number-option-row + +.magicfeedback-rating-number-container-ltr:last-child {
690
- margin-right: 0;
691
- width: calc(100% - 10px);
692
- }
693
- }
694
-
695
-
696
- .magicfeedback-rating-number-option-label-container {
697
- display: flex;
698
- flex-direction: column;
699
- align-items: center;
700
- justify-content: center;
701
- width: 100%;
702
- height: 100%;
703
- }
704
-
705
- .magicfeedback-rating-number-option-label-container label {
706
- display: flex;
707
- align-items: center;
708
- justify-content: center;
709
- white-space: nowrap;
710
- overflow: hidden;
711
- text-overflow: ellipsis;
712
- margin: 0 10px;
713
- font-weight: 600;
714
- font-size: 14px;
715
- color: #475569;
716
- cursor: pointer;
717
- width: 100%;
718
- height: 100%;
719
- min-height: 50px;
720
- text-align: center;
721
- padding: 0;
722
- border-radius: 4px;
723
- border: 1px solid #ced4da;
724
- background-color: rgba(246, 246, 246, 0.3);
725
- transition: background-color 0.15s ease-in-out;
726
- }
727
-
728
- .magicfeedback-rating-number-option-label-container input[type=radio] {
729
- position: absolute;
730
- opacity: 0;
731
- width: 0;
732
- height: 0;
733
- }
734
-
735
- .magicfeedback-rating-number-option-label-container input[type=radio] + label {
736
- cursor: pointer;
737
- }
738
-
739
- .magicfeedback-rating-number-option-label-container input[type=radio] + label:hover,
740
- .magicfeedback-rating-number-option-label-container input[type=radio]:checked + label {
741
- color: #007bff;
742
- border: 1px solid #007bff;
743
- background-color: rgba(86, 168, 253, 0.29);
744
- }
745
-
746
- .magicfeedback-rating-number-container-column{
747
- align-items: flex-start !important;
748
- }
749
-
750
- @media (max-width: 650px) {
751
- .magicfeedback-rating-number-container-ltr {
752
- justify-content: center;
753
- flex-wrap: wrap;
754
- }
755
-
756
- .magicfeedback-rating-number-container-rtl {
757
- justify-content: center;
758
- flex-wrap: wrap-reverse;
759
- }
760
-
761
- .magicfeedback-rating-number-option {
762
- max-width: 100%;
763
- height: 55px;
764
- margin: 5px 0;
765
- }
766
- }
767
-
768
- .magicfeedback-rating-star {
769
- width: 100%;
770
- }
771
-
772
- .magicfeedback-rating-star-container {
773
- display: flex !important;
774
- flex-direction: row !important;
775
- justify-content: center !important;
776
- align-items: center !important;
777
- flex-wrap: wrap;
778
- margin: 15px 5px;
779
- padding: 0;
780
- list-style: none;
781
- font-size: 1.5rem;
782
- cursor: pointer;
783
- user-select: none;
784
- -webkit-user-select: none;
785
- -moz-user-select: none;
786
- -ms-user-select: none;
787
- }
788
-
789
- .magicfeedback-rating-star-option:not(:checked) > .rating__star {
790
- float: right;
791
- width: 1em;
792
- overflow: hidden;
793
- white-space: nowrap;
794
- cursor: pointer;
795
- font-size: 30px;
796
- color: #ccc;
797
- margin: 0 10px;
798
- }
799
-
800
- .magicfeedback-rating-star-option:not(:checked) > .rating__star:hover,
801
- .magicfeedback-rating-star-option:not(:checked) > .rating__star:hover ~ span {
802
- color: #deb217 !important;
803
- }
804
-
805
- .magicfeedback-rating-star-selected {
806
- color: #ffc700 !important;
807
- }
808
-
809
-
810
- /* Style for multiple choice image */
811
- .magicfeedback-image-option-label-container:hover {
812
- cursor: pointer;
813
- border: 0 !important;
814
- }
815
-
816
- .magicfeedback-image-option-label-container input[type=radio]:checked + img,
817
- .magicfeedback-image-option-label-container input[type=checkbox]:checked + img {
818
- border: 1px solid #007bff !important;
819
- box-shadow: 0 0 3px #007bff !important;
820
- }
821
-
822
- .magicfeedback-image-option-label-container input[type=radio] + img,
823
- .magicfeedback-image-option-label-container input[type=checkbox] + img {
824
- border: 1px solid transparent !important;
825
- }
826
-
827
- .magicfeedback-image-option-label-container{
828
- margin: 10px !important;
829
- height: calc(100% - 20px) !important;
830
- width: calc(100% - 20px) !important;
831
- }
832
-
833
- .magicfeedback-multiple-choice-image-option {
834
- display: flex;
835
- height: auto !important;
836
- box-shadow: 0 0 3px #ccc;
837
- }
838
-
839
- .magicfeedback-multiple-choice-image-label {
840
- font-size: 1rem;
841
- }
842
-
843
- .magicfeedback-consent-container {
844
- display: flex;
845
- flex-direction: row;
846
- justify-content: flex-start;
847
- align-items: center;
848
- margin: 10px 0;
849
- width: 100%;
850
- }
851
-
852
- .magicfeedback-consent-container label {
853
- margin-left: 10px;
854
- }
855
-
856
- .magicfeedback-start-message-container {
857
- /* ... add your start message container styles here ... */
858
- }
859
-
860
- .magicfeedback-success-message,
861
- .magicfeedback-info-message,
862
- .magicfeedback-start-message {
863
- margin: 35px 5px;
864
- }
865
-
866
- .magicfeedback-success-message p,
867
- .magicfeedback-info-message p,
868
- .magicfeedback-start-message p {
869
- font-style: normal;
870
- text-align: start;
871
- font-weight: 400;
872
- font-size: 16px;
873
- color: #475569;
874
- line-height: 1.5;
875
- }
876
-
877
- .magicfeedback-success-message img,
878
- .magicfeedback-info-message img,
879
- .magicfeedback-start-message img {
880
- width: 400px;
881
- max-width: 80%;
882
- height: auto;
883
- margin: 10px auto;
884
- }
885
-
886
- .magicfeedback-counter,
887
- .magicfeedback-point-system-total {
888
- margin: 10px 0 0 0 !important;
889
- display: flex !important;
890
- justify-content: right !important;
891
- align-items: center !important;
892
- width: 100%;
893
- }
894
-
895
- .magicfeedback-skip-container {
896
- background-color: transparent !important;
897
- border: 0 !important;
898
- color: #999999FF !important;
899
- padding: 0 !important;
900
- margin: 0 !important;
901
- cursor: pointer !important;
902
- font-size: 0.5em !important;
903
- font-weight: 400 !important;
904
- box-shadow: 0 0 0 !important;
905
-
906
- &:hover {
907
- color: #0066ff;
908
- }
909
- }
910
-
911
- .magicfeedback-skip-container input[type=checkbox] {
912
- border-color: #999999FF !important;
913
- border: 0 !important;
914
- cursor: pointer !important;
915
-
916
- }
917
-
918
- .magicfeedback-multi-question-matrix,
919
- .magicfeedback-point-system,
920
- .magicfeedback-priority-list {
921
- width: 100% !important;
922
- }
923
-
924
- .magicfeedback-multi-question-matrix-container,
925
- .magicfeedback-point-system-container,
926
- .magicfeedback-priority-list-container {
927
- width: 100% !important;
928
- overflow-x: auto;
929
- }
930
-
931
- .magicfeedback-point-system-list,
932
- .magicfeedback-priority-list-list,
933
- .magicfeedback-multi-question-matrix-table {
934
- width: 100% !important;
935
- padding: 0 !important;
936
- overflow-x: auto;
937
- }
938
-
939
- .magicfeedback-multi-question-matrix {
940
- position: relative !important;
941
- }
942
-
943
- .magicfeedback-multi-question-matrix-container {
944
- overflow: auto !important;
945
- padding-bottom: 10px;
946
- }
947
-
948
- .magicfeedback-multi-question-matrix-label{
949
- text-align: left !important;
950
- }
951
- .magicfeedback-priority-list-item {
952
- display: flex;
953
- flex-direction: row;
954
- justify-content: space-between;
955
- align-items: center;
956
- padding: 10px 0 10px 10px;
957
-
958
- border: 1px solid #ced4da;
959
- border-radius: 5px;
960
- margin: 10px 0 !important;
961
- }
962
-
963
- .magicfeedback-point-system-item {
964
- height: 45px;
965
- margin: 5px 0 !important;
966
- }
967
-
968
- .magicfeedback-point-system-item label,
969
- .magicfeedback-priority-list-item-label {
970
- font-family: inherit;
971
- font-size: 18px !important;
972
- color: #212529;
973
- text-align: start;
974
- word-wrap: break-word;
975
- white-space: normal;
976
- padding: 0 10px 0 0;
977
- }
978
-
979
- .magicfeedback-point-system-input-container {
980
- padding: 0 10px 0 0;
981
- border-radius: 5px;
982
- font-size: 14px;
983
- border: 1px solid #ced4da;
984
- height: 42px;
985
- margin: auto 0;
986
- }
987
-
988
- .magicfeedback-multi-question-matrix-table thead {
989
- margin-bottom: 20px;
990
- }
991
-
992
- .magicfeedback-multi-question-matrix-table th{
993
- text-align: center;
994
- font-weight: 400 !important;
995
- font-size: 1rem !important;
996
- padding: 0 10px !important;
997
- }
998
-
999
- .magicfeedback-multi-question-matrix-table th,
1000
- .magicfeedback-multi-question-matrix-table td label,
1001
- .magicfeedback-priority-list-arrow-up,
1002
- .magicfeedback-priority-list-arrow-down {
1003
- font-style: normal !important;
1004
- font-weight: 400 !important;
1005
- font-size: 1rem !important;
1006
- color: #475569 !important;
1007
- cursor: pointer !important;
1008
- margin: 0 !important;
1009
- line-height: inherit;
1010
- }
1011
-
1012
- .magicfeedback-multi-question-matrix-table tr {
1013
- align-items: center;
1014
- margin: 5px 0;
1015
- }
1016
-
1017
- .magicfeedback-multi-question-matrix-row-tr{
1018
- border-radius: 5px;
1019
- margin: 10px 0 !important;
1020
- }
1021
-
1022
- .magicfeedback-multi-question-matrix-row-tr:nth-child(odd) {
1023
- background-color: #f1f6f6; /* Light gray background for odd rows */
1024
- }
1025
-
1026
- .magicfeedback-multi-question-matrix-row-tr:nth-child(even) {
1027
- background-color: #ffffff; /* White background for even rows */
1028
- }
1029
-
1030
- .magicfeedback-multi-question-matrix-row-label {
1031
- text-align: left !important;
1032
- }
1033
-
1034
- .magicfeedback-point-system-item span {
1035
- color: #475569 !important;
1036
- }
1037
-
1038
- .magicfeedback-point-system-item input {
1039
- padding: 0;
1040
- margin: 0 !important;
1041
- width: auto !important;
1042
- min-width: 100px !important;
1043
- height: auto !important;
1044
- min-height: 42px;
1045
- cursor: pointer;
1046
- font-size: 14px;
1047
- text-align: center !important;
1048
- }
1049
-
1050
- .magicfeedback-multi-question-matrix-table input {
1051
- margin: auto !important;
1052
- padding: 20px !important;
1053
- width: 100% !important;
1054
- height: 15px;
1055
- border: 1px solid #ced4da;
1056
- cursor: pointer;
1057
- }
1058
-
1059
- .magicfeedback-image{
1060
- max-height: 400px !important;
1061
- height: auto !important;
1062
- max-width: 100% !important;
1063
- margin: 10px auto !important;
1064
- }
1065
-
1066
- .magicfeedback-error{
1067
- margin-top: 5px;
1068
- }`;
1069
- }
1070
- });
1071
-
1072
210
  // src/ui/renderPopup.ts
1073
211
  var renderPopup_exports = {};
1074
212
  __export(renderPopup_exports, {
@@ -1149,6 +287,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
1149
287
  const isDark = style?.theme === "dark";
1150
288
  const theme = {
1151
289
  popupBg: isDark ? "#1e1e1e" : "#fff",
290
+ // El popup fija su propio fondo, así que debe fijar también el esquema de color.
291
+ // Sin esto los controles de formulario (input/textarea/select) caen al estilo por
292
+ // defecto del navegador: en un host con `prefers-color-scheme: dark` se pintan
293
+ // oscuros con texto blanco sobre el fondo claro del popup.
294
+ colorScheme: isDark ? "dark" : "light",
1152
295
  textPrimary: isDark ? "#f0f0f0" : "#111",
1153
296
  closeBtnHoverBg: isDark ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.06)",
1154
297
  closeBtnHoverColor: isDark ? "#fff" : "#000000"
@@ -1171,6 +314,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
1171
314
  flex-direction: column;
1172
315
  justify-content: flex-start;
1173
316
  background: ${theme.popupBg};
317
+ color-scheme: ${theme.colorScheme};
1174
318
  border-radius: 8px;
1175
319
  padding: 24px;
1176
320
  box-shadow: 0 4px 6px rgba(0,0,0,0.1);
@@ -1816,6 +960,16 @@ init_tracking_manager();
1816
960
 
1817
961
  // src/ui/surveyHtml.ts
1818
962
  init_font();
963
+ init_style();
964
+ var POSITION_MAP = {
965
+ center: { justifyContent: "center", alignItems: "center", padding: "0", background: "rgba(0,0,0,0.5)" },
966
+ bottom: { justifyContent: "center", alignItems: "flex-end", padding: "16px", background: "transparent" },
967
+ "bottom-right": { justifyContent: "flex-end", alignItems: "flex-end", padding: "16px", background: "transparent" },
968
+ "bottom-left": { justifyContent: "flex-start", alignItems: "flex-end", padding: "16px", background: "transparent" },
969
+ top: { justifyContent: "center", alignItems: "flex-start", padding: "16px", background: "transparent" },
970
+ "top-right": { justifyContent: "flex-end", alignItems: "flex-start", padding: "16px", background: "transparent" },
971
+ "top-left": { justifyContent: "flex-start", alignItems: "flex-start", padding: "16px", background: "transparent" }
972
+ };
1819
973
  function buildSurveyHtml(opts) {
1820
974
  const env = opts.env === "development" ? "dev" : "prod";
1821
975
  const version = opts.version ?? "2.2.4";
@@ -1826,17 +980,141 @@ function buildSurveyHtml(opts) {
1826
980
  const metaJson = JSON.stringify(opts.metadata ?? []);
1827
981
  const fontFaceCss = opts.font ? buildFontFaceCss(opts.font.family, opts.font.url) : "";
1828
982
  const fontFamilyValue = opts.font ? buildFontFamilyValue(opts.font.family) : "-apple-system,system-ui,sans-serif";
983
+ const popupClass = opts.font?.family ? "deepdots-popup deepdots-has-font" : "deepdots-popup";
984
+ const isDark = opts.theme === "dark";
985
+ const popupBg = isDark ? "#1e1e1e" : "#fff";
986
+ const colorScheme = isDark ? "dark" : "light";
987
+ const textPrimary = isDark ? "#f0f0f0" : "#111";
988
+ const pos = POSITION_MAP[opts.position ?? "center"] ?? POSITION_MAP.center;
989
+ const backLabel = JSON.stringify(opts.actions?.back?.label ?? "Back");
990
+ const startLabel = JSON.stringify(opts.actions?.start?.label ?? "Start survey");
991
+ const completeLabel = JSON.stringify(opts.actions?.complete?.label ?? "Complete survey");
992
+ const submitLabel = JSON.stringify(opts.actions?.accept?.label ?? "Send");
1829
993
  return `<!DOCTYPE html><html><head>
1830
994
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
1831
- <meta name="color-scheme" content="light"/>
1832
- <style>${fontFaceCss}html,body{margin:0;padding:0;background:transparent;font-family:${fontFamilyValue}}#mf{width:100%;box-sizing:border-box}#mf *{max-width:100%;box-sizing:border-box}</style>
995
+ <meta name="color-scheme" content="${colorScheme}"/>
996
+ <style>
997
+ ${style_default}
998
+ ${fontFaceCss}
999
+ html,body{margin:0;padding:0;height:100%;font-family:${fontFamilyValue}}
1000
+ body{display:flex;justify-content:${pos.justifyContent};align-items:${pos.alignItems};background:${pos.background};padding:${pos.padding};box-sizing:border-box}
1001
+ .deepdots-popup{position:relative;display:flex;flex-direction:column;justify-content:flex-start;background:${popupBg};color-scheme:${colorScheme};border-radius:8px;padding:24px;box-shadow:0 4px 6px rgba(0,0,0,0.1);max-width:600px;width:90%;min-height:200px;box-sizing:border-box}
1002
+ .deepdots-popup-header{display:flex;justify-content:flex-end;align-items:center;width:100%}
1003
+ #dd-close{background:transparent;border:none;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:8px;cursor:pointer;color:${textPrimary};padding:4px}
1004
+ .deepdots-popup-container-content{display:flex;flex-direction:column;padding:0 20px 12px 20px;max-height:80vh;overflow:hidden;box-sizing:border-box}
1005
+ .deepdots-popup-main{display:flex;flex-direction:column;width:100%;max-height:80vh;overflow-y:auto;overflow-x:hidden;position:relative}
1006
+ #dd-form-wrapper{width:100%;flex:1 1 auto}
1007
+ #mf{width:100%;box-sizing:border-box;visibility:hidden}
1008
+ .deepdots-popup-main *{max-width:100%;box-sizing:border-box}
1009
+ #dd-logo{max-height:40px;max-width:100%;object-fit:contain}
1010
+ .deepdots-error-hint{display:none;margin:12px 0 0 0;padding:10px 12px;border-radius:6px;background:#FEF3C7;color:#92400E;border:1px solid #FCD34D;font-size:13px}
1011
+ .deepdots-popup-footer{display:flex;flex-direction:row-reverse;justify-content:space-between;align-items:center;gap:8px;margin-top:auto;width:100%;padding-top:16px}
1012
+ .dd-nav-btn{display:none;border:none;padding:12px 24px;border-radius:4px;cursor:pointer;font-size:14px;box-shadow:0 2px 4px rgba(0,0,0,0.1);align-items:center;justify-content:center;text-align:center}
1013
+ #dd-back{background:transparent;color:#333;border:1px solid #999}
1014
+ #dd-start,#dd-complete,#dd-submit{background:#1E293B;color:#fff;border:none}
1015
+ #dd-submit:disabled,#dd-start:disabled,#dd-back:disabled,#dd-complete:disabled{opacity:0.6;cursor:not-allowed}
1016
+ .mf-spinner{display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}
1017
+ .mf-spinner-circle{width:28px;height:28px;border:3px solid #e0e6ed;border-top-color:#1E293B;border-radius:50%;animation:ddspin 0.9s linear infinite}
1018
+ @keyframes ddspin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
1019
+ @media (max-width:640px){
1020
+ .deepdots-popup{width:calc(100% - 24px);max-width:calc(100% - 24px);border-radius:12px}
1021
+ .deepdots-popup-footer{flex-direction:column-reverse;gap:12px}
1022
+ .dd-nav-btn{width:100%}
1023
+ .magicfeedback-checkbox-container,.magicfeedback-radio-container{margin:4px 0;padding:4px 8px}
1024
+ .magicfeedback-checkbox label,.magicfeedback-radio-container label{font-size:15px}
1025
+ .deepdots-popup input[type="radio"]{margin:6px 0 6px 8px}
1026
+ .deepdots-popup input[type="checkbox"]{width:16px;height:16px}
1027
+ }
1028
+ </style>
1833
1029
  </head><body>
1834
- <div id="mf"></div>
1030
+ <div class="${popupClass}" id="dd-popup">
1031
+ <div class="deepdots-popup-header">
1032
+ <button type="button" id="dd-close" aria-label="Close popup">
1033
+ <svg width="28" height="28" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1034
+ <path d="M6 6L18 18M6 18L18 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
1035
+ </svg>
1036
+ </button>
1037
+ </div>
1038
+ <div class="deepdots-popup-container-content">
1039
+ <div class="deepdots-popup-main" id="dd-main">
1040
+ <div id="dd-form-wrapper">
1041
+ <div class="mf-spinner" id="dd-spinner" role="status" aria-label="Loading survey"><div class="mf-spinner-circle"></div></div>
1042
+ <div id="mf"></div>
1043
+ </div>
1044
+ <div class="deepdots-error-hint" id="dd-error" role="alert" aria-live="polite"></div>
1045
+ <div class="deepdots-popup-footer" id="dd-footer">
1046
+ <button type="button" class="dd-nav-btn" id="dd-submit">Send</button>
1047
+ <button type="button" class="dd-nav-btn" id="dd-back">Back</button>
1048
+ <button type="button" class="dd-nav-btn" id="dd-complete">Complete survey</button>
1049
+ <button type="button" class="dd-nav-btn" id="dd-start">Start survey</button>
1050
+ </div>
1051
+ </div>
1052
+ </div>
1053
+ </div>
1835
1054
  <script>
1836
1055
  (function(){
1837
1056
  function emit(s){ try{ if(window.ReactNativeWebView){ window.ReactNativeWebView.postMessage(s); } }catch(e){} }
1838
1057
  function emitJSON(n,p){ try{ emit(JSON.stringify({name:n,payload:p||{}})); }catch(e){} }
1839
1058
  window.DeepdotsClose = function(){ emitJSON('popup_close'); };
1059
+
1060
+ var popup=document.getElementById('dd-popup');
1061
+ var main=document.getElementById('dd-main');
1062
+ var footer=document.getElementById('dd-footer');
1063
+ var spinner=document.getElementById('dd-spinner');
1064
+ var formHost=document.getElementById('mf');
1065
+ var errorHint=document.getElementById('dd-error');
1066
+ var backBtn=document.getElementById('dd-back');
1067
+ var startBtn=document.getElementById('dd-start');
1068
+ var completeBtn=document.getElementById('dd-complete');
1069
+ var submitBtn=document.getElementById('dd-submit');
1070
+
1071
+ backBtn.textContent=${backLabel};
1072
+ startBtn.textContent=${startLabel};
1073
+ completeBtn.textContent=${completeLabel};
1074
+ submitBtn.textContent=${submitLabel};
1075
+
1076
+ document.getElementById('dd-close').onclick=function(){ emitJSON('popup_close'); };
1077
+ backBtn.onclick=function(){ if(window.DeepdotsForm && window.DeepdotsForm.back){ window.DeepdotsForm.back(); } };
1078
+ startBtn.onclick=function(){ if(window.DeepdotsForm && window.DeepdotsForm.startForm){ window.DeepdotsForm.startForm(); } };
1079
+ completeBtn.onclick=function(){ emitJSON('popup_close'); };
1080
+ var surveyCompletedEmitted=false;
1081
+ submitBtn.onclick=function(){
1082
+ if(surveyCompletedEmitted) return;
1083
+ if(window.DeepdotsForm && window.DeepdotsForm.send){ window.DeepdotsForm.send(); }
1084
+ };
1085
+
1086
+ function updateButtons(state){
1087
+ backBtn.style.display='none';
1088
+ startBtn.style.display='none';
1089
+ submitBtn.style.display='none';
1090
+ completeBtn.style.display='none';
1091
+ backBtn.style.width='';
1092
+ startBtn.style.width='';
1093
+ submitBtn.style.width='';
1094
+ completeBtn.style.width='';
1095
+ if(state==='start'){
1096
+ startBtn.style.display='inline-flex';
1097
+ startBtn.style.width='100%';
1098
+ } else if(state==='in_progress_first'){
1099
+ submitBtn.style.display='inline-flex';
1100
+ } else if(state==='in_progress_next'){
1101
+ backBtn.style.display='inline-flex';
1102
+ submitBtn.style.display='inline-flex';
1103
+ } else if(state==='completed'){
1104
+ completeBtn.style.display='inline-flex';
1105
+ completeBtn.style.width='100%';
1106
+ }
1107
+ }
1108
+
1109
+ function setLoading(isLoading){
1110
+ spinner.style.display=isLoading?'flex':'none';
1111
+ if(!isLoading){ formHost.style.visibility='visible'; }
1112
+ footer.style.display=isLoading?'none':'flex';
1113
+ backBtn.disabled=isLoading; startBtn.disabled=isLoading; completeBtn.disabled=isLoading; submitBtn.disabled=isLoading;
1114
+ if(isLoading){ updateButtons(null); }
1115
+ }
1116
+ setLoading(true);
1117
+
1840
1118
  var initialized=false;
1841
1119
  function initMF(){
1842
1120
  if(initialized || !window.magicfeedback) return initialized;
@@ -1845,23 +1123,85 @@ function buildSurveyHtml(opts) {
1845
1123
  window.magicfeedback.init({ debug:false, env:'${env}' });
1846
1124
  var form = window.magicfeedback.form(${sid}, ${pid}, ${profileJson}, ${metaJson});
1847
1125
  window.DeepdotsForm = form;
1126
+ var stylesInjected=false;
1848
1127
  form.generate('mf', {
1849
1128
  addButton:false,
1850
1129
  getMetaData:true,
1851
- onLoadedEvent:function(){ emitJSON('loaded'); },
1852
- beforeSubmitEvent:function(){ emitJSON('before_submit'); },
1130
+ onLoadedEvent:function(args){
1131
+ var s=args && args.formData ? args.formData.style : null;
1132
+ if(s && !stylesInjected){
1133
+ stylesInjected=true;
1134
+ if(s.boxBackgroundColor){ popup.style.background=s.boxBackgroundColor; }
1135
+ if(s.contentAlign){ main.style.justifyContent = s.contentAlign==='center' ? 'center' : 'flex-start'; }
1136
+ if(s.buttonPrimaryColor){
1137
+ submitBtn.style.background=s.buttonPrimaryColor; submitBtn.style.border='none'; submitBtn.style.color='#fff';
1138
+ startBtn.style.background=s.buttonPrimaryColor; startBtn.style.border='none'; startBtn.style.color='#fff';
1139
+ completeBtn.style.background=s.buttonPrimaryColor; completeBtn.style.border='none'; completeBtn.style.color='#fff';
1140
+ }
1141
+ if(s.buttonSecondaryColor){
1142
+ backBtn.style.background='#fff'; backBtn.style.color=s.buttonSecondaryColor; backBtn.style.border='1px solid '+s.buttonSecondaryColor;
1143
+ }
1144
+ if(s.logo && !document.getElementById('dd-logo')){
1145
+ var logoImg=document.createElement('img');
1146
+ logoImg.id='dd-logo'; logoImg.src=s.logo; logoImg.alt='Logo';
1147
+ if(s.logoSize==='small'){ logoImg.style.maxHeight='30px'; }
1148
+ else if(s.logoSize==='medium'){ logoImg.style.maxHeight='50px'; }
1149
+ else if(s.logoSize==='large'){ logoImg.style.maxHeight='70px'; }
1150
+ if(s.logoPosition==='left'){ logoImg.style.margin='0 16px 20px 0'; logoImg.style.display='block'; logoImg.style.marginLeft='0'; }
1151
+ else if(s.logoPosition==='right'){ logoImg.style.margin='0 0 20px 16px'; logoImg.style.display='block'; logoImg.style.marginLeft='auto'; }
1152
+ else if(s.logoPosition==='center'){ logoImg.style.margin='0 auto 20px auto'; logoImg.style.display='block'; }
1153
+ main.insertBefore(logoImg, document.getElementById('dd-form-wrapper'));
1154
+ }
1155
+ if(s.startMessage && s.startMessage!==''){ updateButtons('start'); }
1156
+ else { updateButtons('in_progress_first'); }
1157
+ } else {
1158
+ updateButtons('in_progress_first');
1159
+ }
1160
+ emitJSON('loaded');
1161
+ setLoading(false);
1162
+ },
1163
+ beforeSubmitEvent:function(){
1164
+ setLoading(true);
1165
+ emitJSON('before_submit');
1166
+ },
1853
1167
  afterSubmitEvent:function(p){
1854
- try{ if(p && p.completed){ emitJSON('survey_completed', p); } else { emitJSON('after_submit', p); } }
1855
- catch(e){ emitJSON('after_submit'); }
1168
+ var error=p && p.error ? (typeof p.error==='string' ? p.error : String(p.error)) : '';
1169
+ if(error){
1170
+ setLoading(false);
1171
+ if(error.toLowerCase().indexOf('no response')!==-1){
1172
+ errorHint.textContent='Please answer the required question to continue.';
1173
+ errorHint.style.display='block';
1174
+ updateButtons('in_progress_next');
1175
+ return;
1176
+ }
1177
+ errorHint.textContent='An error occurred while submitting. Please try again or close the popup.';
1178
+ errorHint.style.display='block';
1179
+ return;
1180
+ }
1181
+ errorHint.style.display='none';
1182
+ setLoading(false);
1183
+ if(p && p.completed){
1184
+ surveyCompletedEmitted=true;
1185
+ updateButtons('completed');
1186
+ emitJSON('survey_completed', p);
1187
+ return;
1188
+ }
1189
+ if(p && p.total>1 && p.progress>0 && p.progress<p.total){ updateButtons('in_progress_next'); }
1190
+ else { updateButtons('in_progress_first'); }
1191
+ emitJSON('after_submit', p);
1856
1192
  },
1857
- onBackEvent:function(p){ emitJSON('back', p); }
1858
- }).catch(function(e){ emit('error:init'); });
1859
- }catch(e){ emit('error:exception'); }
1193
+ onBackEvent:function(p){
1194
+ if(p && p.progress===0){ updateButtons('in_progress_first'); }
1195
+ else { updateButtons('in_progress_next'); }
1196
+ emitJSON('back', p);
1197
+ }
1198
+ }).catch(function(e){ setLoading(false); emit('error:init'); });
1199
+ }catch(e){ setLoading(false); emit('error:exception'); }
1860
1200
  return true;
1861
1201
  }
1862
1202
  var s=document.createElement('script'); s.src=${JSON.stringify(cdn)}; s.async=true;
1863
1203
  s.onload=function(){ initMF(); };
1864
- s.onerror=function(){ emit('error:load'); };
1204
+ s.onerror=function(){ setLoading(false); emit('error:load'); };
1865
1205
  document.head.appendChild(s);
1866
1206
  var tries=0, t=setInterval(function(){ if(initMF()||++tries>40){ clearInterval(t); } },150);
1867
1207
  })();
@@ -1881,18 +1221,28 @@ var ReactNativePopupRenderer = class {
1881
1221
  }
1882
1222
  init() {
1883
1223
  }
1884
- show(surveyId, productId, _actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
1224
+ show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
1885
1225
  this.emitFn = emit;
1886
1226
  this.onCloseFn = onClose;
1887
1227
  this.currentSurveyId = surveyId;
1888
1228
  this.partialEmitted = false;
1889
1229
  const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);
1890
- const html = buildSurveyHtml({ surveyId, productId, env, profile, metadata, font: style?.font });
1230
+ const html = buildSurveyHtml({
1231
+ surveyId,
1232
+ productId,
1233
+ env,
1234
+ profile,
1235
+ metadata,
1236
+ font: style?.font,
1237
+ theme: style?.theme,
1238
+ position: style?.position,
1239
+ actions
1240
+ });
1891
1241
  if (this.options.onShow) {
1892
1242
  this.options.onShow({ surveyId, productId, html });
1893
1243
  } else {
1894
1244
  sdkWarn(
1895
- "[Deepdots] ReactNativePopupRenderer sin onShow: pasa { onShow } a new ReactNativePopupRenderer({...}) para montar el WebView del survey."
1245
+ "[Deepdots] ReactNativePopupRenderer has no onShow: pass { onShow } to new ReactNativePopupRenderer({...}) to mount the survey WebView."
1896
1246
  );
1897
1247
  }
1898
1248
  }
@@ -2010,7 +1360,7 @@ init_tracking_manager();
2010
1360
  // src/analytics/analytics-manager.ts
2011
1361
  function createDryRunSink(log = console.log) {
2012
1362
  return (payload) => log(
2013
- "[DeepdotsAnalytics] (dry-run \xB7 NO enviado \xB7 sin init.analytics) POST /sdk/feedback \u2192",
1363
+ "[DeepdotsAnalytics] (dry-run \xB7 NOT sent \xB7 no init.analytics) POST /sdk/feedback \u2192",
2014
1364
  JSON.stringify(payload, null, 2)
2015
1365
  );
2016
1366
  }
@@ -2079,6 +1429,15 @@ var AnalyticsManager = class {
2079
1429
  this.exitMiniService(name);
2080
1430
  }
2081
1431
  }
1432
+ /**
1433
+ * Olvida lo que pertenecía al usuario anterior (user attributes + métricas) al cambiar de
1434
+ * usuario. NO toca el buffer de eventos: el cierre de sesión ya lo vació con la identidad
1435
+ * vieja, y lo que llegue después es del usuario nuevo.
1436
+ */
1437
+ resetUserScope() {
1438
+ this.attributes = {};
1439
+ this.metrics = {};
1440
+ }
2082
1441
  /** Registra un evento de analítica (modelo GA: nombre + params). */
2083
1442
  track(name, params) {
2084
1443
  const current = this.getMiniService();
@@ -2166,7 +1525,7 @@ function pushKV(list, key, value) {
2166
1525
  if (value === void 0 || value === null || value === "") return;
2167
1526
  list.push({ key, value: [String(value)] });
2168
1527
  }
2169
- function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
1528
+ function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId, options) {
2170
1529
  const { context } = envelope;
2171
1530
  const profile = [];
2172
1531
  pushKV(profile, "external-user-id", envelope.userId);
@@ -2217,7 +1576,8 @@ function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
2217
1576
  },
2218
1577
  publicKey: keys.publicKey,
2219
1578
  integration: keys.integration,
2220
- completed: false,
1579
+ // Único marcador de cierre acordado con backend: el último lote de la sesión.
1580
+ completed: options?.sessionEnd === true,
2221
1581
  ...feedbackSessionId ? { sessionId: feedbackSessionId } : {}
2222
1582
  };
2223
1583
  }
@@ -2236,20 +1596,20 @@ function createFeedbackSink(options) {
2236
1596
  let feedbackSessionId;
2237
1597
  let firstPostInFlight = null;
2238
1598
  const url = `${options.baseUrl}/sdk/feedback`;
2239
- const post = async (body, final) => {
1599
+ const post = async (body, final, sessionEnd = false) => {
2240
1600
  const json = JSON.stringify(body);
2241
1601
  const small = json.length <= KEEPALIVE_MAX_BYTES;
2242
1602
  if (final && options.sendBeaconImpl && small) {
2243
1603
  const payload = typeof Blob !== "undefined" ? new Blob([json], { type: "application/json" }) : json;
2244
1604
  if (options.sendBeaconImpl(url, payload)) {
2245
- options.log?.("[DeepdotsAnalytics] flush final v\xEDa sendBeacon");
1605
+ options.log?.("[DeepdotsAnalytics] final flush via sendBeacon");
2246
1606
  return;
2247
1607
  }
2248
- options.log?.("[DeepdotsAnalytics] sendBeacon rechaz\xF3 el lote; fallback a fetch");
1608
+ options.log?.("[DeepdotsAnalytics] sendBeacon rejected the batch; falling back to fetch");
2249
1609
  }
2250
1610
  const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
2251
1611
  if (!f) {
2252
- options.log?.("[DeepdotsAnalytics] no fetch disponible; payload no enviado", body);
1612
+ options.log?.("[DeepdotsAnalytics] no fetch available; payload not sent", body);
2253
1613
  return;
2254
1614
  }
2255
1615
  const res = await f(url, {
@@ -2262,19 +1622,20 @@ function createFeedbackSink(options) {
2262
1622
  if (!res.ok) {
2263
1623
  const detail = await safeBodyText(res);
2264
1624
  if (isRetryableStatus(res.status)) {
2265
- throw new Error(`POST /sdk/feedback ${res.status} (reintentable): ${detail}`);
1625
+ throw new Error(`POST /sdk/feedback ${res.status} (retryable): ${detail}`);
2266
1626
  }
2267
1627
  options.log?.(
2268
- `[DeepdotsAnalytics] POST /sdk/feedback rechazado con ${res.status}; lote DESCARTADO:`,
1628
+ `[DeepdotsAnalytics] POST /sdk/feedback rejected with ${res.status}; batch DISCARDED:`,
2269
1629
  detail
2270
1630
  );
2271
1631
  return;
2272
1632
  }
1633
+ if (sessionEnd) return;
2273
1634
  try {
2274
1635
  const data = await res.json();
2275
1636
  if (data?.sessionId && data.sessionId !== feedbackSessionId) {
2276
1637
  feedbackSessionId = data.sessionId;
2277
- options.log?.("[DeepdotsAnalytics] feedbackSessionId cacheado:", feedbackSessionId);
1638
+ options.log?.("[DeepdotsAnalytics] feedbackSessionId cached:", feedbackSessionId);
2278
1639
  options.onSessionId?.(feedbackSessionId);
2279
1640
  }
2280
1641
  } catch {
@@ -2282,13 +1643,18 @@ function createFeedbackSink(options) {
2282
1643
  };
2283
1644
  return async (envelope, meta) => {
2284
1645
  const final = meta?.final === true;
1646
+ const sessionEnd = meta?.sessionEnd === true;
2285
1647
  if (!feedbackSessionId && firstPostInFlight && !final) {
2286
1648
  await firstPostInFlight;
2287
1649
  }
2288
- const body = buildAnalyticsFeedbackBody(envelope, options.keys, feedbackSessionId);
1650
+ const body = buildAnalyticsFeedbackBody(envelope, options.keys, feedbackSessionId, { sessionEnd });
2289
1651
  options.log?.("[DeepdotsAnalytics] POST /sdk/feedback \u2192", body);
2290
- const sent = post(body, final);
2291
- if (!feedbackSessionId) {
1652
+ const sent = post(body, final, sessionEnd);
1653
+ if (sessionEnd) {
1654
+ feedbackSessionId = void 0;
1655
+ firstPostInFlight = null;
1656
+ options.onSessionReset?.();
1657
+ } else if (!feedbackSessionId) {
2292
1658
  firstPostInFlight = sent.then(
2293
1659
  () => void 0,
2294
1660
  () => void 0
@@ -2727,6 +2093,7 @@ var CrashReporter = class {
2727
2093
  init_logger();
2728
2094
 
2729
2095
  // src/analytics/messaging.ts
2096
+ var MESSAGE_CHANNELS = ["push", "in_app"];
2730
2097
  function buildMessageParams(stage, o) {
2731
2098
  const p = {
2732
2099
  stage,
@@ -2739,6 +2106,57 @@ function buildMessageParams(stage, o) {
2739
2106
  if (o.currency) p.currency = o.currency;
2740
2107
  return { ...p, ...o.params ?? {} };
2741
2108
  }
2109
+ var MAX_TRACKED_MESSAGES = 500;
2110
+ var MessageGuard = class {
2111
+ constructor(maxTrackedMessages = MAX_TRACKED_MESSAGES) {
2112
+ this.maxTrackedMessages = maxTrackedMessages;
2113
+ /** message_id → canal fijado + stages ya emitidos. El orden de inserción marca la eviction. */
2114
+ this.seen = /* @__PURE__ */ new Map();
2115
+ }
2116
+ evaluate(stage, options) {
2117
+ const channel = options.channel;
2118
+ if (!MESSAGE_CHANNELS.includes(channel)) {
2119
+ return {
2120
+ emit: false,
2121
+ reason: "invalid_channel",
2122
+ detail: `invalid channel "${channel}" (expected ${MESSAGE_CHANNELS.join(" | ")})`
2123
+ };
2124
+ }
2125
+ const entry = this.seen.get(options.id);
2126
+ if (!entry) {
2127
+ this.remember(options.id, channel, stage);
2128
+ return { emit: true };
2129
+ }
2130
+ if (entry.channel !== channel) {
2131
+ return {
2132
+ emit: false,
2133
+ reason: "channel_conflict",
2134
+ detail: `message_id "${options.id}" was already reported on channel "${entry.channel}"; discarding "${channel}"`
2135
+ };
2136
+ }
2137
+ if (entry.stages.has(stage)) {
2138
+ return {
2139
+ emit: false,
2140
+ reason: "duplicate_stage",
2141
+ detail: `stage "${stage}" was already sent for message_id "${options.id}"`
2142
+ };
2143
+ }
2144
+ entry.stages.add(stage);
2145
+ return { emit: true };
2146
+ }
2147
+ /** Olvida todo lo vigilado (nueva sesión). */
2148
+ reset() {
2149
+ this.seen.clear();
2150
+ }
2151
+ remember(id, channel, stage) {
2152
+ this.seen.set(id, { channel, stages: /* @__PURE__ */ new Set([stage]) });
2153
+ while (this.seen.size > this.maxTrackedMessages) {
2154
+ const oldest = this.seen.keys().next();
2155
+ if (oldest.done) break;
2156
+ this.seen.delete(oldest.value);
2157
+ }
2158
+ }
2159
+ };
2742
2160
 
2743
2161
  // src/core/deepdots-popups.ts
2744
2162
  var EXIT_QUEUE_STORAGE_KEY = "__deepdots_exit_popup_queue__";
@@ -2780,14 +2198,24 @@ var DeepdotsPopups = class {
2780
2198
  this.analyticsFlushTimer = void 0;
2781
2199
  /** Crash & error reporting (#14–17). Null hasta init(). */
2782
2200
  this.crashReporter = null;
2201
+ /** Protecciones del funnel de Messaging (#18–22). Vigencia de sesión: se reinicia en init(). */
2202
+ this.messageGuard = new MessageGuard();
2783
2203
  /** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
2784
2204
  this.navObserver = null;
2785
2205
  /** Tiempo activo (engagement time, #8). */
2786
2206
  this.engagement = null;
2787
2207
  /** Atributos internos del usuario identificado → POST /sdk/popups/contact. Null si no hay userId. */
2788
2208
  this.contact = null;
2209
+ /** Storage resuelto en init() (inyectado por el host o localStorage). Reusado al cambiar de usuario. */
2210
+ this.storage = null;
2789
2211
  /** Marca si ya se inició la navegación manual (setScreen) — para RN sin History API. */
2790
2212
  this.navStarted = false;
2213
+ /**
2214
+ * `true` entre `session_start` y `session_end`. Hace idempotente tanto la apertura como
2215
+ * el cierre (p. ej. `visibilitychange` + `pagehide` seguidos) y permite abrir una sesión
2216
+ * nueva al volver a foreground o al conceder el consentimiento más tarde.
2217
+ */
2218
+ this.sessionOpen = false;
2791
2219
  /**
2792
2220
  * Idioma resuelto en init() (explícito > navigator.language > Intl). Única fuente de
2793
2221
  * verdad para el context de analytics Y para la segmentación por idioma de los popups.
@@ -2799,6 +2227,11 @@ var DeepdotsPopups = class {
2799
2227
  this.logger = config.logger ?? console;
2800
2228
  setLogger(this.logger);
2801
2229
  if (this.initialized) {
2230
+ const nextUserId = config.userId || void 0;
2231
+ if (nextUserId !== this.config?.userId) {
2232
+ this.setUserId(nextUserId);
2233
+ return;
2234
+ }
2802
2235
  this.log("SDK already initialized");
2803
2236
  return;
2804
2237
  }
@@ -2811,6 +2244,7 @@ var DeepdotsPopups = class {
2811
2244
  userId: config.userId || void 0
2812
2245
  };
2813
2246
  const storage = config.storage ?? createDefaultStorage();
2247
+ this.storage = storage;
2814
2248
  this.tracking = new TrackingManager({
2815
2249
  storage,
2816
2250
  clientUserId: this.config.userId,
@@ -2838,6 +2272,9 @@ var DeepdotsPopups = class {
2838
2272
  sendBeaconImpl: typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" ? (url, body) => navigator.sendBeacon(url, body) : void 0,
2839
2273
  onSessionId: (id) => {
2840
2274
  this.analyticsFeedbackSessionId = id;
2275
+ },
2276
+ onSessionReset: () => {
2277
+ this.analyticsFeedbackSessionId = void 0;
2841
2278
  }
2842
2279
  }) : createDryRunSink((...a) => this.logger.log(...a));
2843
2280
  const device = config.device ?? collectDeviceInfo(config.appVersion);
@@ -2846,6 +2283,7 @@ var DeepdotsPopups = class {
2846
2283
  navigator: typeof navigator !== "undefined" ? navigator : void 0,
2847
2284
  intl: typeof Intl !== "undefined" ? Intl : void 0
2848
2285
  });
2286
+ this.messageGuard.reset();
2849
2287
  this.analytics = new AnalyticsManager({
2850
2288
  sink: analyticsSink,
2851
2289
  publicKey: config.analytics?.publicKey ?? this.config.apiKey,
@@ -2881,7 +2319,7 @@ var DeepdotsPopups = class {
2881
2319
  this.engagement = new EngagementTracker();
2882
2320
  this.engagement.resume();
2883
2321
  this.setupAnalyticsFlush();
2884
- this.track("deepdots_session_start", {});
2322
+ this.openSession();
2885
2323
  const pendingCrashes = this.crashReporter.drainPendingCrashes();
2886
2324
  if (this.tracking?.isTrackingEnabled()) {
2887
2325
  for (const rec of pendingCrashes) {
@@ -2913,7 +2351,9 @@ var DeepdotsPopups = class {
2913
2351
  }
2914
2352
  /** Activa/desactiva el tracking (identidad + sesión + analytics). Kill-switch del contrato §7bis. */
2915
2353
  setTrackingEnabled(enabled) {
2354
+ if (!enabled) this.closeSession("tracking_disabled");
2916
2355
  this.tracking?.setTrackingEnabled(enabled);
2356
+ if (enabled) this.openSession();
2917
2357
  this.log("tracking \xB7 setTrackingEnabled:", enabled, "\xB7 session_id:", this.tracking?.getSessionId() ?? null);
2918
2358
  }
2919
2359
  // ───────── Analytics (canal separado del feedback, vinculado por user_id) ─────────
@@ -2983,8 +2423,18 @@ var DeepdotsPopups = class {
2983
2423
  trackFunnelStep(funnel, step, taskId, params) {
2984
2424
  this.track("deepdots_funnel_step", { funnel, step, task_id: taskId, ...params ?? {} });
2985
2425
  }
2986
- /** Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app). No-op si tracking off. */
2426
+ /**
2427
+ * Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app).
2428
+ * No-op si tracking off. El evento se descarta (con warning) si el `channel` no es válido,
2429
+ * si ese `(message_id, stage)` ya se emitió en la sesión, o si el `message_id` ya se reportó
2430
+ * en otro canal — ver `MessageGuard`.
2431
+ */
2987
2432
  trackMessage(stage, options) {
2433
+ const verdict = this.messageGuard.evaluate(stage, options);
2434
+ if (!verdict.emit) {
2435
+ console.warn(`[DeepdotsPopups] trackMessage discarded (${verdict.reason}): ${verdict.detail}`);
2436
+ return;
2437
+ }
2988
2438
  this.track("deepdots_message", buildMessageParams(stage, options));
2989
2439
  }
2990
2440
  /**
@@ -3000,21 +2450,86 @@ var DeepdotsPopups = class {
3000
2450
  this.navObserver.visit(name);
3001
2451
  }
3002
2452
  }
3003
- /** App a foreground (RN: AppState 'active'): reanuda el engagement time. */
2453
+ /** App a foreground (RN: AppState 'active'): reanuda el engagement time y abre sesión nueva. */
3004
2454
  onForeground() {
3005
2455
  this.engagement?.resume();
2456
+ this.openSession();
3006
2457
  }
3007
2458
  /**
3008
- * App a background (RN: AppState 'background'): cierra pantalla actual (page_view),
3009
- * mini-service (mini_service_exit), emite engagement y hace flush del lote.
2459
+ * App a background (RN: AppState 'background'): FIN DE SESIÓN. Cierra pantalla actual
2460
+ * (page_view), mini-services (mini_service_exit), emite engagement + `session_end` y
2461
+ * envía el último lote con `completed:true`.
2462
+ *
2463
+ * Es la única señal disponible en móvil: el kill de la app (swipe) NO da callback.
3010
2464
  */
3011
2465
  onBackground() {
2466
+ this.closeSession("background");
2467
+ this.engagement?.pause();
2468
+ }
2469
+ /**
2470
+ * Cierre EXPLÍCITO de la sesión por parte del host (logout, fin de flujo…): emite
2471
+ * `session_end` y envía el último lote con `completed:true`.
2472
+ */
2473
+ endSession() {
2474
+ this.closeSession("manual");
2475
+ }
2476
+ /**
2477
+ * Cambio de usuario (login / logout / cambio de cuenta). Cierra la sesión del usuario
2478
+ * anterior (`session_end` con `reason: 'user_change'` + `completed:true`), cambia la
2479
+ * identidad y abre una sesión nueva. Sin `userId` vuelve al id anónimo del SDK.
2480
+ *
2481
+ * Equivale a llamar a `init()` otra vez con otro `userId`.
2482
+ */
2483
+ setUserId(userId) {
2484
+ const next = userId || void 0;
2485
+ if (!this.initialized || !this.config || next === this.config.userId) return;
2486
+ this.closeSession("user_change");
2487
+ const enabled = this.tracking?.isTrackingEnabled() ?? true;
2488
+ const storage = this.storage ?? createDefaultStorage();
2489
+ this.config.userId = next;
2490
+ this.tracking = new TrackingManager({ storage, clientUserId: next, enabled });
2491
+ this.contact = next ? new ContactManager({
2492
+ storage,
2493
+ publicKey: this.config.apiKey ?? "",
2494
+ userId: next,
2495
+ post: (body) => this.postContact(body)
2496
+ }) : null;
2497
+ this.analytics?.resetUserScope();
2498
+ this.messageGuard.reset();
2499
+ this.log("tracking \xB7 user_change \xB7 user_id:", this.tracking.getUserId());
2500
+ this.openSession();
2501
+ }
2502
+ /**
2503
+ * Cierra la sesión actual: vuelca todo lo que quedaba abierto y envía el último lote
2504
+ * marcado con `completed:true`, para que el backend cierre el registro. El `sessionId`
2505
+ * cacheado se olvida → el lote siguiente abre un registro nuevo.
2506
+ *
2507
+ * Idempotente: dos cierres seguidos (p. ej. `visibilitychange` + `pagehide`) no duplican
2508
+ * el `session_end`.
2509
+ */
2510
+ closeSession(reason) {
2511
+ if (!this.sessionOpen) return;
2512
+ if (!this.tracking?.isTrackingEnabled()) return;
2513
+ this.sessionOpen = false;
3012
2514
  this.navObserver?.stop();
3013
2515
  this.navStarted = false;
3014
- if (this.tracking?.isTrackingEnabled()) this.analytics?.exitAllMiniServices();
2516
+ this.analytics?.exitAllMiniServices();
3015
2517
  this.flushEngagement();
3016
- this.engagement?.pause();
3017
- this.flushAnalytics();
2518
+ this.track("deepdots_session_end", { reason });
2519
+ this.flushAnalytics({ final: reason === "page_hide", sessionEnd: true });
2520
+ this.analyticsFeedbackSessionId = void 0;
2521
+ this.tracking?.setSessionId(null);
2522
+ this.log("tracking \xB7 session_end:", reason);
2523
+ }
2524
+ /**
2525
+ * Abre sesión (`session_start`) si no hay una abierta y el tracking está activo. Idempotente:
2526
+ * se llama en init(), al volver a foreground y al conceder el consentimiento.
2527
+ */
2528
+ openSession() {
2529
+ if (this.sessionOpen) return;
2530
+ if (!this.tracking?.isTrackingEnabled()) return;
2531
+ this.sessionOpen = true;
2532
+ this.track("deepdots_session_start", {});
3018
2533
  }
3019
2534
  /** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
3020
2535
  previewAnalytics() {
@@ -3052,10 +2567,7 @@ var DeepdotsPopups = class {
3052
2567
  this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);
3053
2568
  window.addEventListener("pagehide", () => {
3054
2569
  clearInterval(this.analyticsFlushTimer);
3055
- this.navObserver?.stop();
3056
- if (this.tracking?.isTrackingEnabled()) this.analytics?.exitAllMiniServices();
3057
- this.flushEngagement();
3058
- this.flushAnalytics({ final: true });
2570
+ this.closeSession("page_hide");
3059
2571
  });
3060
2572
  document.addEventListener("visibilitychange", () => {
3061
2573
  if (document.visibilityState === "hidden") {
@@ -3730,7 +3242,8 @@ function setupReactNative(sdk2, config, deps = {}) {
3730
3242
  if (deps.appState?.addEventListener) {
3731
3243
  subscription = deps.appState.addEventListener("change", (state) => {
3732
3244
  if (state === "active") sdk2.onForeground();
3733
- else sdk2.onBackground();
3245
+ else if (state === "background") sdk2.onBackground();
3246
+ else sdk2.flushAnalytics();
3734
3247
  });
3735
3248
  }
3736
3249
  return () => {
@@ -3776,9 +3289,10 @@ function DeepdotsProvider({ config, children }) {
3776
3289
  survey ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Modal, { visible: true, transparent: true, animationType: "slide", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3777
3290
  import_react_native_webview.WebView,
3778
3291
  {
3292
+ style: { flex: 1 },
3779
3293
  originWhitelist: ["*"],
3780
3294
  javaScriptEnabled: true,
3781
- source: { html: survey.html },
3295
+ source: { html: survey.html, baseUrl: "https://sdk.deepdots.com/" },
3782
3296
  onMessage: (e) => rendererRef.current?.handleMessage(e.nativeEvent.data)
3783
3297
  }
3784
3298
  ) }) }) : null