@contensis/forms
Version:
Render Contensis Forms with React
1,700 lines (1,678 loc) • 110 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
ContensisForm: () => ContensisForm
});
module.exports = __toCommonJS(src_exports);
// src/components/Form.tsx
var import_react37 = __toESM(require("react"));
// src/state/captcha.ts
var CAPTCHA_LOAD_CALLBACK = "on_captcha_load";
function getCaptchaUrl(siteKey) {
return `https://www.google.com/recaptcha/api.js?onload=${CAPTCHA_LOAD_CALLBACK}&render=${siteKey}`;
}
function load(captcha) {
if (captcha?.enabled && captcha?.siteKey) {
ensureLoadCallback();
const captchaUrl = getCaptchaUrl(captcha.siteKey);
const head = document.getElementsByTagName("head")[0];
const scripts = [...head.getElementsByTagName("script")];
const scriptSrcs = scripts.map((s) => s.src);
const hasCaptcha = scriptSrcs.includes(captchaUrl);
if (!hasCaptcha) {
const captcha2 = document.createElement("script");
captcha2.src = captchaUrl;
head.appendChild(captcha2);
}
}
}
async function submit(formId, captcha) {
if (captcha?.enabled && captcha?.siteKey) {
load(captcha);
await window[CAPTCHA_LOAD_CALLBACK].loaded;
await new Promise((resolve) => grecaptcha.ready(() => resolve(true)));
return grecaptcha.execute(captcha.siteKey, { action: `${formId}_submit` });
} else {
return Promise.resolve("");
}
}
function ensureLoadCallback() {
if (!window[CAPTCHA_LOAD_CALLBACK]) {
let loadedResolve;
const loaded = new Promise((resolve) => {
loadedResolve = resolve;
});
const callback = function() {
loadedResolve(null);
};
Object.assign(callback, { loaded });
window[CAPTCHA_LOAD_CALLBACK] = callback;
}
}
var Captcha = {
load,
submit
};
// src/state/version.ts
function isPublishedVersion(versionStatus) {
return !versionStatus || versionStatus === "published";
}
var Version = {
isPublishedVersion
};
// src/state/api.ts
function getAllCookies() {
return (document.cookie || "").split("; ").reduce(
(cookies, cookieString) => {
const parts = cookieString.split("=");
const name = decode(parts[0]);
if (name) {
cookies[name] = decode(parts.slice(1).join("="));
}
return cookies;
},
{}
);
}
function decode(s) {
try {
return decodeURIComponent(s);
} catch (e) {
return s;
}
}
function setCookie(key, value, expiry) {
document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; expires=${expiry.toUTCString()}; path=/;`;
}
var CmsBearerTokenCookie = "ContensisSecurityBearerToken";
var CmsRefreshTokenCookie = "ContensisSecurityRefreshToken";
var RefreshTokenCookie = "RefreshToken";
var RefreshTokenExpiryTime = 15 * 24 * 3600 * 1e3;
var SCOPE = "Security_Administrator ContentType_Read ContentType_Write ContentType_Delete Entry_Read Entry_Write Entry_Delete Project_Read Project_Write Project_Delete Workflow_Administrator";
var GRANT_TYPE = "contensis_classic_refresh_token";
function isLoggedIn() {
const cookies = getAllCookies();
return !!cookies[CmsBearerTokenCookie];
}
async function getBearerToken(options) {
const cookies = getAllCookies();
if (cookies[CmsBearerTokenCookie]) {
return cookies[CmsBearerTokenCookie];
}
const refreshToken = cookies[CmsRefreshTokenCookie] || cookies[RefreshTokenCookie];
if (!refreshToken) {
return null;
}
try {
const currentDate = /* @__PURE__ */ new Date();
const response = await fetch(`${options.apiUrl}/authenticate/connect/token`, {
headers: {
"content-type": "application/x-www-form-urlencoded;charset=UTF-8"
},
body: `scope=${encodeURIComponent(SCOPE)}&grant_type=${encodeURIComponent(GRANT_TYPE)}&refresh_token=${encodeURIComponent(refreshToken)}`,
method: "POST",
mode: "cors",
credentials: "omit"
});
if (!response.ok) {
return null;
}
const data = await response.json();
const bearerToken = data.access_token;
const bearerTokenExpiryDate = new Date(currentDate.getTime() + data.expires_in * 1e3);
const newRefreshToken = data.refresh_token;
const refreshTokenExpiryDate = new Date(currentDate.getTime() + RefreshTokenExpiryTime);
setCookie(CmsBearerTokenCookie, bearerToken, bearerTokenExpiryDate);
if (newRefreshToken) {
setCookie(RefreshTokenCookie, newRefreshToken, refreshTokenExpiryDate);
}
return bearerToken;
} catch (e) {
return null;
}
}
async function getDefaultHeaders(options) {
const bearerToken = await getBearerToken(options);
const headers = {
"content-type": "application/json"
};
if (bearerToken) {
headers.authorization = `Bearer ${bearerToken}`;
}
return headers;
}
async function getForm({ apiUrl, projectId, formId, language, versionStatus }, signal) {
const query = versionStatus === "latest" ? `?versionStatus=${versionStatus}` : "";
const headers = await getDefaultHeaders({ apiUrl });
const response = await fetch(`${apiUrl}/api/forms/projects/${projectId}/contentTypes/${formId}/languages/${language || "default"}${query}`, {
headers,
method: "GET",
mode: "cors",
signal
});
if (response.ok) {
const result = await response.json();
return result;
} else {
const error = await response.json();
return Promise.reject({
status: response.status,
statusText: response.statusText,
message: error?.message,
error
});
}
}
async function saveFormResponse({
apiUrl,
projectId,
formId,
language,
formVersionNo,
versionStatus,
formResponse,
captcha
}) {
const headers = await getDefaultHeaders({ apiUrl });
const captchaResponse = Version.isPublishedVersion(versionStatus) && !isLoggedIn() ? await Captcha.submit(formId, captcha) : "";
let url = `${apiUrl}/api/forms/projects/${projectId}/contentTypes/${formId}/languages/${language || "default"}/entries`;
url = Version.isPublishedVersion(versionStatus) && formVersionNo ? url : `${url}?contentTypePreviewVersion=${formVersionNo}`;
const response = await fetch(url, {
headers: {
...headers,
...!!captchaResponse ? { "g-recaptcha-response": captchaResponse } : {}
},
method: "POST",
body: JSON.stringify(formResponse),
mode: "cors"
});
if (response.ok) {
const result = await response.json();
return result;
} else {
const error = await response.json();
return Promise.reject({
status: response.status,
statusText: response.statusText,
message: error?.message,
error
});
}
}
var Api = {
isLoggedIn,
getForm,
saveFormResponse
};
// src/state/dates/date-utils.ts
function pad(n, length = 2) {
const padding = Array.from({ length }).map(() => "0").join("");
return `${padding}${n}`.slice(-1 * length);
}
function padYear(year) {
if (year >= 100) {
return year;
}
year = 1900 + year;
const currentYear = (/* @__PURE__ */ new Date()).getUTCFullYear();
return currentYear - year > 80 ? year + 100 : year;
}
function toNumber(n) {
if (typeof n === "number") {
return n;
}
if (typeof n === "string" && !!n) {
const num = Number(n);
if (!Number.isNaN(num)) {
return num;
}
}
return null;
}
function isWithinRange(n, min, max) {
if (typeof n === "number") {
return n >= min && n <= max;
}
return false;
}
var INVALID_DATE = "Invalid date";
var INVALID_TIME = "Invalid time";
function isDate(d) {
return Object.prototype.toString.call(d) === "[object Date]";
}
function isValidDate(dt) {
return !Number.isNaN(Number(dt));
}
function validateDateTimeParts(parts) {
if (!parts?.year && !parts?.month && !parts?.day && !parts?.hour && !parts?.minute) {
return { datetime: null };
}
const { year, month, day, ...timeParts } = parts;
const date = validateDateParts({ year, month, day });
const time = validateTimeParts(timeParts);
if (date.invalid || time.invalid) {
return {
datetime: INVALID_DATE,
invalid: [...date.invalid || [], ...time.invalid || []]
};
}
return {
datetime: date.date.substring(0, 11) + time.time
};
}
function validateDateParts(parts) {
if (!parts?.year && !parts?.month && !parts?.day) {
return { date: null };
}
const year = toNumber(parts.year);
const month = toNumber(parts.month);
const day = toNumber(parts.day);
const invalid = [];
if (!isWithinRange(year, 0, 3e3)) {
invalid.push("year");
}
if (!isWithinRange(month, 1, 12)) {
invalid.push("month");
}
if (!isWithinRange(day, 1, 31)) {
invalid.push("day");
}
if (!invalid.includes("year") && !invalid.includes("month") && !invalid.includes("day")) {
const monthDays = daysInMonth(year, month);
if (day > monthDays) {
invalid.push("day");
}
}
if (invalid.length) {
return {
date: INVALID_DATE,
invalid
};
}
return {
date: `${padYear(year)}-${pad(month)}-${pad(day)}T00:00`
};
}
function validateTimeParts(parts) {
if (!parts?.hour && !parts?.minute) {
return { time: null };
}
const hour = toNumber(parts.hour);
const minute = toNumber(parts.minute);
const { timeFormat, period } = parts;
let hour24 = hour;
const invalid = [];
if (timeFormat === "12h") {
if (!isWithinRange(hour, 1, 12)) {
invalid.push("hour");
} else {
if (hour === 12) {
hour24 = period === "am" ? 0 : hour;
} else {
hour24 = period === "pm" ? hour + 12 : hour;
}
}
} else {
if (!isWithinRange(hour, 0, 23)) {
invalid.push("hour");
}
}
if (!isWithinRange(minute, 0, 59)) {
invalid.push("minute");
}
if (invalid.length) {
return {
time: INVALID_TIME,
invalid
};
}
return {
time: `${pad(hour24)}:${pad(minute)}`
};
}
function daysInMonth(year, month) {
const date = new Date(year, month, 1);
date.setDate(0);
return date.getDate();
}
// src/state/dates/time-parsing.ts
function parseTime(time) {
if (!time) {
return time;
}
const match = [match3Digit, match4Digit].reduce(
(prev, fn) => {
return prev || fn(time);
},
null
);
if (!match) {
return INVALID_TIME;
}
let { hour, minute, period } = match;
if (Number.isNaN(hour) || Number.isNaN(minute)) {
return INVALID_TIME;
}
if (hour === 24) {
if (minute === 0) {
hour = 0;
} else {
return INVALID_TIME;
}
}
if (hour === 0 && period === "pm") {
return INVALID_TIME;
}
if (period === "am" && hour > 12) {
return INVALID_TIME;
}
if (period === "am" && hour === 12) {
hour = 0;
}
if (period === "pm" && hour < 12) {
hour += 12;
}
if (hour > 24 || hour < 0) {
return INVALID_TIME;
}
if (minute > 59 || minute < 0) {
return INVALID_TIME;
}
return `${pad(hour)}:${pad(minute)}`;
}
function match3Digit(time) {
const timeRegex = /^(\d{1,3})([ .])?(am|a.m|a.m.|pm|p.m|p.m.)?$/;
const match = time.match(timeRegex);
if (!match) {
return null;
}
let t = Number(match[1]);
if (Number.isNaN(t)) {
return null;
}
let hour = 0;
let minute = 0;
const period = match[3];
const isAm = !!period?.startsWith("a");
const isPm = !!period?.startsWith("p");
if (t < 100) {
hour = t;
} else {
hour = Math.floor(t / 100);
minute = t % 100;
}
return {
hour,
minute,
period: isAm ? "am" : isPm ? "pm" : null
};
}
function match4Digit(time) {
const timeRegex = /^(\d{1,2})([.: ])?(\d{1,2})?([ .])?(am|a.m|am.|a.m.|pm|p.m|pm.|p.m.)?$/;
const match = time.match(timeRegex);
if (!match) {
return null;
}
let hour = Number(match[1]);
const minute = Number(match[3]);
const period = match[5];
const isAm = !!period?.startsWith("a");
const isPm = !!period?.startsWith("p");
return {
hour,
minute,
period: isAm ? "am" : isPm ? "pm" : null
};
}
// src/state/dates/locale.ts
var DEFAULT_LOCALE = "default";
function isDatePartKey(key) {
return key === "day" || key === "month" || key === "year";
}
var localeInfo = /* @__PURE__ */ function() {
let info = void 0;
const createLocaleInfo = function() {
const shortDateFormatter = new Intl.DateTimeFormat(DEFAULT_LOCALE, { dateStyle: "short" });
const shortTimeFormatter = new Intl.DateTimeFormat(DEFAULT_LOCALE, { timeStyle: "short" });
const shortDatePartsOrder = shortDateFormatter.formatToParts().map((p) => p.type).filter(isDatePartKey);
const formatters = [dateFormatter("short"), dateFormatter("medium"), dateFormatter("long"), dateFormatter("full")];
return {
formatters,
shortDateMatchToParts(match) {
return {
[shortDatePartsOrder[0]]: match[0],
[shortDatePartsOrder[1]]: match[1],
[shortDatePartsOrder[2]]: match[2]
};
},
shortDateTimeMatchToParts(match) {
return {
[shortDatePartsOrder[0]]: match[0],
[shortDatePartsOrder[1]]: match[1],
[shortDatePartsOrder[2]]: match[2],
hour: match[3],
minute: match[4]
};
},
toShortDateString(input) {
if (!input) {
return "";
}
const dt = isDate(input) ? input : new Date(input);
return isValidDate(dt) ? shortDateFormatter.format(dt) : "";
},
toShortDateTimeString(input) {
if (!input) {
return "";
}
const dt = isDate(input) ? input : new Date(input);
return isValidDate(dt) ? `${shortDateFormatter.format(dt)} ${shortTimeFormatter.format(dt)}` : "";
}
};
};
return function() {
info = info || createLocaleInfo();
return info;
};
}();
function dateFormatter(dateStyle) {
const dtf = new Intl.DateTimeFormat(DEFAULT_LOCALE, { dateStyle });
const parts = dtf.formatToParts();
const monthNames = Array.from({ length: 12 }).map((_, i) => {
const parts2 = dtf.formatToParts(new Date(2e3, i, 1));
const monthPart = parts2.find((p) => p.type === "month");
return monthPart?.value || "";
});
return { parts, monthNames };
}
function parseDateFromFormatter(input, formatter) {
const pattern = createDateParsePattern(formatter.parts);
const result = {};
for (const p of pattern) {
if (p.literal) {
const i = input.indexOf(p.literal);
if (i >= 0) {
result[p.type] = input.substring(0, i);
input = input.substring(i + p.literal.length);
}
} else {
result[p.type] = input;
}
}
if (result.day && result.month && result.year) {
let month = Number(result.month);
if (Number.isNaN(month)) {
const index = formatter.monthNames.findIndex((m) => !!m && m?.toUpperCase() === result.month?.toUpperCase());
month = index >= 0 ? index + 1 : month;
}
const dt = validateDateParts({
year: result.year,
month: `${month}`,
day: result.day
});
if (!dt.invalid) {
return dt;
}
}
}
function createDateParsePattern(parts) {
return parts.reduce((prev, part) => {
if (part.type === "literal") {
if (!!prev[prev.length - 1]) {
prev[prev.length - 1].literal = part.value;
}
} else {
prev.push({ type: part.type });
}
return prev;
}, []);
}
function parseDateTimeFromFormatter(input, formatter) {
const pattern = createDateTimeParsePattern(formatter.parts);
const result = {};
for (const p of pattern) {
if (p.literal) {
const i = input.indexOf(p.literal);
if (i >= 0) {
result[p.type] = input.substring(0, i);
input = input.substring(i + p.literal.length);
}
} else {
result[p.type] = input;
}
}
let time = "";
if (input) {
time = parseTime(input);
if (!time) {
return void 0;
}
}
if (result.day && result.month && result.year) {
let month = Number(result.month);
if (Number.isNaN(month)) {
const index = formatter.monthNames.findIndex((m) => !!m && m?.toUpperCase() === result.month?.toUpperCase());
month = index >= 0 ? index + 1 : month;
}
const hour = !!time ? time.split(":")[0] : void 0;
const minute = !!time ? time.split(":")[1] : void 0;
const dt = validateDateTimeParts({
year: result.year,
month: `${month}`,
day: result.day,
hour,
minute
});
if (!dt.invalid) {
return dt;
}
}
}
function createDateTimeParsePattern(parts) {
return parts.reduce((prev, part) => {
if (part.type === "literal") {
if (!!prev[prev.length - 1]) {
prev[prev.length - 1].literal = part.value;
}
} else {
prev.push({ type: part.type, literal: " " });
}
return prev;
}, []);
}
// src/state/dates/date-parsing.ts
var ISO_REG_EX = /^([0-9]{4})-([0-1][0-9])(?:-([0-3][0-9]))?(?:[T ]?([0-2][0-9])(?::([0-5][0-9]))?(?::([0-5][0-9]))?)?(?:\.([0-9]+))?(Z|(?:\+|-)[0-9]{4})?$/;
var SHORT_DATE_REG_EX = /^(\d{1,2})(\/|-|\.)(\d{1,2})(\/|-|\.)(\d{2,4})$/;
function parseDate(date) {
if (!date) {
return date;
}
let d = parseIso(date);
if (d) {
return d.date;
}
d = parseShortDate(date);
if (d) {
return d.date;
}
d = localeInfo().formatters.reduce((prev, formatter) => prev || parseDateFromFormatter(date, formatter), void 0);
if (d) {
return d.date;
}
return INVALID_DATE;
}
function parseIso(date) {
let match = date.match(ISO_REG_EX);
if (match) {
const year = match[1];
const month = match[2];
const day = match[3];
return validateDateParts({ year, month, day });
}
}
function parseShortDate(date) {
let match = date.match(SHORT_DATE_REG_EX);
if (match) {
const dateParts = localeInfo().shortDateMatchToParts([match[1], match[3], match[5]]);
return validateDateParts(dateParts);
}
}
// src/state/dates/date-time-parsing.ts
var ISO_REG_EX2 = /^([0-9]{4})-([0-1][0-9])(?:-([0-3][0-9]))?(?:[T ]?([0-2][0-9])(?::([0-5][0-9]))?(?::([0-5][0-9]))?)?(?:\.([0-9]+))?(Z|(?:\+|-)[0-9]{4})?$/;
var SHORT_DATE_TIME_REG_EX = /^(\d{1,2})(\/|-|\.)(\d{1,2})(\/|-|\.)(\d{2,4}) (\d{1,2}):(\d{1,2})$/;
function parseDateTime(date) {
if (!date) {
return date;
}
let d = parseIso2(date);
if (d) {
return d.datetime;
}
d = parseShortDateTime(date);
if (d) {
return d.datetime;
}
d = localeInfo().formatters.reduce((prev, formatter) => prev || parseDateTimeFromFormatter(date, formatter), void 0);
if (d) {
return d.datetime;
}
return INVALID_DATE;
}
function parseIso2(date) {
let match = date.match(ISO_REG_EX2);
if (match) {
const year = match[1];
const month = match[2];
const day = match[3];
const hour = match[4];
const minute = match[5];
return validateDateTimeParts({ year, month, day, hour, minute, period: "am", timeFormat: "24h" });
}
}
function parseShortDateTime(date) {
let match = date.match(SHORT_DATE_TIME_REG_EX);
if (match) {
const dateParts = localeInfo().shortDateTimeMatchToParts([match[1], match[3], match[5], match[6], match[7]]);
return validateDateTimeParts(dateParts);
}
}
// src/state/dates/dates.ts
function getNowDate() {
return toLocalIsoDate(/* @__PURE__ */ new Date());
}
function getNowDateTime() {
return toLocalIsoDateTime(/* @__PURE__ */ new Date());
}
function getNowTime() {
return toLocalIsoTime(/* @__PURE__ */ new Date());
}
function toLocalIsoDate(dt) {
return `${dt.getFullYear()}-${pad2(dt.getMonth() + 1)}-${pad2(dt.getDate())}T00:00`;
}
function toLocalIsoDateTime(dt) {
return `${dt.getFullYear()}-${pad2(dt.getMonth() + 1)}-${pad2(dt.getDate())}T${pad2(dt.getHours())}:${pad2(dt.getMinutes())}`;
}
function toDateParts(input) {
const { year, month, day } = toDateTimeParts(input, "24h");
return { year, month, day };
}
function timeToTimeParts(hour, minute, timeFormat) {
let period = "am";
if (timeFormat === "12h") {
if (hour === 0) {
hour = 12;
} else if (hour === 12) {
period = "pm";
} else if (hour > 12) {
hour = hour - 12;
period = "pm";
}
}
return {
hour: timeFormat === "24h" ? pad2(hour) : `${hour}`,
minute: pad2(minute),
period,
timeFormat
};
}
function toDateTimeParts(input, timeFormat) {
if (!input) {
return {
year: "",
month: "",
day: "",
hour: "",
minute: "",
period: "am",
timeFormat
};
}
const dt = isDate(input) ? input : new Date(input);
if (isValidDate(dt)) {
return {
year: `${dt.getFullYear()}`,
month: `${dt.getMonth() + 1}`,
day: `${dt.getDate()}`,
...timeToTimeParts(dt.getHours(), dt.getMinutes(), timeFormat)
};
} else {
return {
year: "",
month: "",
day: "",
hour: "",
minute: "",
period: "am",
timeFormat
};
}
}
function toTimeParts(input, timeFormat) {
const dateParts = toDateTimeParts(input, timeFormat);
if (dateParts.hour && dateParts.minute) {
return {
hour: dateParts.hour,
minute: dateParts.minute,
period: dateParts.period,
timeFormat: dateParts.timeFormat
};
}
if (typeof input === "string") {
const parts = input.split(":");
if (parts.length === 2) {
const hourNum = parseInt(parts[0], 10);
const minuteNum = parseInt(parts[1], 10);
if (!Number.isNaN(hourNum) && !Number.isNaN(minuteNum)) {
return timeToTimeParts(hourNum, minuteNum, timeFormat);
}
}
}
return {
hour: "",
minute: "",
period: "am",
timeFormat
};
}
function toLocalIsoTime(dt) {
return `${pad2(dt.getHours())}:${pad2(dt.getMinutes())}`;
}
function pad2(n, length = 2) {
const padding = Array.from({ length }).map(() => "0").join("");
return `${padding}${n}`.slice(-1 * length);
}
// src/state/dates/index.ts
var DateTime = {
getNowDate,
getNowDateTime,
getNowTime,
localeInfo,
parseDate,
parseDateTime,
parseTime,
toDateParts,
toDateTimeParts,
toTimeParts,
toLocalIsoDateTime,
validateDateParts,
validateDateTimeParts,
validateTimeParts
};
var DateTimeSettings = {
dateFormats: {
dd_mm_yyyy: "dd-mm-yyyy",
mm_dd_yyyy: "mm-dd-yyyy",
yyyy_mm_dd: "yyyy-mm-dd"
},
timeFormats: {
h12: "12h",
h24: "24h"
},
defaultSeparators: {
date: "/",
time: ":"
},
timePeriods: {
am: "am",
pm: "pm"
}
};
// src/state/errors.ts
function handleError(error) {
console.log(error);
}
function getErrorMessages(errors) {
return !!errors ? Object.values(errors).map((e) => e.message) : null;
}
var Errors = {
handleError,
getErrorMessages
};
// src/state/localisations.ts
function format(s, ...args) {
if (args.length === 0 || !s) {
return "";
}
return s.replace(/{([^{}]*)}/g, (_a, b) => {
const bValue = parseInt(b, 10);
if (!Number.isNaN(bValue) && bValue < args.length) {
return args[bValue];
}
return "";
});
}
function plural(locale, value, fns) {
const pluralRules = new Intl.PluralRules(locale);
const rule = pluralRules.select(value);
const fn = fns[rule];
return fn();
}
function getPageTitle(defaultPageTitle, pageTitle, pageNo, pageCount, hasError, localizations) {
let result = defaultPageTitle;
if (pageCount > 1) {
const pageProgress = format(localizations.messages.page, pageNo, pageCount);
result = `${defaultPageTitle} - ${pageProgress}`;
if (pageTitle) {
result = `${result} - ${pageTitle}`;
}
}
if (hasError) {
result = `${localizations.error.pageTitle}: ${result}`;
}
return result;
}
// src/state/store.ts
function memo(projector) {
let lastArgs = null;
let lastResult = null;
return function(...args) {
if (lastArgs && lastArgs.length === args.length && lastArgs.every((a, index) => Object.is(a, args[index]))) {
return lastResult;
}
lastArgs = args;
lastResult = projector.apply(null, args);
return lastResult;
};
}
// src/state/validation.ts
var createFieldValidator = memo((field, language, localizations) => {
const dataTypeMessage = localizations.validation.dataType;
const dateFormatMessages = {
...localizations.validation.dataFormat,
reference: ""
};
const validators = [
["dataType", createDataTypeValidator(field.dataType), format(dataTypeMessage[field.dataType], field.name)],
["dataFormat", createDataFormatValidator(field.dataFormat), field.dataFormat ? format(dateFormatMessages[field.dataFormat], field.name) : ""],
[
"required",
createRequiredValidator(field.validations?.required),
field.validations?.required?.message || format(localizations.validation.required, field.name)
],
[
"allowedValue",
createAllowedValueValidator(field.validations?.allowedValue),
field.validations?.allowedValue?.message || format(localizations.validation.allowedValue, field.name)
],
[
"size",
createSizeValidator(field.validations?.min, field.validations?.max),
getRangeErrorMessage(
field.validations?.min,
field.validations?.max,
format(localizations.validation.min, field.name, field.validations?.min?.value),
format(localizations.validation.max, field.name, field.validations?.max?.value),
format(localizations.validation.minMax, field.name, field.validations?.min?.value, field.validations?.max?.value)
)
],
[
"length",
createLengthValidator(field.validations?.minLength, field.validations?.maxLength),
getRangeErrorMessage(
field.validations?.minLength,
field.validations?.maxLength,
format(localizations.validation.minLength, field.name, field.validations?.minLength?.value),
format(localizations.validation.maxLength, field.name, field.validations?.maxLength?.value),
format(localizations.validation.minMax, field.name, field.validations?.minLength?.value, field.validations?.maxLength?.value)
)
],
[
"count",
createCountValidator(field.validations?.minCount, field.validations?.maxCount),
getRangeErrorMessage(
field.validations?.minCount,
field.validations?.maxCount,
countMessage(language, field.validations?.minCount?.value, field, localizations.validation.minCount),
countMessage(language, field.validations?.maxCount?.value, field, localizations.validation.maxCount),
format(localizations.validation.minMaxCount, field.name, field.validations?.minCount?.value, field.validations?.maxCount?.value)
)
],
["regex", createRegExValidator(field.validations?.regex), field.validations?.regex?.message || format(localizations.validation.regEx, field.name)],
[
"allowedValues",
createAllowedValuesValidator(field.validations?.allowedValues),
field.validations?.allowedValues?.message || format(localizations.validation.allowedValues, field.name)
],
[
"pastDateTime",
createPastDateTimeValidator(field.dataType, field.validations?.pastDateTime),
field.validations?.pastDateTime?.message || format(localizations.validation.pastDateTime, field.name)
]
];
return function(value) {
return validators.reduce(
(prev, [key, validator, message]) => {
const error = validator(value);
if (error) {
prev = prev || {};
prev = {
...prev,
[key]: {
...error,
message
}
};
}
return prev;
},
null
);
};
});
function noopValidator() {
return null;
}
function isNull(value) {
return value == null;
}
function isEmpty(value) {
return isNull(value) || value.length === 0;
}
function hasLength(value) {
return typeof value === "string" || Array.isArray(value);
}
function fromValid(fn, getResult) {
return (value) => {
const isValid = fn(value);
return isValid ? null : getResult();
};
}
function createDataTypeValidator(dataType) {
return fromValid(
(value) => {
if (isNull(value)) {
return true;
}
switch (dataType) {
case "boolean": {
return typeof value === "boolean";
}
case "dateTime": {
if (typeof value !== "string") {
return false;
}
const d = new Date(value);
return !Number.isNaN(d.getTime());
}
case "decimal": {
return typeof value === "number";
}
case "integer": {
return typeof value === "number" && Number.isSafeInteger(value);
}
case "string": {
return typeof value === "string";
}
case "stringArray": {
return Array.isArray(value);
}
}
},
() => ({})
);
}
function createDataFormatValidator(dataFormat) {
return fromValid(
(value) => {
if (isEmpty(value) || !dataFormat) {
return true;
}
switch (dataFormat) {
case "email": {
const emailRegex = new RegExp(
'^(([^<>()\\[\\]\\.,;:\\s@\\"]+(\\.[^<>()\\[\\]\\.,;:\\s@\\"]+)*)|(\\".+\\"))@(([^<>()[\\]\\.,;:\\s@\\"]+\\.)+[^<>()[\\]\\.,;:\\s@\\"]{2,})$',
"i"
);
return typeof value === "string" && emailRegex.test(value);
}
case "phone": {
return true;
}
case "reference": {
return true;
}
case "time": {
const timeRegex = new RegExp("^([01]?[0-9]|2[0-3])([:. ])([0-5]\\d)(\\2[0-5]\\d)?$");
return typeof value === "string" && timeRegex.test(value);
}
case "url": {
const urlRegex = new RegExp(
"^(?:(?:(?:https?|ftp):)?\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff-]{0,62})?[a-z0-9\\u00a1-\\uffff]\\.)+(?:[a-z\\u00a1-\\uffff]{2,}\\.?))(?::\\d{2,5})?(?:[/?#]\\S*)?$"
);
return typeof value === "string" && urlRegex.test(value);
}
}
},
() => ({})
);
}
function createRequiredValidator(required) {
return !!required ? fromValid(
(value) => !isEmpty(value),
() => ({})
) : noopValidator;
}
function createSizeValidator(min, max) {
return !!min || !!max ? fromValid(
(value) => {
let valid = true;
if (typeof value === "number") {
if (min) {
valid = value >= min.value;
}
if (valid && max) {
valid = value <= max.value;
}
}
return valid;
},
() => ({ min: min?.value, max: max?.value })
) : noopValidator;
}
function createLengthValidator(minLength, maxLength) {
return !!minLength || !!maxLength ? fromValid(
(value) => {
let valid = true;
if (hasLength(value)) {
if (minLength) {
valid = value.length >= minLength.value;
}
if (valid && maxLength) {
valid = value.length <= maxLength.value;
}
}
return valid;
},
() => ({ minLength: minLength?.value, maxLength: maxLength?.value })
) : noopValidator;
}
function createCountValidator(minCount, maxCount) {
return !!minCount || !!maxCount ? fromValid(
(value) => {
let valid = true;
if (hasLength(value)) {
if (minCount) {
valid = value.length >= minCount.value;
}
if (valid && maxCount) {
valid = value.length <= maxCount.value;
}
}
return valid;
},
() => ({ minCount: minCount?.value, maxCount: maxCount?.value })
) : noopValidator;
}
function createRegExValidator(regex) {
if (!regex?.pattern) {
return noopValidator;
}
const pattern = regex.pattern;
const isValidPattern = pattern && pattern.length > 3 && pattern.startsWith("/");
const flagsIndex = pattern.lastIndexOf("/");
const patternWithoutFlags = isValidPattern ? pattern.substring(1, flagsIndex) : pattern;
const flags = !isValidPattern || flagsIndex === pattern.length - 1 || flagsIndex < 0 ? "" : pattern.substring(flagsIndex + 1);
return fromValid(
(value) => {
if (isEmpty(value)) {
return true;
}
const regex2 = new RegExp(patternWithoutFlags, flags);
return typeof value === "string" && regex2.test(value);
},
() => ({ pattern })
);
}
function createAllowedValueValidator(allowedValue) {
const allowed = allowedValue?.value;
if (isNull(allowed)) {
return noopValidator;
}
return fromValid(
(value) => {
return value === allowed;
},
() => ({ allowed })
);
}
function createAllowedValuesValidator(allowedValues) {
const allowed = allowedValues?.labeledValues ? allowedValues.labeledValues.map((value) => value.value) : allowedValues?.values;
if (!allowed?.length) {
return noopValidator;
}
return fromValid(
(value) => {
if (isEmpty(value)) {
return true;
}
return Array.isArray(value) ? value.every((v) => allowed.includes(v)) : allowed.includes(value);
},
() => ({ allowed })
);
}
function createPastDateTimeValidator(dataType, pastDateTime) {
return !!pastDateTime ? fromValid(
(value) => {
if (isNull(value)) {
return true;
}
if (dataType === "dateTime") {
const now = /* @__PURE__ */ new Date();
const dt = /* @__PURE__ */ new Date(`${value}`);
return dt.getTime() <= now.getTime();
}
return true;
},
() => ({})
) : noopValidator;
}
function validate(value, language, field, localizations) {
if (field?.editor?.properties?.hidden) {
return null;
}
const validator = createFieldValidator(field, language, localizations);
return validator(value);
}
function countMessage(language, value, field, localizations) {
value = value || 0;
return plural(language, value, {
zero: () => format(localizations.zero, field.name, value),
one: () => format(localizations.one, field.name, value),
two: () => format(localizations.two, field.name, value),
few: () => format(localizations.few, field.name, value),
many: () => format(localizations.many, field.name, value),
other: () => format(localizations.other, field.name, value)
});
}
function getRangeErrorMessage(min, max, defaultMinMessage, defaultMaxMessage, defaultRangeMessage) {
if (min?.message) {
return min.message;
}
if (max?.message) {
return max.message;
}
if (min && max) {
return defaultRangeMessage;
}
if (min) {
return defaultMinMessage;
}
if (max) {
return defaultMaxMessage;
}
return "";
}
var Validation = {
validate
};
// src/state/fields.ts
var DEFAULT_DATA_TYPE_EDITOR_TYPES = {
boolean: "checkbox",
dateTime: "date",
decimal: "decimal",
integer: "integer",
string: "text",
stringArray: "radio"
};
var DEFAULT_DATA_FORMAT_EDITOR_TYPES = {
email: "email",
phone: "tel",
reference: "reference",
time: "time",
url: "url"
};
var DEFAULT_EDITOR_ID_EDITOR_TYPES = {
date: "date",
dateparts: "dateparts",
datetime: "datetime",
datetimeparts: "datetimeparts",
decimal: "decimal",
integer: "integer",
list: "radio",
"list-dropdown": "select",
multiline: "multiline",
text: "text",
time: "time",
timeparts: "timeparts"
};
function getEditorType(field) {
let editorType = field.editor?.id ? DEFAULT_EDITOR_ID_EDITOR_TYPES[field.editor.id] : null;
if (!editorType && field.dataFormat) {
editorType = DEFAULT_DATA_FORMAT_EDITOR_TYPES[field.dataFormat];
}
if (!editorType && (field.validations?.allowedValues?.values || field.validations?.allowedValues?.labeledValues)) {
editorType = field.dataType === "stringArray" ? "multiselect" : "radio";
}
editorType = editorType || DEFAULT_DATA_TYPE_EDITOR_TYPES[field.dataType];
return editorType || "text";
}
var EMPTY_FIELD_VALUES = {
boolean: false,
dateTime: null,
decimal: null,
integer: null,
string: "",
stringArray: null
};
function getEmptyFieldValue(field) {
return EMPTY_FIELD_VALUES[field.dataType];
}
function getOptions(field, htmlId, localizations) {
const pairs = field?.validations?.allowedValues?.labeledValues ? field?.validations?.allowedValues?.labeledValues?.map((value) => value) : field?.validations?.allowedValues?.values?.map((value) => ({ value, label: value }));
let options = pairs?.filter((pair) => !!pair.value).map((pair, index) => {
return {
key: `${index}`,
htmlId: `${htmlId}-option-${index}`,
value: pair.value || "",
label: pair.label
};
});
if (getEditorType(field) === "select") {
options = [
{
key: "",
htmlId: `${htmlId}-option--1`,
value: "",
label: field?.editor?.properties?.placeholderText || localizations.labels.selectPlaceholder
},
...options || []
];
}
return options;
}
function getDefaultValue(field) {
const defaultValue = typeof field?.default !== "undefined" && field?.default !== null ? field.default : getEmptyFieldValue(field);
if (field.dataType === "dateTime" && defaultValue === "now()") {
const editorType = getEditorType(field);
return editorType === "datetime" || editorType === "datetimeparts" ? DateTime.getNowDateTime() : DateTime.getNowDate();
} else if (field.dataType === "string" && field.dataFormat === "time" && defaultValue === "now()") {
return DateTime.getNowTime();
}
return defaultValue;
}
function getInputValue(field, value) {
const editor = getEditorType(field);
if (field.dataType === "dateTime") {
if (editor === "datetime") {
return DateTime.localeInfo().toShortDateTimeString(value);
}
if (editor === "date") {
return DateTime.localeInfo().toShortDateString(value);
}
if (editor === "datetimeparts") {
return DateTime.toDateTimeParts(value, field.editor?.properties?.timeFormat || "24h");
}
if (editor === "dateparts") {
return DateTime.toDateParts(value);
}
}
if (field.dataType === "string" && field.dataFormat === "time") {
if (editor === "timeparts") {
return DateTime.toTimeParts(value, field.editor?.properties?.timeFormat || "24h");
}
}
return value;
}
function reduceFields(form, fn) {
return form?.fields ? form.fields.reduce((prev, field, index) => ({ ...prev, [field.id]: fn(field, index) }), {}) : {};
}
function validate2(field, value, language, localizations) {
return Validation.validate(value, language, field, localizations);
}
function isNullish(o) {
return o === null || typeof o === "undefined";
}
function getInitialValue(field, language, query, progressValue, localizations) {
let value = null;
if (typeof progressValue !== "undefined") {
const errors = validate2(field, progressValue, language, localizations);
if (!errors?.dataType && !errors?.allowedValues) {
value = progressValue;
}
}
if (isNullish(value)) {
let queryValue = null;
const firstQuery = query?.[0];
switch (field.dataType) {
case "boolean": {
if (firstQuery === "true" || firstQuery === "checked" || firstQuery === "on") {
queryValue = true;
} else if (firstQuery === "false" || firstQuery === "unchecked" || firstQuery === "off") {
queryValue = false;
}
break;
}
case "dateTime": {
if (firstQuery) {
if (field.dataType === "dateTime") {
return getEditorType(field) === "datetime" ? DateTime.parseDateTime(firstQuery) : DateTime.parseDate(firstQuery);
} else if (field.dataType === "string" && field.dataFormat === "time") {
return DateTime.parseTime(firstQuery);
}
}
break;
}
case "decimal": {
queryValue = !!firstQuery ? parseFloat(firstQuery) : null;
break;
}
case "integer": {
queryValue = !!firstQuery ? parseInt(firstQuery, 10) : null;
break;
}
case "string": {
queryValue = firstQuery;
break;
}
case "stringArray": {
if (query) {
if (field.validations?.allowedValues) {
const allowed = field.validations.allowedValues.labeledValues ? field.validations.allowedValues.labeledValues.map((value2) => value2.value) : field.validations.allowedValues.values;
if (allowed) {
queryValue = query.filter((item) => allowed.includes(item));
}
} else {
queryValue = query;
}
}
break;
}
}
if (queryValue !== null) {
const errors = validate2(field, queryValue, language, localizations);
if (!errors?.dataType && !errors?.allowedValues) {
value = queryValue;
}
}
}
if (isNullish(value)) {
value = getDefaultValue(field);
}
return value;
}
var Fields = {
getEditorType,
getOptions,
getInitialValue,
getInputValue,
reduceFields,
validate: validate2
};
// src/state/progress.ts
var AUTO_SAVE_PROGRESS_EXPIRY_DAYS = 30;
function getProgressExpiry() {
const d = /* @__PURE__ */ new Date();
d.setDate(d.getDate() + AUTO_SAVE_PROGRESS_EXPIRY_DAYS);
return DateTime.toLocalIsoDateTime(d);
}
function autoSave(form, value) {
if (form?.id && form?.properties?.autoSaveProgress) {
localStorage.setItem(`contensis-form-${form?.id}-expiry`, getProgressExpiry());
localStorage.setItem(`contensis-form-${form?.id}-value`, !!value ? JSON.stringify(value) : "");
}
}
function reset(form) {
if (form?.id) {
localStorage.removeItem(`contensis-form-${form?.id}-expiry`);
localStorage.removeItem(`contensis-form-${form?.id}-value`);
}
}
function load2(form) {
if (!!form) {
const expiry = localStorage.getItem(`contensis-form-${form.id}-expiry`);
const jsonValue = localStorage.getItem(`contensis-form-${form.id}-value`);
const d = DateTime.getNowDateTime();
if (expiry && jsonValue && d < expiry) {
try {
const value = JSON.parse(jsonValue);
return {
value
};
} catch {
}
}
}
return null;
}
function loadQuery() {
if (window?.location?.search) {
const params = new URLSearchParams(window.location.search);
return [...params.keys()].reduce(
(prev, key) => ({
...prev,
[key]: params.getAll(key)
}),
{}
);
}
return {};
}
var Progress = {
autoSave,
reset,
load: load2,
loadQuery
};
// src/state/form.ts
function getInputValue2(form, value) {
return Fields.reduceFields(form, (field) => Fields.getInputValue(field, value?.[field.id]));
}
function validate3(form, value, localizations) {
return Fields.reduceFields(form, (field) => Validation.validate(value?.[field.id], form.language, field, localizations));
}
function getPages(form) {
if (!form) {
return [];
}
if (form?.properties?.mode === "survey") {
return form.fields.map((field, index) => {
const { id, name, description } = field;
return {
pageNo: index + 1,
id,
title: name,
description,
fields: [field]
};
});
} else if (!form.groups) {
return [
{
pageNo: 1,
id: "page1",
title: "",
description: null,
fields: form.fields
}
];
} else {
return form.groups.map((group, index) => {
const { id, name, description } = group;
return {
pageNo: index + 1,
id,
title: name,
description,
fields: (form?.fields || []).filter((field) => field.groupId === id)
};
});
}
}
function getInitialValue2(form, localizations) {
const query = Progress.loadQuery();
const progress = Progress.load(form);
return Fields.reduceFields(form, (field) => Fields.getInitialValue(field, form.language, query?.[field.id], progress?.value?.[field.id], localizations));
}
function pageHasErrors(page, errors) {
return !!page && !!page.fields.some((field) => !!errors?.[field.id]);
}
var Form = {
getInputValue: getInputValue2,
getPages,
getInitialValue: getInitialValue2,
pageHasErrors,
validate: validate3
};
// src/state/rules.ts
function isConfirmationRuleReturnUri(r) {
return !!r?.link?.sys?.uri;
}
function isConfirmationRuleReturnContent(r) {
return !!r?.content;
}
var Rules = {
isConfirmationRuleReturnContent,
isConfirmationRuleReturnUri
};
// src/components/FormConfirmation.tsx
var import_react2 = __toESM(require("react"));
// src/components/FormRenderContext.tsx
var import_react = __toESM(require("react"));
var DEFAULT_LANGUAGE = "en-GB";
var DEFAULT_HEADING_LEVEL = 3;
var DEFAULT_LOCALIZATIONS = {
buttons: {
next: "Next",
// next
previous: "Previous",
// previous
submit: "Submit"
//submit
},
load: {
error: "Error loading form",
// na
loading: "Loading..."
// na
},
error: {
label: "Error",
// errorLabel
pageTitle: "Error",
// errorPageTitle
summaryTitle: "There is a problem"
// errorSummaryTitle
},
messages: {
confirmation: "Form submitted successfully",
// na
page: "Page {0} of {1}"
// pageHeader
},
labels: {
selectPlaceholder: "Please select",
// na
required: "required"
// requiredLabel
},
dates: {
day: "Day",
// dateInputDayLabel
month: "Month",
// dateInputMonthLabel
year: "Year",
// dateInputYearLabel
hour: "Hour",
// dateInputHourLabel
minute: "Minute",
// dateInputMinuteLabel
period: "Period"
// dateInputPeriodLabel
},
validation: {
dataType: {
boolean: "{0} is not a boolean",
// validationDataTypeBoolean
dateTime: "{0} is not a date",
// validationDataTypeDateTime
decimal: "{0} is not a number",
// validationDataTypeDecimal
integer: "{0} is not an integer",
// // validationDataTypeInteger
string: "{0} is not a string",
// // validationDataTypeString
stringArray: "{0} is not an array"
// validationDataTypeStringArray
},
dataFormat: {
email: "{0} is not a valid email",
//validationDataFormatEmail
phone: "{0} is not a valid telephone number",
// validationDataFormatPhone
time: "{0} is not a valid time",
// validationDataFormatTime
url: "{0} is not a valid URL"
// validationDataFormatUrl
},
minCount: {
zero: "{0} requires a minimum of {1} items",
// validationMinCountZero
one: "{0} requires a minimum of {1} item",
// validationMinCountOne
two: "{0} requires a minimum of {1} items",
// validationMinCountTwo
few: "{0} requires a minimum of {1} items",
// validationMinCountFew
many: "{0} requires a minimum of {1} items",
// validationMinCountMany
other: "{0} requires a minimum of {1} items"
// validationMinCountOther
},
maxCount: {
zero: "{0} requires a maximum of {1} items",
// validationMaxCountZero
one: "{0} requires a maximum of {1} item",
// validationMaxCountOne
two: "{0} requires a maximum of {1} items",
// validationMaxCountTwo
few: "{0} requires a maximum of {1} items",
// validationMaxCountFew
many: "{0} requires a maximum of {1} items",
// validationMaxCountMany
other: "{0} requires a maximum of {1} items"
// validationMaxCountOther
},
required: "{0} is required",
// validationRequired
allowedValue: "{0} is required",
// validationAllowedValue
allowedValues: "{0} has an invalid value",
// validationAllowedValues
regEx: "{0} has an invalid format",
// validationRegEx
pastDateTime: "{0} must be in the past",
// validationPastDateTime
min: "{0} must be minimum of {1}",
// validationMin
max: "{0} must be a maximum of {1}",
// validationMax
minMax: "{0} must be between {1} and {2}",
// validationMinMax
minLength: "{0} must have a minimum length of {1}",
// validationMinLength
maxLength: "{0} must have a maximum length of {1}",
// validationMaxLength
minMaxLength: "{0} must have a length between {1} and {2}",
// validationMinMaxLength
minMaxCount: "{0} requires between {1} and {2} items"
// validationMinMaxCount
},
characterCount: {
remaining: {
zero: "You have {0} characters remaining",
// characterCountRemainingZero
one: "You have {0} character remaining",
// characterCountRemainingOne
two: "You have {0} characters remaining",
// characterCountRemainingTwo
few: "You have {0} characters remaining",
// characterCountRemainingFew
many: "You have