jQuery portlets

 

I love JQuery portlets !
These great JQuery elements are so universal that I have used them in many different situations and solutions.

Here are the most important features (at least in my opinion):

  • They are flexible in the size of the text display
  • You can customize their look very easily
  • They can be arranged in columns
  • Simple drag and drop allows sorting or moving between columns

Let’s just start by looking at the construction of these little helpers.
A portlet can consist of a header, the content and a footer.
I deliberately say “can” because in certain applications you can of course also omit individual areas.

Bildschirmfoto 2021 03 13 um 10 35 42

We can easily insert the content of these individual areas into the HTML using a simple Filemaker calculation.
We will look at this in more detail later.
First, we set up the requirements for using the portlets.
To arrange portlets in a meaningful way we use columns from the JQuery plugin.
Arranging portlets in a single column simply results in a simple list – multiple, linked lists allows us to drag and drop between columns.

Bildschirmfoto 2021 03 14 um 13 20 34      Bildschirmfoto 2021 03 13 um 10 58 44

 

To use the portlets we start with 4 tables:

  • Parameter
  • Team
  • Columns
  • Portlets

 

Let’s start with the table Parameter.

Here we have text fields for the JQuery libraries:

  • jquery_1.10.2.js
  • jquery_ui.css
  • jquery_ui.js

In addition, there are the container fields for the icons:

  • ui_icons_444444_256x240
  • ui_icons_555555_256x240
  • ui_icons_777620_256x240
  • ui_icons_777777_256x240
  • ui_icons_cc0000_256x240
  • ui_icons_ffffff_256x240

The next two fields contain the actual HTML and the CSS data:

  • HTML (see end of this article)
  • CSS (see end of this article)

 

Next, let’s consider the Team table.
We use a team table so that each user has a separate record in which calculations and changes take place.
This way we avoid an error message when two people edit the same record at the same time.

  • connectedLists
  • defaultMinimize
  • g_portlet_Id
  • webview

The number field ‘connectedLists’ indicates if the columns are connected.
(portlets can be moved between these columns)
‘defaultMinimize‘ specifies whether the portlets are displayed as minimized.
(Minimized displays only the header)
The ‘g_portlet_Id’ field of type global is set in the ‘Return_Portlet’ script to address a portlet with its id.
The ‘webview‘ is an unstored calculation (text):

webview

Let (
[
fm_appVersion = Floor ( GetAsNumber ( Substitute ( Get ( ApplicationVersion ) ; "." ; "," ) ) ) ;
fm_fmpProtocol = Case ( fm_appVersion ≥ 18 ; "fmp" & fm_appVersion & "://" ; "fmp://" ) & "$/" &
Get ( FileName ) & "?script=Return_Portlet" ;
COLUMNS = List ( Team~Column#activ::div_column )
] ;

Substitute ( Parameter::HTML ;
["__jquery-ui.css" ; Parameter::jquery_ui.css] ;
["__jquery-ui.js" ; Parameter::jquery_ui.js ] ;
["__jquery-1.10.2.js"; Parameter::jquery_1.10.2.js] ;
["__css"; Parameter::css] ;
["__ui-icon ui-icon" ; If ( defaultMinimize = 1 ; "ui-icon ui-icon-plus" ; "ui-icon ui-icon-minus" )] ;
["__connectedLists" ; If ( G_connectedLists = 1 ; " connectWith: \".column\"," ; "" )] ;
["__data"; COLUMNS ] ;
["__script"; fm_fmpProtocol] ;
["__display" ; If ( DefaultMinimize = 1 ; "display: none;"; "" )] ;
["__glyphiconspath" ; "file://" & Middle ( Get ( TemporaryPath ) ;
Position ( Get ( TemporaryPath ) ; "/" ; 1 ; 2 ) ; Length ( Get ( TemporaryPath ) ) )]
)

)

fm_appVersion and fm_fmpProtocol create a script call that we use from the webview in Filemaker.
Columns returns a list of the DIV objects of the active lists.

The replacing part of the calculation includes the JQuery libraries/CSS file and replaces text markers
in the HTML (__markername):
__jquery-ui.css, __jquery-ui.js, __jquery-1.10.2.js, __css replaces the markers with the text documents from the parameter table.
__ui-icon ui-icon creates a line in the HTML that contains either a class with an icon symbol “-” or “+”.
Depending on whether the field “defaultMinimize” contains the value 1 or not.
__connectedLists generates a HTML line which sets the parameter in the ‘HTML’ of the columns whether a 
drag&drop between the columns is allowed or not.
__data writes the ‘COLUMNS’ part into the HTML.
__script replaces the ‘fm_fmpProtocol’ part into the HTML.
__display creates an HTML line that hides the content and footers in the CSS file in the portlets if necessary.
__glyphiconspath replaces a marker in the CSS file to read the icons from the temporary Filemaker path (details can be found in my Kanban article). 

 

In the Columns table we have these fields:

  • active
  • id
  • color
  • sort
  • title
  • containerDisabled
  • headerDisabled
  • createItem
  • div_column 

‘active’ is a number field containing 1 to show the column, or empty if it should be hidden.
‘id’ is an indexed, auto-enter serial field.
‘active’ is a number field containing 1 to show the column, or empty if it should be hidden.
‘color’ unsurprisingly contains the color as a hex value.
‘sort’ is a numerical value for sorting the columns
’title’ is displayed as column title.
‘containerDisabled’, ‘headerDisabled‘ and ‘createItem’ are number fields with the content 0/1 which are used as parameters for the ‘div_column’ calculation field.
‘div_column’ is an unstored calculation (text):

div_column

If ( active ≠ 1 ; "" ;
Case (
containerDisabled = 1 ;
"<div id = '" & id & "' class=\"column\">¶" & List
( Column~Portlets#ColumnId#activ::div_overview ) & "¶
</div>"
;
headerDisabled = 1 ;
"<div id = '" & id & "' class='column portlet disabled'>
<div id = '" & id & "' class=\"column\">¶" & 
List ( Column~Portlets#ColumnId#activ::div_overview ) & "¶
</div>
</div>"
;
"<div id = '" & id & "' class='column portlet disabled'><span id = '" & id & "'
class='columnHeader ui-widget-header ui-corner-all' style='background-color:" &
color & "'>" & title & "</span>
<div id = '" & id & "' class=\"column\">¶" &
If ( createItem ≠ 1 ; "" ; " <div id='" & id & "' class='function-header disabled'" &
If ( color ≠ "" ; " style='background-color:" & color & "'" ; "" ) & ">+ Create card</div>“ ) &
List ( Column~Portlets#ColumnId#activ::div_overview ) & "¶
</div>
</div>“
) // Case
) //If

The calculation is divided into 3 parts:

containerDisabled = 1
This part of the calculation creates only a list of portlets without header and without surrounding container
for the column. 

headerDisabled = 1
Creates a container for the column, but does not create a header area so that no header or function
area is displayed.

In the last part of the calculation a complete column with container and header is generated.
createItem = 1

Displays a function button below the header to create new portlets in the column.

In all calculations the portlets of the column are displayed as a list.

 

Finally, let’s look at the Portlets table.

  • activ
  • id
  • idColumn
  • sort
  • color
  • dueDate
  • header
  • content
  • tooltip
  • sortDisabled
  • div_portlet

‘active’ is a number field containing 1 to show the column, or empty if it should be hidden.
‘id’ is an indexed, auto-enter serial field.
‘idColumn’ connects the portlet to the column in which it is displayed.
‘sort’ is a numerical value for sorting the portlet in a column.
‘color’ contains the portlet color as a hex value which exceeds the color of the column, for example, if you want to indicate a higher priority of the portlet.
‘dueDate’ is a date field i added to demonstrate how to add a display with an icon in the footer.
‘header‘ is just a text field for, as easily be guessed, the header part of the portlet.
‘content‘ is a text field.
’tooltip‘ is also a text field which can contain a text for the bubble help or create it by calculation.
‘sortDisabled‘ prevents the sorting ability of the portlet with content = 1 by inserting a ‘disabled’ class into the HTML.
‘div_portlet’ is an unstored calculation (text):

div_portlet

If ( activ ≠ 1 ; "" ;
Let (
[
color = If ( color = "" ; Portets~Column#ColumnId::color ; color ) ;
R = HexToRGB ( color ; "R" ) ;
G = HexToRGB ( color ; "G" ) ;
B = HexToRGB ( color ; "B" )
] ;

"<div title='" & tooltip & "' id='" & id & "' class='portlet item" & If ( sortDisabled = 1 ;
" disabled" ; "" ) & "'>
<div class='portlet-header-all " & If ( sortDisabled = 1 ; "portlet-header-disabled" ;
"portlet-header" ) & "'" &
If ( color ≠ "" ; " style='background-color:" & "rgba(" & R & "," & G & "," & B & ",.5)
" & "'" ; "" ) & ">" & "" &
If ( header = "" ; "&nbsp" ; id & " " & Substitute ( header ; ¶ ; "<br/>" ) ) & "</b>
</div>
<div class='portlet-content‘>“ & 
If ( activ ≠ 1 ; "" ; Substitute ( GetAsCSS( FilterChar ( content ) ) ; ¶ ; "<br/>“ ) ) & "
</div>
<div class='portlet-footer'" & If ( color ≠ "" ; " style='background-color:" &
"rgba(" & R & "," & G & "," & B & ",.2)" & "'" ; "" ) & ">" &
If ( sortDisabled = 1 ; "<span class='ui-icon ui-icon-locked‘></span>" ; "" ) & 
Case (
dueDate ≤ Get ( CurrentDate ) and dueDate ≠ "" ;
"<span style='float:right‘>
<span id='" & id & "' class='ui-icon ui-icon-alert‘></span><span style='color:red'>" &
dueDate & "</span></span></br>" ;
dueDate ≠ "" ;
"<span style='float:right'>
<span id='" & id & "' class='ui-icon ui-icon-calendar'>
</span><span>" & dueDate & "</span><span></br>“ ;
"") & "
</div>
</div>"
) // Let
) // If

The calculation field generates a portlet only if the ‘active’ field contains 1.
In the Let part of the calculation the HEX color value (from the column or the portlet record itself) is
converted to a RGB value.

The actual calculation then calculates the HTML of the portlet:
If the marker ‘sortDisabled‘ is set then the portlet gets the class ‘disabled’ which prevents the portlet
from being reordered or moved between the cloumns.

Next, the 3 classes for the portlet’s display are created.

The ‘portlet-header-all’ class creates the header with the RGB background color and inserts the
contents of the ‘header’ field.
If ‘sortDisabled‘ = 1 again a class is added which removes the ‘Move’ cursor on the header.

portlet-content‘ creates the content area from the ‘content’ field and filters the text with a  
custom functions ‘FilterChar’.
(You can find a detailed description in my ‘Kanban’ article under the 
paragraph escaping illegal characters to avoid HTML errors)

In the ‘portlet-footer’ again the background color is inserted.
For ‘sortDisabled’ = 1 we create a ‘<span>’ element with a locked icon from the parameter table.
We include these icons in the webview calculation.
(To read how to include them please have a look at my ‘Kanban’ article under the
paragraph ‘Include jQuery icons in Filemaker webview‘)

Using the same method, we display the ‘dueDate’ in the footer on the right.
If a due date is entered and not yet reached, we display the date and show a ‘calendar’ icon.
If the due date is reached it is displayed in red and the icon changes to ‘alert’.

 

Now let’s take a look at the relationship diagram:

Bildschirmfoto 2021 04 01 um 19 20 39

The parameter table is connected via an all match relation (relational operator “X”) to all other tables.
And as I mentioned before, you should always do it this way, trust me.
Team fetches the DIV of the active columns using the ‘Team~Column#X‘ relationship.
Columns list the DIVs of the respective active portlets via the ‘Column~Portlets#ColumnId‘ relationship.
To reorder or move a portlet to another column, the ‘Team~Portlets#G_portlet_Id‘ relationship is used in
the ‘Return_Portlet‘ script.
Finally, the portlets get the color of the assigned column using the ‘Portets~Column#ColumnId‘ relationship.

 

Now let’s take a look at the necessary scripts.

Start

Set Error Capture [ On ]
Go to Layout [ “Main” (Team) ; Animation: None ]
Show/Hide Toolbars [ Lock ; Hide ]
Adjust Window [ Resize to Fit ]
Show All Records
# Export ICONs
Set Variable [ $TempPath ; Value: Get ( TemporaryPath ) ]
Set Variable [ $TempDatei ; Value: Get ( TemporaryPath ) &
GetAsText ( Parameter::ui_icons_444444_256x240 ) ]
Export Field Contents [ Parameter::ui_icons_444444_256x240 ; “$TempDatei” ; Create folders: Off ]
Set Variable [ $TempDatei ; Value: Get ( TemporaryPath ) &
GetAsText ( Parameter::ui_icons_555555_256x240 ) ]
Export Field Contents [ Parameter::ui_icons_555555_256x240 ; “$TempDatei” ; Create folders: Off ]
Set Variable [ $TempDatei ; Value: Get ( TemporaryPath ) &
GetAsText ( Parameter::ui_icons_777620_256x240 ) ]
Export Field Contents [ Parameter::ui_icons_777620_256x240 ; “$TempDatei” ; Create folders: Off ]
Set Variable [ $TempDatei ; Value: Get ( TemporaryPath ) &
GetAsText ( Parameter::ui_icons_777777_256x240 ) ]
Export Field Contents [ Parameter::ui_icons_777777_256x240 ; “$TempDatei” ; Create folders: Off ]
Set Variable [ $TempDatei ; Value: Get ( TemporaryPath ) &
GetAsText ( Parameter::ui_icons_cc0000_256x240 ) ]
Export Field Contents [ Parameter::ui_icons_cc0000_256x240 ; “$TempDatei” ; Create folders: Off ]
Set Variable [ $TempDatei ; Value: Get ( TemporaryPath ) &
GetAsText ( Parameter::ui_icons_ffffff_256x240 ) ]
Export Field Contents [ Parameter::ui_icons_ffffff_256x240 ; “$TempDatei” ; Create folders: Off ]

The ’Start‘ script exports the icon container fields to the Filemaker temporary directory.

 

Return_Portlet

# Get parameters

Set Variable [ $parameter ; Value: Substitute ( GetValue ( Get ( ScriptParameter ) ; 1 ) ; "||||" ; ¶ ) ]
Set Variable [ $parameter ; Value: Substitute ( $parameter ; [ "__auml__" ; "ä" ] ; [ "__Aauml__" ; "Ä" ] ; [ "__uuml__" ; "ü" ] ; [ "__Uuuml__" ; "Ü" ] ; [ "__ouml__" ; "ö" ] ; [ "__Oouml__" ; "Ö" ] ; [ "__szet__" ; "ß" ] ; [ "__amp__" ; "&" ] ; [ "__plus__" ; "+" ] ; [ "__minus__" ; "-" ] ; [ "__slash__" ; "/" ] ; [ "__glz__" ; "=" ] )
Set Variable [ $function ; Value: GetValue ( $parameter ; 1 ) ]
Set Variable [ $value1 ; Value: GetValue ( $parameter ; 2 ) ]
Set Variable [ $value2 ; Value: GetValue ( $parameter ; 3 ) ]

If [ $function = "reorder" ]
Set Variable [ $idColumn ; Value: GetValue ( Substitute ( $value1 ; "[" ; ¶ ) ; 1 ) ]
Set Variable [ $idList ; Value: Let ( [ Var1 = Substitute ( $value1 ; ["[" ; ¶] ; ["]" ; ¶] ) ;
Var2 = GetValue ( Var1 ; 2 ) ] ; Substitute ( Var2 ; ";" ; ¶ ) ) ]
Set Variable [ $idDropped ; Value: Let ( [ Var1 = Substitute ( $value1 ; ["[" ; ¶] ; ["]" ; ¶] ) ;
Var2 = GetValue ( Var1 ; 3 ) ] ; Var2 ) ]
Set Variable [ $m ; Value: ValueCount ( $idList ) ]
If [ $m > 0 ]
Set Variable [ $z ; Value: 1 ]
Set Variable [ $sort ; Value: 1 ]
Loop
Set Variable [ $id ; Value: GetValue ( $idList ; $z ) ]
Set Field [ Team::g_portlet_Id ; $id ]
Commit Records/Requests [ Skip data entry validation ; With dialog: Off ]
Set Field [ Team~Portlets#G_portlet_Id::idColumn ; $idColumn ]
Set Field [ Team~Portlets#G_portlet_Id::sort ; $sort ]
Set Variable [ $sort ; Value: $sort+1 ]
Set Variable [ $z ; Value: $z+1 ]
Exit Loop If [ $z > $m ]
End Loop
Set Field [ Team::g_portlet_Id ; "" ]
End If
Commit Records/Requests [ Skip data entry validation ; With dialog: Off ]
Go to Object [ Object Name: "webview" ]


Else If [ $function = "selectItem" ]
Set Field [ Team::g_portlet_Id ; $value1 ]
Commit Records/Requests [ With dialog: Off ]
New Window [ Style: Card ; Using layout: “Portlet Card” (Portlets) ]
Go to Related Record [ Show only related records ; From table: “Team~Portlets#G_portlet_Id” ;
Using layout: “Portlet Card” (Portlets) ]
Adjust Window [ Resize to Fit ]

Else If [ $function = "newItem" ]
Set Variable [ $max ; Value: ExecuteSQL ( "SELECT MAX(sort) FROM Portlets WHERE idColumn=?" ;
"##" ; ¶ ; $value1 ) ]
New Window [ Style: Card ; Using layout: “Portlet Card” (Portlets) ]
Go to Layout [ “Portlet Card” (Portlets) ; Animation: None ]
New Record/Request
Set Field [ Portlets::idColumn ; $value1 ]
Set Field [ Portlets::sort ; $max +1 ]
Commit Records/Requests [ With dialog: Off ]

Else If [ $function = "deleteItem" ]
Show Custom Dialog [ "Alert" ; "Delete ?" ]
If [ Get ( LastMessageChoice ) = 2 ]
Exit Script [ Text Result: ]
End If
Delete Record/Request [ With dialog: Off ]
Close Window [ Current Window ]
Refresh Window []

Else If [ $function = "saveItem" ]
Close Window [ Current Window ]
Refresh Window []
End If

The script ‘Return_Portlet’ is divided into individual function parts.
The parameter block passed from the HTML via fmp protocol is taken over by the separator ‘||||’ into
individual variables.
(The special characters were replaced in the HTML by strings, which are now translated back by substitute).

In the script part ‘reorder‘ the target column, the list of portlet ids in the correct order and the id of the moved
portlet is extracted from a string generated in JQuery (see below at “HTML“).
The portlets of the column are processed in a loop in which the sorting is reassigned and, if necessary,
the column assignment is corrected.

selectItem‘ calls a modal window with the portlet data.

newItem‘ first fetches the maximum value of the sorting of the portlets of this column.
A new portlet is created, the column assigned and the sort entered.

deleteItem‘ deletes the record after request to the user.

Finally, ‘saveItem‘ just closes the modal portlet window and refreshes the webview.

 

Last but not least, here’s a quick look at the stored JQuery files and our HTML and CSS data.

Please download the JQuery libraries from https://jqueryui.com/download/.

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>__jquery-ui.css</style>
<script type="text/javascript">__jquery-1.10.2.js</script>
<script type="text/javascript">__jquery-ui.js</script>
<style>__css</style>
<script>

$(document).ready(function() {

$( function() {

$(document ).tooltip({ position: "bottom left", opacity: 0.7});

$( ".column").sortable({
__connectedLists
scroll: true,
scrollSensitivity: 80,
scrollSpeed: 3,
handle: ".portlet-header",
cancel: ".portlet-toggle",
placeholder: "portlet-placeholder ui-corner-all",
items: ".portlet:not(.disabled)",
update: function( event, ui ) {
var changedList = this.id;
var order = $(this).sortable('toArray');
var positions = order.join(';');
output = changedList + '[' + positions + ']'
},
stop: function( event, ui ) {
var id = ui.item.attr("id");
$( self.location = '__script&param=reorder||||' + output + id );
}
});

$( ".portlet" )
.addClass( "ui-widget ui-widget-content ui-helper-clearfix ui-corner-all" )
.removeClass("disabled")
.find( ".portlet-header-all" )
.addClass( "ui-widget-header ui-corner-all" )
.prepend( „<span class='ui-icon __ui-icon ui-icon portlet-toggle'></span>");

$( ".portlet-toggle" ).on( "click", function(e) { var icon = $( this );
icon.toggleClass( "ui-icon-minus ui-icon-plus" );
icon.closest( ".portlet" ).find( ".portlet-content" ).toggle();
icon.closest( ".portlet" ).find( ".portlet-footer" ).toggle();
// prevent function portlet-header" ).on( "click", function to fire
e.preventDefault();
e.stopPropagation();
return false; });

$( ".portlet-content" ).on( "click", function( e ) {
var id = $(this).closest('.portlet').attr('id');
$( self.location = '__script&param=selectItem||||' + id );
});

$( ".portlet-header" ).on( "click", function( e ) {
var id = $(this).closest('.portlet').attr('id');
$( self.location = '__script&param=selectItem||||' + id );
});

$( ".portlet-header-disabled" ).on( "click", function( e ) {
var id = $(this).closest('.portlet').attr('id');
$( self.location = '__script&param=selectItem||||' + id );
});

$( ".portlet-footer" ).on( "click", function( e ) {
var id = $(this).closest('.portlet').attr('id');
$( self.location = '__script&param=selectItem||||' + id );
});

$( ".function-header" ).on( "click", function( e ) {
var id = $(this).attr('id');
$( self.location = '__script&param=newItem||||' + id );
});

} ); //Document Ready

</script>
</head>
<body>
<div class='parent‘>
  __data
</div>
</body>
</html>

The HMTL document actually only consists of the standard specifications for including the JQuery columns and click calls for the portlets.
The substitutions from the substitute calculations are easily recognized by the two leading ‘__’.

Only the part to create the parameter for the ‘Return_Portlet’ script is a bit more complicated:
In the ‘update’ and ‘stop’ part of the JQuery a parameter in the form

columnId[portletId1,portletId3,portletId3,…]portletIdDropped

is created.

update: function( event, ui ) {
var changedList = this.id;
var order = $(this).sortable(‘toArray’);
var positions = order.join(‘;’);
output = changedList + ‘[‘ + positions + ‘]’
},
stop: function( event, ui ) {
var id = ui.item.attr(‘id’);
$( self.location = ‘__script&param=reorder||||’ + output + id );
}

This is evaluated for the classification of a portlet within a column or when dropping to another column in the script “Return_Portlet” in the part “reorder”.

CSS:

html, body{
margin: 0px;
padding: 1px;
font-family: 'arial';
font-size: 0.8em;
}
body {
}
p {
margin-top: 4px;
margin-bottom: 0px;
}

.parent {
white-space: nowrap;
}

.ui-tooltip {
padding: 6px 6px;
color: black;
background-color: #edeef2;
position: absolute;
border: 1px solid #767676;
max-width: 180px;
z-index: 9999;
box-shadow:
0 2.8px 2.2px rgba(0, 0, 0, 0.034),
0 6.7px 5.3px rgba(0, 0, 0, 0.048),
0 12.5px 10px rgba(0, 0, 0, 0.06),
0 22.3px 17.9px rgba(0, 0, 0, 0.072),
0 41.8px 33.4px rgba(0, 0, 0, 0.086),
0 100px 80px rgba(0, 0, 0, 0.12)
}

.ui-icon-comment {
cursor:pointer;
}
.ui-icon-document {
cursor:pointer;
}
.ui-icon-bullet {
cursor:pointer;
}
.ui-icon-check {
cursor:pointer;
}
.ui-icon-person {
cursor:pointer;
}
.columnHeader {
width: 228px;
display: block;
margin: 0px 4px 4px 0px;
padding-top: 4px;
padding-bottom: 4px;
}
.column {
width: 230px;
padding-bottom: 4px;
text-align: center;
display:inline-block;
vertical-align: top;
white-space: normal;
}
.portlet {
margin: 0 0.6em 1em 0;
padding: 0.3em;
}
.function-header {
padding: 0.3em 0.8em;
margin-bottom: 0.2em;
position: relative;
  text-align: center;
cursor:pointer;

  border-radius: 50%;
  -webkit-border-radius: 50%;
  -moz-border-radius: 50%;
}
.portlet-header {
padding: 0.2em;
word-wrap: break-word;
margin-bottom: 0em;
position: relative;
text-align: left;
cursor:move;
}
.portlet-header-disabled {
padding: 0.2em;
word-wrap: break-word;
margin-bottom: 0em;
position: relative;
text-align: left;
}
.portlet-footer {
__display
word-wrap: break-word;
text-align:justify;
padding: 0.3em 0.3em;
position: relative;
cursor:pointer;
}
.portlet-toggle {
position: absolute;
top: 50%;
right: 0;
margin-top: -8px;
cursor:pointer;
}
.portlet-content {
__display
word-wrap: break-word;
padding: 0.4em;
text-align: left;
cursor:pointer;
}
.portlet-placeholder {
border: 1px dotted black;
margin: 0 1em 1em 0;
height: 50px;
}
.disabled {
}
.item {
}
a {
color: blue !important;
}
.ui-tooltip {
white-space: pre-line;
}

All brought together our result looks like this:

We can use the parameters to set the defaults for both columns and portlets.
In the picture above you can see that the header has been deactivated for the two columns on the right and that
the container has been removed on the far right.

Here you can see the modal preset window:

After clicking on a portlet, a window opens with the portlet data.

 

Finally, let’s take a look at the possibilities you have with columns and portlets.
We can nest columns so that we can display a column within a column while retaining full functionality.
As an example, I show a simple weekly calendar where a column ‘Day’ is nested inside the columns ‘Week’,
which holds the actual task portlets.
So it is possible not only to change the order of the tasks within the day, but also to drag&drop them to
another day or week.

Bildschirmfoto 2021 03 13 um 19 13 34

In a Filemaker database it looks like this.

Bildschirmfoto 2021 03 18 um 12 37 41

Portlets offer endless possibilities and I myself am still discovering new applications where such a representation
is clearly superior to the possibilities of pure Filemaker. 

My Kanban would never have been possible without these portlets.

It’s just fun to use these smart little tools and open up new horizons for the Filemaker user.

If you like to have an open version of the file, just send me an email.

You have any questions? Feel free to send me an email, I’ll try to answer as soon as possible.