Last active
August 10, 2020 17:00
-
-
Save minhhungit/4915e4d6cf96c9868a7648f7d2f7cacd to your computer and use it in GitHub Desktop.
bootstrap-popover-with-serenity-form
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.popover.i-am-a-class.Date { | |
width: 600px; | |
max-width: unset; | |
color:red; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Modules/Common/Helpers/J.HelpPopover.ts */ | |
//=================================================== | |
// Copyright @ 2020 | |
// Author : Hung Vo ([email protected]) | |
// Time : 2020, August 06 | |
// Description : HelpPopover | |
//=================================================== | |
namespace J { | |
export class HelpPopover { | |
public buttons: HelpPopoverItem[] = []; | |
constructor( | |
public form: Serenity.PrefixedContext, | |
public propertyItems: Serenity.PropertyItem[], | |
public klassName?: string, | |
public bootstrapPopoverOption?: any) { | |
this.klassName = Q.coalesce(klassName, ""); | |
} | |
public init() { | |
if (this.form && this.propertyItems) { | |
this.propertyItems.forEach((editor, idx) => { | |
let popoverEditorOptionKey = "j_custom_help_popover"; | |
if (editor.editorParams && editor.editorParams[popoverEditorOptionKey]) { | |
let helpData = JSON.parse(editor.editorParams[popoverEditorOptionKey]); | |
let button = this.createHelpPopoverButton(this.form[editor.name], helpData, this.bootstrapPopoverOption); | |
if (button) { | |
this.buttons.push({ | |
editor: this.form[editor.name], | |
helpButton: button | |
}); | |
} | |
} | |
}); | |
} | |
} | |
public createHelpPopoverButton(editorField: Serenity.Widget<any> | string, opt: HelpPopoverOption, bootstrapPopoverOption?: any): JQuery { | |
//console.log(this.form); | |
let editor: any; | |
if (typeof editorField == "string") { | |
editor = this.form[editorField]; | |
} | |
else { | |
editor = editorField; | |
} | |
if (!editor) { | |
return; | |
} | |
if (editor && editor.element) { | |
let fieldName = editor.element.attr("id").split("_").pop(); | |
let trigger: string = Q.coalesce(opt?.trigger, "hover"); | |
let position: "caption" | "editor" = Q.coalesce(opt?.position, "editor"); | |
let html: boolean = Q.coalesce(opt?.html, false); | |
bootstrapPopoverOption = Q.extend({ delay: { "show": 10, "hide": 100 } }, bootstrapPopoverOption || {}); | |
//let enumEditor: Serenity.EnumEditor = (Q as any).safeCast(editor, Serenity.EnumEditor); | |
//if (enumEditor) { | |
// console.log(enumEditor.items); | |
//} | |
//else { | |
// let lookup: Serenity.LookupEditor = (Q as any).safeCast(editor, Serenity.LookupEditor); | |
// if (lookup) { | |
// //console.log(lookup.items); | |
// } | |
//} | |
let helpButton: JQuery; | |
let uniqueId: string = J.createGuid(); | |
let helpKlass = "j-custom-inplace-help-button"; | |
let helpPopoverKlass = "j-custom-help-popover"; | |
if (position == "caption") { | |
helpButton = $(`<a tabindex="0" role="button" class="${helpKlass}" data-toggle="popover" style="padding-left: 5px"><i class="fa fa-question-circle" style="color: #3c8dbc;"></i></a>`); | |
} | |
else { | |
helpButton = $(`<a tabindex="0" role="button" class="inplace-button ${helpKlass}" data-toggle="popover"><b><i class="fa fa-question-circle" style="color: #3c8dbc"></i></b></a>`); | |
} | |
helpButton.attr("j-popover-id", uniqueId); | |
helpButton.attr("j-popover-field-name", fieldName); | |
helpButton.addClass(this.klassName); | |
// https://bootstrapdocs.com/v3.3.6/docs/javascript/#popovers | |
if (opt?.content) { | |
helpButton.attr('data-content', opt?.content); | |
} | |
if (opt?.title) { | |
helpButton.attr('title', `${Q.text(opt?.title)}`); | |
} | |
helpButton.attr('data-placement', Q.coalesce(opt?.placement, "auto")) | |
.attr("data-trigger", trigger) | |
.attr('data-html', `${html}`) | |
.attr('data-container', "body") | |
.click(e => { | |
let target = $(e.target); | |
if (!target.hasClass(helpKlass)) { | |
target = target.closest(`.${helpKlass}`); | |
} | |
//if (target.parent().hasClass("inplace-button")) { | |
// target = target.parent(); | |
//} | |
//else { | |
// if (target.parent().parent().hasClass('inplace-button')) { | |
// target = target.parent().parent(); | |
// } | |
//} | |
(target as any).popover(bootstrapPopoverOption) | |
.data('bs.popover') | |
.tip() | |
.addClass(helpPopoverKlass) | |
.addClass(`${this.klassName}`) | |
.addClass(`j-popover-id-${uniqueId}`) | |
.addClass(`${fieldName}`); | |
(target as any).popover("toggle"); | |
}); | |
//registryEvents && registryEvents(helpButton); | |
//helpButton.on('hidden.bs.popover', function () { | |
// Q.notifyInfo("hidden.bs.popover"); | |
//}); | |
//helpButton.on('show.bs.popover', function () { | |
// Q.notifyInfo("show.bs.popover"); | |
//}); | |
if (position == "caption") { | |
helpButton.appendTo(editor.getGridField().find(".caption")); | |
} | |
else { | |
helpButton.insertBefore(editor.getGridField().find(".vx")); | |
} | |
(helpButton as any).popover(bootstrapPopoverOption) | |
.data('bs.popover') | |
.tip() | |
.addClass(helpPopoverKlass) | |
.addClass(`${this.klassName}`) | |
.addClass(`j-popover-id-${uniqueId}`) | |
.addClass(`${fieldName}`); | |
(helpButton as any).popover(); | |
return helpButton; | |
} | |
return null; | |
} | |
} | |
export function createGuid() { | |
function s4() { | |
return Math.floor((1 + Math.random()) * 0x10000) | |
.toString(16) | |
.substring(1); | |
} | |
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); | |
} | |
export class HelpPopoverItem { | |
editor: Serenity.Widget<any>; | |
helpButton: JQuery; | |
} | |
export class HelpPopoverOption { | |
content: string; | |
title?: string; | |
trigger?: string // "click" | "hover" | "focus" | "manual"; | |
placement?: "top" | "bottom" | "left" | "right" | "auto"; | |
position?: "caption" | "editor"; | |
html?: boolean; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Modules/Common/Helpers/J.HelpPopoverAttribute.cs */ | |
//=================================================== | |
// Copyright @ 2020 | |
// Author : Hung Vo ([email protected]) | |
// Time : 2020, August 06 | |
// Description : HelpPopover | |
//=================================================== | |
using Newtonsoft.Json; | |
using Serenity.ComponentModel; | |
namespace [YOUR_NAMESPACE] | |
{ | |
public partial class HelpPopoverAttribute : EditorOptionAttribute | |
{ | |
private const string optionKey = "j_custom_help_popover"; | |
public HelpPopoverAttribute(string content) | |
: base(optionKey, BuildHelpData(content)) | |
{ | |
} | |
public HelpPopoverAttribute(string content, string title) | |
: base(optionKey, BuildHelpData(content, title)) | |
{ | |
} | |
public HelpPopoverAttribute(string content = null, string title = null, string trigger = null, string placement = null, string position = null, bool html = false) | |
: base(optionKey, BuildHelpData(content: content, title: title, trigger: trigger, placement: placement, position: position, html: html)) | |
{ | |
} | |
static string BuildHelpData(string content) | |
{ | |
return JsonConvert.SerializeObject(new { content }); | |
} | |
static string BuildHelpData(string content, string title) | |
{ | |
return JsonConvert.SerializeObject(new { content, title }); | |
} | |
static string BuildHelpData(string content = null, string title = null, string trigger = null, string placement = null, string position = null, bool? html = false) | |
{ | |
return JsonConvert.SerializeObject(new { content, title, trigger, placement, position, html }); | |
} | |
} | |
public class HelpPopoverTypes | |
{ | |
public class Trigger | |
{ | |
public const string Click = "click"; | |
public const string Hover = "hover"; | |
public const string Focus = "focus"; | |
public const string Manual = "manual"; | |
} | |
public class Placement | |
{ | |
public const string Top = "top"; | |
public const string Bottom = "bottom"; | |
public const string Left = "left"; | |
public const string Right = "right"; | |
public const string Auto = "auto"; | |
} | |
public class Position | |
{ | |
public const string Caption = "caption"; | |
public const string Editor = "editor"; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//=================================================== | |
// Copyright @ 2020 | |
// Author : Hung Vo ([email protected]) | |
// Time : 2020, August 06 | |
// Description : HelpPopover | |
//=================================================== | |
/* wwwroot/Content/site/site.less */ | |
.popover.j-custom-help-popover { | |
display: block !important; | |
z-index: 1101; | |
/*min-width: 400px*/ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//=================================================== | |
// Copyright @ 2020 | |
// Author : Hung Vo ([email protected]) | |
// Time : 2020, August 06 | |
// Description : HelpPopover | |
//=================================================== | |
export class TimelogRecordDialog extends Serenity.EntityDialog<TimelogRecordRow, any> { | |
protected getFormKey() { return TimelogRecordForm.formKey; } | |
// ... | |
protected form = new TimelogRecordForm(this.idPrefix); | |
constructor() { | |
super(); | |
let helpPopover = new J.HelpPopover(this.form, this.getPropertyItems(), "i-am-a-class").init(); | |
// or | |
// let helpPopover = new J.HelpPopover(this.form, this.getPropertyItems(), "i-am-a-class"); | |
// helpPopover.createHelpPopoverButton(TimelogRecordRow.Fields.UserId, { content: "My content", title: "My title" }); | |
// or | |
//let helpPopover = new J.HelpPopover(this.form, this.getPropertyItems(), "i-am-a-class"); | |
//helpPopover.klassName = "xxx"; | |
//helpPopover.init(); | |
//(helpPopover.buttons[1].helpButton as any).on('hidden.bs.popover', function () { | |
// Q.notifyWarning("test"); | |
//}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//=================================================== | |
// Copyright @ 2020 | |
// Author : Hung Vo ([email protected]) | |
// Time : 2020, August 06 | |
// Description : HelpPopover | |
//=================================================== | |
namespace YOUR_NAME_SPACE.Default.Forms | |
{ | |
using Serenity; | |
using Serenity.ComponentModel; | |
using Serenity.Data; | |
using System; | |
using System.ComponentModel; | |
using System.Collections.Generic; | |
using System.IO; | |
using YOUR_NAME_SPACE.Default.Entities; | |
[FormScript("Default.TimelogRecord")] | |
[BasedOnRow(typeof(Entities.TimelogRecordRow), CheckNames = true)] | |
public class TimelogRecordForm | |
{ | |
[ReadOnly(true)] | |
public Int32? UserId { get; set; } | |
[HelpPopover( | |
content: "Content uses html format <strong>Hello World</strong> <i class='fa fa-question-circle'></i><br />New line.", | |
title: "This is title", | |
placement: "top", | |
html: true)] | |
public Int64 ProjectId { get; set; } | |
[HelpPopover("Another content with html = false and default palcement (auto) <a href='https://google.com' target='_blank'>Link</a>", trigger: "hover focus", html: true)] | |
public DateTime Date { get; set; } | |
//[HelpPopover( | |
// content: "Help button is placed at field caption and will show after clicking on button", | |
// position: "caption", | |
// trigger: "focus")] | |
public TimelogRecordStatus Status { get; set; } | |
[HelpPopover( | |
content: "Help button is placed at field caption and will show after clicking on button", | |
position: "caption", | |
trigger: "focus")] | |
public List<TimelogTaskItemRow> TaskList { get; set; } | |
[HalfWidth, ReadOnly(true), HideOnInsert(true)] | |
public DateTime CreatedDate { get; set; } | |
[HalfWidth, ReadOnly(true), HideOnInsert(true)] | |
public Int32 CreatedBy { get; set; } | |
[HalfWidth, ReadOnly(true), HideOnInsert(true)] | |
public DateTime EditedDate { get; set; } | |
[HalfWidth, ReadOnly(true), HideOnInsert(true)] | |
public Int32 EditedBy { get; set; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
UPDATE: fixed trigger focus issue: it didn't dismiss-on-next-click