diff --git a/apps/OpenSign/public/locales/de/translation.json b/apps/OpenSign/public/locales/de/translation.json index 32b2b682b..f3c95c4c8 100644 --- a/apps/OpenSign/public/locales/de/translation.json +++ b/apps/OpenSign/public/locales/de/translation.json @@ -361,6 +361,7 @@ "date": "Datum", "text": "Text", "text input": "Texteingabe", + "cells": "Zellen", "checkbox": "Checkbox", "dropdown": "Dropdown", "radio button": "Radiobutton", @@ -415,10 +416,12 @@ "options": "Optionen", "minimun-check": "Minimale Anzahl", "maximum-check": "Maximale Anzahl", + "cell-count": "Zellzahl", "default-value": "Standardwert", "select": "Auswählen", - "read-only": "Nur lesen", + "read-only": "Ist schreibgeschützt", "hide-labels": "Labels ausblenden", + "layout": "Layout", "checkbox": "Checkbox", "alert": "Warnung", "zoom-in": "Vergrößern", @@ -1025,5 +1028,6 @@ "not-found-in-signature": "Nicht in Signatur gefunden", "readonly-textinput-error": "Schreibgeschütztes Text-Widget muss einen Standardwert haben oder optional sein.", "readonly-dropdown-error": "Schreibgeschütztes Dropdown-Widget muss einen Standardwert haben oder optional sein.", - "readonly-radiobtn-error": "Schreibgeschütztes Optionsfeld-Widget muss einen Standardwert haben oder optional sein." + "readonly-radiobtn-error": "Schreibgeschütztes Optionsfeld-Widget muss einen Standardwert haben oder optional sein.", + "choose-one":"Wählen Sie eine aus" } diff --git a/apps/OpenSign/public/locales/en/translation.json b/apps/OpenSign/public/locales/en/translation.json index 39d377405..da3832002 100644 --- a/apps/OpenSign/public/locales/en/translation.json +++ b/apps/OpenSign/public/locales/en/translation.json @@ -361,6 +361,7 @@ "date": "date", "text": "text", "text input": "text input", + "cells": "cells", "checkbox": "checkbox", "dropdown": "dropdown", "radio button": "radio button", @@ -415,10 +416,12 @@ "options": "Options", "minimun-check": "Minimun check", "maximum-check": "Maximum check", + "cell-count": "Cell count", "default-value": "Default value", "select": "Select", - "read-only": "Is read only", + "read-only": "read only", "hide-labels": "Hide labels", + "layout": "Layout", "checkbox": "Checkbox", "alert": "Alert", "zoom-in": "Zoom in", @@ -1025,5 +1028,6 @@ "not-found-in-signature": "Not found in signature", "readonly-textinput-error": "Read-only text widget must have a default value or you can make it optional.", "readonly-dropdown-error": "Read-only dropdown widget must have a default value or you can make it optional.", - "readonly-radiobtn-error": "Read-only radio button widget must have a default value or you can make it optional." + "readonly-radiobtn-error": "Read-only radio button widget must have a default value or you can make it optional.", + "choose-one":"Choose One" } \ No newline at end of file diff --git a/apps/OpenSign/public/locales/es/translation.json b/apps/OpenSign/public/locales/es/translation.json index 551e3104d..aea3b6f8c 100644 --- a/apps/OpenSign/public/locales/es/translation.json +++ b/apps/OpenSign/public/locales/es/translation.json @@ -362,6 +362,7 @@ "date": "fecha", "text": "texto", "text input": "entrada de texto", + "cells": "células", "checkbox": "casilla", "dropdown": "desplegable", "radio button": "botón de radio", @@ -416,10 +417,12 @@ "options": "Opciones", "minimun-check": "Chequeo mínimo", "maximum-check": "Chequeo máximo", + "cell-count": "recuento de células", "default-value": "Valor por defecto", "select": "Seleccionar", "read-only": "Es de solo lectura", "hide-labels": "Esconder etiquetas", + "layout": "Diseño", "checkbox": "Casilla", "alert": "Alerta", "zoom-in": "Acercar", @@ -1025,5 +1028,6 @@ "not-found-in-signature": "No encontrado en la firma", "readonly-textinput-error": "El widget de texto de solo lectura debe tener un valor predeterminado o puede hacerse opcional.", "readonly-dropdown-error": "El widget desplegable de solo lectura debe tener un valor predeterminado o puede hacerse opcional.", - "readonly-radiobtn-error": "El widget de botón de opción de solo lectura debe tener un valor predeterminado o puede hacerse opcional." + "readonly-radiobtn-error": "El widget de botón de opción de solo lectura debe tener un valor predeterminado o puede hacerse opcional.", + "choose-one":"Elige uno" } diff --git a/apps/OpenSign/public/locales/fr/translation.json b/apps/OpenSign/public/locales/fr/translation.json index b2d558179..ef15a3cde 100644 --- a/apps/OpenSign/public/locales/fr/translation.json +++ b/apps/OpenSign/public/locales/fr/translation.json @@ -361,6 +361,7 @@ "date": "date", "text": "texte", "text input": "saisie de texte", + "cells": "cellules", "checkbox": "case à cocher", "dropdown": "dérouler", "radio button": "bouton radio", @@ -415,10 +416,12 @@ "options": "Possibilités", "minimun-check": "Vérification minimale", "maximum-check": "Contrôle maximum", + "cell-count": "numération cellulaire", "default-value": "Valeur par défaut", "select": "Sélectionner", "read-only": "Est en lecture seule", "hide-labels": "Masquer les étiquettes", + "layout": "Disposition", "checkbox": "Case à cocher", "alert": "Alerte", "zoom-in": "Agrandir", @@ -1025,5 +1028,6 @@ "not-found-in-signature": "Introuvable dans la signature", "readonly-textinput-error": "Le widget de texte en lecture seule doit avoir une valeur par défaut ou être rendu optionnel.", "readonly-dropdown-error": "Le widget déroulant en lecture seule doit avoir une valeur par défaut ou être rendu optionnel.", - "readonly-radiobtn-error": "Le widget bouton radio en lecture seule doit avoir une valeur par défaut ou être rendu optionnel." + "readonly-radiobtn-error": "Le widget bouton radio en lecture seule doit avoir une valeur par défaut ou être rendu optionnel.", + "choose-one":"Choisissez-en un" } diff --git a/apps/OpenSign/public/locales/hi/translation.json b/apps/OpenSign/public/locales/hi/translation.json index aefb718d3..5415755a5 100644 --- a/apps/OpenSign/public/locales/hi/translation.json +++ b/apps/OpenSign/public/locales/hi/translation.json @@ -361,6 +361,7 @@ "date": "दिनांक", "text": "पाठ", "text input": "पाठ इनपुट", + "cells": "सेल्स", "checkbox": "चेकबॉक्स", "dropdown": "ड्रॉपडाउन", "radio button": "रेडियो बटन", @@ -415,10 +416,12 @@ "options": "विकल्प", "minimun-check": "न्यूनतम जाँच", "maximum-check": "अधिकतम जाँच", + "cell-count": "सेल्स संख्या", "default-value": "डिफ़ॉल्ट मान", "select": "चुनें", - "read-only": "केवल पढ़ने के लिए है", + "read-only": "सिर्फ पढ़ने के लिए है", "hide-labels": "लेबल छिपाएँ", + "layout": "लेआउट", "checkbox": "चेकबॉक्स", "alert": "चेतावनी", "zoom-in": "ज़ूम इन करें", @@ -1025,5 +1028,6 @@ "not-found-in-signature": "हस्ताक्षर में नहीं मिला", "readonly-textinput-error": "रीड-ओनली टेक्स्ट विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।", "readonly-dropdown-error": "रीड-ओनली ड्रॉपडाउन विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।", - "readonly-radiobtn-error": "रीड-ओनली रेडियो बटन विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।" + "readonly-radiobtn-error": "रीड-ओनली रेडियो बटन विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।", + "choose-one":"एक का चयन" } diff --git a/apps/OpenSign/public/locales/it/translation.json b/apps/OpenSign/public/locales/it/translation.json index b164e80fe..5d7713f67 100644 --- a/apps/OpenSign/public/locales/it/translation.json +++ b/apps/OpenSign/public/locales/it/translation.json @@ -361,6 +361,7 @@ "date": "data", "text": "testo", "text input": "campo di testo", + "cells": "cellule", "checkbox": "casella di controllo", "dropdown": "menu a tendina", "radio button": "pulsante di opzione", @@ -415,10 +416,12 @@ "options": "Opzioni", "minimun-check": "Controllo minimo", "maximum-check": "Controllo massimo", + "cell-count": "conteggio delle cellule", "default-value": "Valore predefinita", "select": "Seleziona", - "read-only": "È solo lettura", + "read-only": "È di sola lettura", "hide-labels": "Nascondi etichette", + "layout": "Layout", "checkbox": "Casella di controllo", "alert": "Avviso", "zoom-in": "Ingrandisci", @@ -1025,5 +1028,6 @@ "not-found-in-signature": "Non trovato nella firma", "readonly-textinput-error": "Il widget di testo di sola lettura deve avere un valore predefinito oppure può essere reso opzionale.", "readonly-dropdown-error": "Il widget a discesa di sola lettura deve avere un valore predefinito oppure può essere reso opzionale.", - "readonly-radiobtn-error": "Il widget pulsante di opzione di sola lettura deve avere un valore predefinito oppure può essere reso opzionale." + "readonly-radiobtn-error": "Il widget pulsante di opzione di sola lettura deve avere un valore predefinito oppure può essere reso opzionale.", + "choose-one":"Scegline uno" } diff --git a/apps/OpenSign/src/components/pdf/CellsWidget.jsx b/apps/OpenSign/src/components/pdf/CellsWidget.jsx new file mode 100644 index 000000000..d00b4a393 --- /dev/null +++ b/apps/OpenSign/src/components/pdf/CellsWidget.jsx @@ -0,0 +1,123 @@ +import { useState, useEffect, useRef } from "react"; + +const Cell = ({ + count, + h, + value, + editable, + onChange, + onKeyDown, + inputRef, + index +}) => ( +
+ onChange && onChange(e, index) : undefined} + onKeyDown={editable ? (e) => onKeyDown && onKeyDown(e, index) : undefined} + className="w-full text-center uppercase focus:outline-none bg-transparent text-[12px]" + style={{ fontFamily: "Arial, sans-serif" }} + /> +
+); + +export default function CellsWidget({ + count = 8, + height = 40, + value = "", + editable = false, + onChange, + onKeyDown, + onCellCountChange, + inputRefs, + resizable = false +}) { + const [cellCount, setCellCount] = useState(count); + + // keep internal state in sync with prop updates + useEffect(() => setCellCount(count), [count]); + + const startX = useRef(0); + const startCount = useRef(cellCount); + + const capture = (downEv, onMove, onUp = () => {}) => { + const id = downEv.pointerId; + const move = (ev) => id === ev.pointerId && onMove(ev); + const up = (ev) => { + if (id !== ev.pointerId) return; + onUp(ev); + downEv.target.releasePointerCapture(id); + window.removeEventListener("pointermove", move); + window.removeEventListener("pointerup", up); + }; + window.addEventListener("pointermove", move); + window.addEventListener("pointerup", up); + downEv.target.setPointerCapture(id); + }; + + const onTopHandlePointerDown = (ev) => { + // Prevent triggering the widget drag logic + ev.stopPropagation(); + ev.preventDefault(); + startX.current = ev.clientX; + startCount.current = cellCount; + capture(ev, (moveEv) => { + const dx = moveEv.clientX - startX.current; + const delta = Math.floor(-dx / 5); + let newCount = Math.max(1, startCount.current + delta); + if (newCount !== cellCount) { + setCellCount(newCount); + onCellCountChange?.(newCount); + } + }); + }; + + const cells = Array.from({ length: cellCount }).map((_, i) => value[i] || ""); + + return ( +
+ {resizable && ( +
+ +
+ )} + {cells.map((val, i) => ( + (inputRefs.current[i] = el) : undefined} + index={i} + /> + ))} +
+ ); +} diff --git a/apps/OpenSign/src/components/pdf/DropdownWidgetOption.jsx b/apps/OpenSign/src/components/pdf/DropdownWidgetOption.jsx index 7a7800f30..e6fefc339 100644 --- a/apps/OpenSign/src/components/pdf/DropdownWidgetOption.jsx +++ b/apps/OpenSign/src/components/pdf/DropdownWidgetOption.jsx @@ -7,8 +7,8 @@ import { fontColorArr, fontsizeArr } from "../../constant/Utils"; function DropdownWidgetOption(props) { const { t } = useTranslation(); const [dropdownOptionList, setDropdownOptionList] = useState([ - "option-1", - "option-2" + "Option-1", + "Option-2" ]); const [minCount, setMinCount] = useState(0); const [maxCount, setMaxCount] = useState(0); @@ -17,11 +17,13 @@ function DropdownWidgetOption(props) { const [isHideLabel, setIsHideLabel] = useState(false); const [status, setStatus] = useState("required"); const [defaultValue, setDefaultValue] = useState(""); - const statusArr = ["required", "optional"]; const [defaultCheckbox, setDefaultCheckbox] = useState([]); + const [layout, setLayout] = useState("vertical"); + const statusArr = ["required", "optional"]; + const layoutArr = ["vertical", "horizontal"]; const resetState = () => { - setDropdownOptionList(["option-1", "option-2"]); + setDropdownOptionList(["Option-1", "Option-2"]); setDropdownName(props.currWidgetsDetails?.options?.name || props.type); setIsReadOnly(false); setIsHideLabel(false); @@ -29,6 +31,7 @@ function DropdownWidgetOption(props) { setMaxCount(0); setDefaultCheckbox([]); setDefaultValue(""); + setLayout("vertical"); }; useEffect(() => { if ( @@ -48,6 +51,7 @@ function DropdownWidgetOption(props) { setStatus(props.currWidgetsDetails?.options?.status || "required"); setDefaultValue(props.currWidgetsDetails?.options?.defaultValue || ""); setDefaultCheckbox(props.currWidgetsDetails?.options?.defaultValue || []); + setLayout(props.currWidgetsDetails?.options?.layout || "vertical"); } else { setStatus("required"); resetState(); @@ -107,6 +111,9 @@ function DropdownWidgetOption(props) { props?.type === "dropdown" || props?.type === radioButtonWidget; const readOnlyWithoutValue = isReadOnly && !defaultValue && status !== "optional"; + const WidgetLayout = ["checkbox", radioButtonWidget].includes(props.type) + ? layout + : null; // If it’s a dropdown and it’s read-only without a value (nor marked optional), stop here. if (isDropdownOrRadio && readOnlyWithoutValue) { @@ -129,7 +136,8 @@ function DropdownWidgetOption(props) { null, status, defaultData, - isHideLabel + isHideLabel, + WidgetLayout ); resetState(); }; @@ -153,17 +161,18 @@ function DropdownWidgetOption(props) { }} >
-