Skip to content

Commit

Permalink
Signature Pad. Add ability to use background image (#6744)
Browse files Browse the repository at this point in the history
* work for #4403 Signature Pad. Add ability to use background image

* Describe backgroundImage and fix backgroundColor and penColor descriptions

* work for #4403 backgroundImage is priopity over backgroundColor

* work for #4403 : fix markup tests for vue3

* work for #4403 : fix style and add into general group

---------

Co-authored-by: OlgaLarina <[email protected]>
Co-authored-by: Roman Tsukanov <[email protected]>
  • Loading branch information
3 people authored Aug 24, 2023
1 parent 9ab333e commit b55c3c2
Show file tree
Hide file tree
Showing 24 changed files with 204 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<div
[class]="model.cssClasses.root"
[style.height]="model.signatureHeight + 'px'" [style.width]="model.signatureWidth + 'px'" #contentElement>
<div [class]="model.cssClasses.placeholder" [visible]="model.needShowPlaceholder()">
{{ model.placeHolderText }}
</div>
<div [class]="model.cssClasses.placeholder" [visible]="model.needShowPlaceholder()">{{ model.placeHolderText }}</div>
<div>
<canvas tabindex="0"></canvas>
<img *ngIf="!!model.backgroundImage" [src]="model.backgroundImage" [width]="model.signatureWidth" [height]="model.signatureHeight" [class]="model.cssClasses.backgroundImage">
<canvas tabindex="0" [class]="model.cssClasses.canvas"></canvas>
</div>
<div [class]="model.cssClasses.controls" [visible]="model.canShowClearButton">
<button
Expand Down
1 change: 1 addition & 0 deletions packages/survey-vue3-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"typescript": "~4.7.4",
"vite": "^3.1.8",
"vitest": "~0.32.4",
"vitest-canvas-mock": "^0.3.3",
"vue": "^3.2.41",
"vue-tsc": "^1.0.8"
}
Expand Down
13 changes: 9 additions & 4 deletions packages/survey-vue3-ui/src/Signaturepad.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@
<div
:class="question.cssClasses.placeholder"
v-show="question.needShowPlaceholder()"
>
{{ question.placeHolderText }}
</div>
>{{ question.placeHolderText }}</div>
<div>
<canvas tabindex="0"></canvas>
<img
v-if="question.backgroundImage"
:class="question.cssClasses.backgroundImage"
:src="question.backgroundImage"
:width="question.signatureWidth"
:height="question.signatureHeight"
/>
<canvas tabindex="0" :class="question.cssClasses.canvas"></canvas>
</div>
<div
:class="question.cssClasses.controls"
Expand Down
4 changes: 4 additions & 0 deletions packages/survey-vue3-ui/vitest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { fileURLToPath, URL } from "node:url";
export default defineConfig({
plugins: [vue()],
test:{
setupFiles: ['./vitest.setup.ts'],
environment: 'jsdom',
deps: {
inline: ['vitest-canvas-mock'],
},
},
resolve: {
alias: {
Expand Down
1 change: 1 addition & 0 deletions packages/survey-vue3-ui/vitest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "vitest-canvas-mock";
2 changes: 1 addition & 1 deletion src/default-styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ f-panel {
align-items: center;
justify-content: center;
position: absolute;
z-index: 0;
z-index: 1;
user-select: none;
pointer-events: none;
width: 100%;
Expand Down
2 changes: 2 additions & 0 deletions src/defaultCss/cssmodern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,8 @@ export var modernCss = {
small: "sv-row__question--small",
controls: "sjs_sp_controls",
placeholder: "sjs_sp_placeholder",
canvas: "sjs_sp_canvas",
backgroundImage: "sjs_sp__background-image",
clearButton: "sjs_sp_clear",
},
saveData: {
Expand Down
2 changes: 2 additions & 0 deletions src/defaultCss/cssstandard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@ export var defaultStandardCss = {
root: "sv_q_signaturepad sjs_sp_container",
controls: "sjs_sp_controls",
placeholder: "sjs_sp_placeholder",
canvas: "sjs_sp_canvas",
backgroundImage: "sjs_sp__background-image",
clearButton: "sjs_sp_clear",
},
saveData: {
Expand Down
2 changes: 2 additions & 0 deletions src/defaultCss/defaultV2Css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,8 @@ export var defaultV2Css = {
small: "sd-row__question--small",
controls: "sjs_sp_controls sd-signaturepad__controls",
placeholder: "sjs_sp_placeholder",
canvas: "sjs_sp_canvas sd-signaturepad__canvas",
backgroundImage: "sjs_sp__background-image sd-signaturepad__background-image",
clearButton: "sjs_sp_clear sd-context-btn sd-context-btn--negative sd-signaturepad__clear",
clearButtonIconId: "icon-clear"
},
Expand Down
10 changes: 9 additions & 1 deletion src/defaultV2-theme/blocks/sd-signaturepad.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,12 @@
.sjs_sp_placeholder {
background-color: $red-light;
}
}
}

.sd-signaturepad__canvas,
.sd-signaturepad__background-image {
position: absolute;
top: 0;
left: 0;
object-fit: cover;
}
5 changes: 4 additions & 1 deletion src/knockout/templates/question-signaturepad.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
data-bind="text: placeHolderText, css: question.cssClasses.placeholder, visible: $data.needShowPlaceholder()">
</div>
<div>
<canvas tabindex='0'></canvas>
<!-- ko if: question.backgroundImage -->
<img data-bind="attr: { src: question.backgroundImage, width: question.signatureWidth, height: question.signatureHeight }, css: question.cssClasses.backgroundImage">
<!-- /ko -->
<canvas tabindex='0' data-bind="css: question.cssClasses.canvas"></canvas>
</div>
<!-- ko if: question.canShowClearButton -->
<div data-bind="css: question.cssClasses.controls">
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/themes/bootstrap/cssbootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ export var defaultCss = {
root: "sv_q_signaturepad sjs_sp_container",
controls: "sjs_sp_controls",
placeholder: "sjs_sp_placeholder",
canvas: "sjs_sp_canvas",
backgroundImage: "sjs_sp__background-image",
clearButton: "sjs_sp_clear",
},
saveData: {
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/themes/bootstrapmaterial/cssbootstrapmaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ export var defaultCss = {
root: "sv_q_signaturepad sjs_sp_container",
controls: "sjs_sp_controls",
placeholder: "sjs_sp_placeholder",
canvas: "sjs_sp_canvas",
backgroundImage: "sjs_sp__background-image",
clearButton: "sjs_sp_clear",
},
saveData: {
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/themes/common-theme-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ export function setStyles(): void {
".sv_main .sjs_sp_controls > button": "user-select: none;",
".sv_main .sjs_sp_container>div>canvas:focus": "outline: none;",
".sv_main .sjs_sp_placeholder": "display: flex; align-items: center; justify-content: center; position: absolute; z-index: 0; user-select: none; pointer-events: none; width: 100%; height: 100%;",
".sv_main .sjs_sp_canvas": "position: absolute; top: 0; left: 0;",
".sv_main .sjs_sp__background-image": "position: absolute; top: 0; left: 0;",

// logo
// ".sv_main .sv_header": "white-space: nowrap;",
Expand Down
36 changes: 33 additions & 3 deletions src/question_signaturepad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ export class QuestionSignaturePadModel extends Question {

const backgroundColorProperty = this.getPropertyByName("backgroundColor");
const backgroundColorFromTheme = penColorFromTheme ? "transparent" : undefined;
signaturePad.backgroundColor = this.backgroundColor || backgroundColorFromTheme || backgroundColorProperty.defaultValue || "#ffffff";
const background = !!this.backgroundImage ? "transparent" : this.backgroundColor;
signaturePad.backgroundColor = background || backgroundColorFromTheme || backgroundColorProperty.defaultValue || "#ffffff";
}

protected getCssRoot(cssClasses: any): string {
Expand Down Expand Up @@ -217,24 +218,49 @@ export class QuestionSignaturePadModel extends Question {
return !this.isInputReadOnly && this.allowClear;
}
/**
* Specifies a color for the pen. Accepts hexadecimal colors (`"#FF0000"`), RGB colors (`"rgb(255,0,0)"`), or color names (`"red"`).
* Specifies a color for the pen.
*
* This property accepts color values in the following formats:
*
* - Hexadecimal colors (`"#FF0000"`)
* - RGB colors (`"rgb(255,0,0)"`)
* - Color names (`"red"`)
* @see backgroundColor
*/
public get penColor(): string {
return this.getPropertyValue("penColor");
}
public set penColor(val: string) {
this.setPropertyValue("penColor", val);
!!this.signaturePad && this.updateColors(this.signaturePad);
}
/**
* Specifies a color for the signature area background. Accepts hexadecimal colors (`"#FF0000"`), RGB colors (`"rgb(255,0,0)"`), or color names (`"red"`).
* Specifies a color for the signature area background. Ignored if [`backgroundImage`](#backgroundImage) is set.
*
* This property accepts color values in the following formats:
*
* - Hexadecimal colors (`"#FF0000"`)
* - RGB colors (`"rgb(255,0,0)"`)
* - Color names (`"red"`)
* @see penColor
*/
public get backgroundColor(): string {
return this.getPropertyValue("backgroundColor");
}
public set backgroundColor(val: string) {
this.setPropertyValue("backgroundColor", val);
!!this.signaturePad && this.updateColors(this.signaturePad);
}
/**
* An image to display in the background of the signature area. Accepts a base64 or URL string value.
* @see backgroundColor
*/
public get backgroundImage(): string {
return this.getPropertyValue("backgroundImage");
}
public set backgroundImage(val: string) {
this.setPropertyValue("backgroundImage", val);
!!this.signaturePad && this.updateColors(this.signaturePad);
}
get clearButtonCaption(): string {
return this.getLocalizationString("clearCaption");
Expand Down Expand Up @@ -294,6 +320,10 @@ Serializer.addClass(
category: "general",
default: true,
},
{
name: "backgroundImage",
category: "general",
},
{
name: "penColor:color",
category: "general",
Expand Down
9 changes: 8 additions & 1 deletion src/react/signaturepad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,20 @@ export class SurveyQuestionSignaturePad extends SurveyQuestionElementBase {
{this.question.placeHolderText}
</div>
<div>
<canvas tabIndex={0}></canvas>
{this.renderBackgroundImage()}
<canvas tabIndex={0} className={this.question.cssClasses.canvas}></canvas>
</div>
{clearButton}
</div>
);
}

renderBackgroundImage(): JSX.Element | null {
if(!this.question.backgroundImage) return null;

return <img className={this.question.cssClasses.backgroundImage} src={this.question.backgroundImage} width={this.question.signatureWidth} height= {this.question.signatureHeight}></img>;
}

renderCleanButton(): JSX.Element | null {
if(!this.question.canShowClearButton) return null;

Expand Down
10 changes: 9 additions & 1 deletion src/signaturepad.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@
justify-content: center;

position: absolute;
z-index: 0;
z-index: 1;
user-select: none;
pointer-events: none;
width: 100%;
height: 100%;
}

.sjs_sp_canvas,
.sjs_sp__background-image {
position: absolute;
top: 0;
left: 0;
object-fit: cover;
}
8 changes: 4 additions & 4 deletions src/vue/signaturepad.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
width: question.signatureWidth + 'px',
}"
>
<div :class="question.cssClasses.placeholder" v-show="question.needShowPlaceholder()">
{{ question.placeHolderText }}
</div>
<div :class="question.cssClasses.placeholder" v-show="question.needShowPlaceholder()">{{ question.placeHolderText }}</div>
<div>
<canvas tabindex="0"></canvas>
<img v-if="question.backgroundImage" :class="question.cssClasses.backgroundImage" :src="question.backgroundImage"
:width="question.signatureWidth" :height="question.signatureHeight">
<canvas tabindex="0" :class="question.cssClasses.canvas"></canvas>
</div>
<div :class="question.cssClasses.controls" v-if="question.canShowClearButton">
<button
Expand Down
1 change: 1 addition & 0 deletions tests/markup/etalon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from "./etalon_matrixdropdown";
export * from "./etalon_matrixdynamic";
export * from "./etalon_question";
export * from "./etalon_survey";
export * from "./etalon_signaturepad";
export { markupTests } from "./helper";

registerMarkupTests([
Expand Down
33 changes: 33 additions & 0 deletions tests/markup/etalon_signaturepad.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { registerMarkupTests } from "./helper";

registerMarkupTests(
[
{
name: "Test Signaturepad question markup",
json: {
questions: [
{
type: "signaturepad",
name: "q1",
titleLocation: "hidden",
}
]
},
snapshot: "signaturepad"
},
{
name: "Test Signaturepad question with backgroundImage",
json: {
questions: [
{
"type": "signaturepad",
"name": "q1",
titleLocation: "hidden",
"backgroundImage": "https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg"
}
]
},
snapshot: "signaturepad-with-backgroundImage"
}
]
);
6 changes: 6 additions & 0 deletions tests/markup/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,12 @@ function sortInlineStyles(str: string) {
div.querySelectorAll("*").forEach(el => {
if(!!el.getAttribute("style")) {
const inlineStyle = (<string>el.getAttribute("style")).replace(/(;)\s+|;$/g, "$1").split(/;(?![^(]*\))/);
if(el.tagName === "CANVAS") {
const excludeStyle = "touch-action: none";
if(inlineStyle.indexOf(excludeStyle) !== -1) {
inlineStyle.splice(inlineStyle.indexOf(excludeStyle), 1);
}
}
const flexRules = ["flex-grow", "flex-shrink", "flex-basis"];
const flexStyles: Array<string> = [];
flexRules.forEach(rule => {
Expand Down
13 changes: 13 additions & 0 deletions tests/markup/snapshots/signaturepad-with-backgroundImage.snap.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div class="sjs_sp_container sv_q_signaturepad" style="height:200px; width:300px;">
<div class="sjs_sp_placeholder">Sign here</div>
<div>
<img class="sjs_sp__background-image" height="200" src="https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg" width="300">
<canvas class="sjs_sp_canvas" height="200" style="height:200px; user-select:none; width:300px;" tabindex="0" width="300">
</canvas>
</div>
<div class="sjs_sp_controls">
<button class="sjs_sp_clear" title="Clear" type="button">
<span></span>
</button>
</div>
</div>
12 changes: 12 additions & 0 deletions tests/markup/snapshots/signaturepad.snap.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="sjs_sp_container sv_q_signaturepad" style="height:200px; width:300px;">
<div class="sjs_sp_placeholder">Sign here</div>
<div>
<canvas class="sjs_sp_canvas" height="200" style="height:200px; user-select:none; width:300px;" tabindex="0" width="300">
</canvas>
</div>
<div class="sjs_sp_controls">
<button class="sjs_sp_clear" title="Clear" type="button">
<span></span>
</button>
</div>
</div>
Loading

0 comments on commit b55c3c2

Please sign in to comment.