|
@@ -0,0 +1,384 @@
|
|
|
|
+{
|
|
|
|
+ This file is part of the Pas2JS run time library.
|
|
|
|
+ Copyright (C) 2019 Michael Van Canneyt
|
|
|
|
+
|
|
|
|
+ extra Bootstrap widgets
|
|
|
|
+
|
|
|
|
+ See the file COPYING.FPC, included in this distribution,
|
|
|
|
+ for details about the copyright.
|
|
|
|
+
|
|
|
|
+ This program is distributed in the hope that it will be useful,
|
|
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
+
|
|
|
|
+ **********************************************************************}
|
|
|
|
+
|
|
|
|
+unit bootstrapwidgets;
|
|
|
|
+
|
|
|
|
+{$mode objfpc}
|
|
|
|
+
|
|
|
|
+interface
|
|
|
|
+
|
|
|
|
+uses
|
|
|
|
+ Classes, SysUtils, js, libjquery, libbootstrap, web, webwidget;
|
|
|
|
+
|
|
|
|
+Type
|
|
|
|
+
|
|
|
|
+ { TSimpleToastWidget }
|
|
|
|
+ TContextual = (cNone,cPrimary,cSecondary,cSuccess,cDanger,cWarning,cInfo,cLight,cDark);
|
|
|
|
+
|
|
|
|
+ TSimpleToastWidget = Class(TCustomTemplateWidget)
|
|
|
|
+ private
|
|
|
|
+ FAnimate: Boolean;
|
|
|
|
+ FAutoHide: Boolean;
|
|
|
|
+ FBody: String;
|
|
|
|
+ FBoolean: Boolean;
|
|
|
|
+ FContextual: TContextual;
|
|
|
|
+ FHeader: String;
|
|
|
|
+ FHeaderImage: String;
|
|
|
|
+ FHideDelay: Integer;
|
|
|
|
+ FMinWidth: Integer;
|
|
|
|
+ FSmallHeader: String;
|
|
|
|
+ procedure SetAnimate(AValue: Boolean);
|
|
|
|
+ procedure SetAutoHide(AValue: Boolean);
|
|
|
|
+ procedure SetBody(AValue: String);
|
|
|
|
+ procedure SetBoolean(AValue: Boolean);
|
|
|
|
+ procedure SetContextual(AValue: TContextual);
|
|
|
|
+ procedure SetHeader(AValue: String);
|
|
|
|
+ procedure SetHeaderImage(AValue: String);
|
|
|
|
+ procedure SetHideDelay(AValue: Integer);
|
|
|
|
+ procedure SetMinWidth(AValue: Integer);
|
|
|
|
+ procedure SetSmallHeader(AValue: String);
|
|
|
|
+ Protected
|
|
|
|
+ function BodyHTML: String; virtual;
|
|
|
|
+ function CloseButtonHTML: String; virtual;
|
|
|
|
+ function HeaderHTML: String; virtual;
|
|
|
|
+ Function GetTemplateHTML: String; override;
|
|
|
|
+ Function DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement; override;
|
|
|
|
+ Public
|
|
|
|
+ Constructor Create(aOwner : TComponent); override;
|
|
|
|
+ Procedure Hide;
|
|
|
|
+ Published
|
|
|
|
+ Property Header : String Read FHeader Write SetHeader;
|
|
|
|
+ Property SmallHeader : String Read FSmallHeader Write SetSmallHeader;
|
|
|
|
+ Property Body : String Read FBody Write SetBody;
|
|
|
|
+ Property HeaderImage : String Read FHeaderImage Write SetHeaderImage;
|
|
|
|
+ Property CloseButton : Boolean Read FBoolean Write SetBoolean;
|
|
|
|
+ Property Contextual : TContextual Read FContextual write SetContextual;
|
|
|
|
+ Property HideDelay : Integer Read FHideDelay Write SetHideDelay default 2000;
|
|
|
|
+ Property AutoHide : Boolean Read FAutoHide Write SetAutoHide default True;
|
|
|
|
+ Property Animate : Boolean Read FAnimate Write SetAnimate default False;
|
|
|
|
+ Property MinWidth : Integer Read FMinWidth Write SetMinWidth default 200;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ // Encapsulates the global tag where the toasts are shown.
|
|
|
|
+
|
|
|
|
+ { TToastManager }
|
|
|
|
+
|
|
|
|
+ TToastManager = Class(TWebWidget)
|
|
|
|
+ Private
|
|
|
|
+ FAnimate: Boolean;
|
|
|
|
+ FAutoHide: Boolean;
|
|
|
|
+ FHideDelay: Integer;
|
|
|
|
+ FMinheight: Integer;
|
|
|
|
+ FMinWidth: Integer;
|
|
|
|
+ FMultiToast: Boolean;
|
|
|
|
+ FContentElement : TJSHTMLElement;
|
|
|
|
+ FToastIcon: String;
|
|
|
|
+ class var
|
|
|
|
+ _instance : TToastManager;
|
|
|
|
+ procedure CheckInit;
|
|
|
|
+ procedure SetMinHeight(AValue: Integer);
|
|
|
|
+ procedure SetMultiToast(AValue: Boolean);
|
|
|
|
+ Protected
|
|
|
|
+ Class Function DefaultParentElement : TJSHTMLElement; override;
|
|
|
|
+ Function DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement; override;
|
|
|
|
+ Function GetContentElement: TJSHTMLELement; override;
|
|
|
|
+ Procedure InvalidateElement; override;
|
|
|
|
+ Function HTMLTag: String; override;
|
|
|
|
+ Public
|
|
|
|
+ Constructor Create(aOwner : TComponent); override;
|
|
|
|
+ class function Instance : TToastManager;
|
|
|
|
+ function ShowToast(const aHeader, aBody: String; aContext: TContextual=cNone; Closable: Boolean=True): TSimpleToastWidget;
|
|
|
|
+ Published
|
|
|
|
+ Property MultiToast : Boolean Read FMultiToast Write SetMultiToast;
|
|
|
|
+ Property MinHeight : Integer Read FMinheight Write SetMinHeight default 250;
|
|
|
|
+ Property ToastHideDelay : Integer Read FHideDelay Write FHideDelay default 2000;
|
|
|
|
+ Property ToastAutoHide : Boolean Read FAutoHide Write FAutoHide default True;
|
|
|
|
+ Property ToastAnimate : Boolean Read FAnimate Write FAnimate default False;
|
|
|
|
+ Property ToastMinWidth : Integer Read FMinWidth Write FMinWidth default 200;
|
|
|
|
+ Property ToastIcon : String Read FToastIcon Write FToastIcon;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+Const
|
|
|
|
+ ContextualNames : Array[TContextual] of string = ('','primary','secondary','success','danger','warning','info','light','dark');
|
|
|
|
+
|
|
|
|
+Function Toasts : TToastManager;
|
|
|
|
+
|
|
|
|
+Implementation
|
|
|
|
+
|
|
|
|
+function Toasts: TToastManager;
|
|
|
|
+begin
|
|
|
|
+ Result:=TToastManager.Instance;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+{ TToastManager }
|
|
|
|
+
|
|
|
|
+class function TToastManager.Instance: TToastManager;
|
|
|
|
+begin
|
|
|
|
+ if _instance=Nil then
|
|
|
|
+ _instance:=TToastManager.Create(Nil);
|
|
|
|
+ Result:=_instance;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TToastManager.CheckInit;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ if not IsRendered then
|
|
|
|
+ Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TToastManager.SetMinHeight(AValue: Integer);
|
|
|
|
+begin
|
|
|
|
+ if FMinheight=AValue then Exit;
|
|
|
|
+ FMinheight:=AValue;
|
|
|
|
+ if IsRendered then
|
|
|
|
+ Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TToastManager.SetMultiToast(AValue: Boolean);
|
|
|
|
+begin
|
|
|
|
+ if FMultiToast=AValue then Exit;
|
|
|
|
+ FMultiToast:=AValue;
|
|
|
|
+ if IsRendered then refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+class function TToastManager.DefaultParentElement: TJSHTMLElement;
|
|
|
|
+begin
|
|
|
|
+ Result:=TJSHTMLElement(Document.body);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TToastManager.DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ El : TJSHTMLElement;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ Result:=AElement;
|
|
|
|
+ Result['aria-live']:='polite';
|
|
|
|
+ Result['aria-atomic']:='true';
|
|
|
|
+ Result['style']:='position: relative; min-height: '+IntToStr(MinHeight)+'px;';
|
|
|
|
+ if not MultiToast then
|
|
|
|
+ FContentElement:=Result
|
|
|
|
+ else
|
|
|
|
+ begin
|
|
|
|
+ El:=CreateElement('div',ElementID+'-multi');
|
|
|
|
+ El['style']:='position: absolute; top: 0; right: 0;';
|
|
|
|
+ FContentElement:=El;
|
|
|
|
+ Result.AppendChild(El);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TToastManager.GetContentElement: TJSHTMLELement;
|
|
|
|
+begin
|
|
|
|
+ Result:=FContentElement;
|
|
|
|
+ if (Result=Nil) then
|
|
|
|
+ Result:=Element;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TToastManager.InvalidateElement;
|
|
|
|
+begin
|
|
|
|
+ inherited;
|
|
|
|
+ FContentElement:=nil;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+function TToastManager.HTMLTag: String;
|
|
|
|
+begin
|
|
|
|
+ Result:='div';
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+constructor TToastManager.Create(aOwner: TComponent);
|
|
|
|
+begin
|
|
|
|
+ inherited Create(aOwner);
|
|
|
|
+ FMinHeight:=250;
|
|
|
|
+ FMinWidth:=200;
|
|
|
|
+ FMultiToast:=True;
|
|
|
|
+ FHideDelay:=2000;
|
|
|
|
+ FAutoHide:=True;
|
|
|
|
+ FAnimate:=False;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TToastManager.ShowToast(const aHeader, aBody: String; aContext : TContextual = cNone; Closable: Boolean = True): TSimpleToastWidget;
|
|
|
|
+begin
|
|
|
|
+ CheckInit;
|
|
|
|
+ Result:=TSimpleToastWidget.Create(Self) ;
|
|
|
|
+ With Result do
|
|
|
|
+ begin
|
|
|
|
+ Parent:=Self;
|
|
|
|
+ Header:=aHeader;
|
|
|
|
+ Body:=aBody;
|
|
|
|
+ HeaderImage:=ToastIcon;
|
|
|
|
+ CloseButton:=Closable;
|
|
|
|
+ Contextual:=aContext;
|
|
|
|
+ AutoHide:=ToastAutoHide;
|
|
|
|
+ HideDelay:=ToastHideDelay;
|
|
|
|
+ Animate:=ToastAnimate;
|
|
|
|
+ MinWidth:=ToastMinWidth;
|
|
|
|
+ Refresh;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+{ TSimpleToastWidget }
|
|
|
|
+
|
|
|
|
+function TSimpleToastWidget.CloseButtonHTML: String;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ S : String;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ S:=ContextualNames[Contextual];
|
|
|
|
+ if S<>'' then
|
|
|
|
+ S:='text-'+S;
|
|
|
|
+ Result:=Result+ '<button type="button" class="ml-2 mb-1 close '+S+'" data-dismiss="toast" aria-label="Close">';
|
|
|
|
+ Result:=Result+ ' <span aria-hidden="true">×</span>';
|
|
|
|
+ Result:=Result+ '</button>';
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TSimpleToastWidget.HeaderHTML: String;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ S : String;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ S:=ContextualNames[Contextual];
|
|
|
|
+ if S<>'' then
|
|
|
|
+ S:='text-'+S;
|
|
|
|
+ Result:='<div class="toast-header '+S+'">';
|
|
|
|
+ if HeaderImage<>'' then
|
|
|
|
+ Result:=Result+'<img src="'+HeaderImage+'" class="rounded mr-2">';
|
|
|
|
+ Result:=Result+'<div class="mr-auto">'+Header+'</div>';
|
|
|
|
+ if (SmallHeader<>'') then
|
|
|
|
+ Result:=Result+'<small>'+SmallHeader+'</div>';
|
|
|
|
+ if CloseButton then
|
|
|
|
+ Result:=Result+CloseButtonHTML;
|
|
|
|
+ Result:=Result+'</div>';
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.SetBody(AValue: String);
|
|
|
|
+begin
|
|
|
|
+ if FBody=AValue then Exit;
|
|
|
|
+ FBody:=AValue;
|
|
|
|
+ if isRendered then Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.SetAnimate(AValue: Boolean);
|
|
|
|
+begin
|
|
|
|
+ if FAnimate=AValue then Exit;
|
|
|
|
+ FAnimate:=AValue;
|
|
|
|
+ if isRendered then Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.SetAutoHide(AValue: Boolean);
|
|
|
|
+begin
|
|
|
|
+ if FAutoHide=AValue then Exit;
|
|
|
|
+ FAutoHide:=AValue;
|
|
|
|
+ if isRendered then Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.SetBoolean(AValue: Boolean);
|
|
|
|
+begin
|
|
|
|
+ if FBoolean=AValue then Exit;
|
|
|
|
+ FBoolean:=AValue;
|
|
|
|
+ if isRendered then Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.SetContextual(AValue: TContextual);
|
|
|
|
+begin
|
|
|
|
+ if FContextual=AValue then Exit;
|
|
|
|
+ FContextual:=AValue;
|
|
|
|
+ if isRendered then Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.SetHeader(AValue: String);
|
|
|
|
+begin
|
|
|
|
+ if FHeader=AValue then Exit;
|
|
|
|
+ FHeader:=AValue;
|
|
|
|
+ if isRendered then Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.SetHeaderImage(AValue: String);
|
|
|
|
+begin
|
|
|
|
+ if FHeaderImage=AValue then Exit;
|
|
|
|
+ FHeaderImage:=AValue;
|
|
|
|
+ if isRendered then Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.SetHideDelay(AValue: Integer);
|
|
|
|
+begin
|
|
|
|
+ if FHideDelay=AValue then Exit;
|
|
|
|
+ FHideDelay:=AValue;
|
|
|
|
+ if isRendered then Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.SetMinWidth(AValue: Integer);
|
|
|
|
+begin
|
|
|
|
+ if FMinWidth=AValue then Exit;
|
|
|
|
+ FMinWidth:=AValue;
|
|
|
|
+ if isRendered then Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.SetSmallHeader(AValue: String);
|
|
|
|
+begin
|
|
|
|
+ if FSmallHeader=AValue then Exit;
|
|
|
|
+ FSmallHeader:=AValue;
|
|
|
|
+ if isRendered then Refresh;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TSimpleToastWidget.BodyHTML: String;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ S : String;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ S:=ContextualNames[Contextual];
|
|
|
|
+ if S<>'' then
|
|
|
|
+ S:='alert-'+S;
|
|
|
|
+ Result:='<div class="toast-body '+S+'">';
|
|
|
|
+ Result:=Result+Body;
|
|
|
|
+ Result:=Result+'</div>';
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TSimpleToastWidget.GetTemplateHTML: String;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ Head : String;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ Result:='<div class="toast" aria-live="assertive" aria atomic="true" style="min-width: '+IntToStr(MinWidth)+'px;">';
|
|
|
|
+ Result:=Result+HeaderHTML;
|
|
|
|
+ Result:=Result+BodyHTML;
|
|
|
|
+ Result:=Result+'</div>';
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TSimpleToastWidget.DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement;
|
|
|
|
+begin
|
|
|
|
+ Result:=inherited DoRenderHTML(aParent, aElement);
|
|
|
|
+ JQuery(Result).toast(New(['animation',FAnimate,'autohide',autohide,'delay',FHideDelay]));
|
|
|
|
+ JQuery(Result).ToastShow;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+constructor TSimpleToastWidget.Create(aOwner: TComponent);
|
|
|
|
+begin
|
|
|
|
+ inherited Create(aOwner);
|
|
|
|
+ FMinWidth:=200;
|
|
|
|
+ FAutoHide:=True;
|
|
|
|
+ FHideDelay:=2000;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TSimpleToastWidget.Hide;
|
|
|
|
+begin
|
|
|
|
+ JQuery(Element).Toast('hide');
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+end.
|