Skip to content

BurningTreeC/resizer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

97 Commits
 
 
 
 
 
 
 
 

Repository files navigation

TiddlyWiki Resizer Widget Plugin

A powerful and flexible resizer widget for TiddlyWiki that enables interactive resizing of UI elements with support for multiple tiddlers, various CSS units, and calc() expressions.

Features

  • Multi-tiddler Support: Resize multiple tiddlers simultaneously using filter expressions
  • Comprehensive Unit Support: Works with all CSS units (px, %, em, rem, vh, vw, vmin, vmax)
  • CSS calc() Expressions: Use complex calculations for min/max values like calc(100% - 350px)
  • Unit Preservation: Maintains each tiddler's original unit type while ensuring consistent resize behavior
  • Smart Unit Conversion: Automatically converts between units when needed
  • Constraint System: Enforces min/max limits across all target tiddlers as a group
  • Live Preview: Optional real-time visual feedback during resizing
  • Directional Control: Supports both horizontal and vertical resizing
  • Aspect Ratio: Maintain aspect ratios during resize operations
  • Touch Support: Works with both mouse and touch input via pointer events
  • Double-Click Reset: Double-click any resizer handle to reset to default/min/max values
  • Enhanced Handle Styles: Choose from multiple handle visual styles (solid, dots, lines, chevron, grip)
  • Haptic Feedback: Tactile feedback on mobile devices for better user experience

Installation

  1. Go to burningtreec.github.io/resizer/
  2. Drag and drop the plugin file into your TiddlyWiki
  3. Save and reload your wiki

Basic Usage

<$resizer
  direction="horizontal"
  tiddler="$:/themes/tiddlywiki/vanilla/metrics/sidebarwidth"
  min="200px"
  max="800px"
  default="350px"
/>

Widget Attributes

Core Attributes

Attribute Description Default
direction Resize direction: "horizontal" or "vertical" "horizontal"
tiddler Target tiddler -
filter Filter attribute to specify multiple tiddlers (optional alternative to tiddler) -
field Field to update in the target tiddler "text"
unit Unit for the resizer (px, %, em, rem, vh, vw, etc.) "px"
default Default value if tiddler doesn't exist (supports calc() expressions) "200px" or "50%"
min Minimum value (supports calc() expressions) "50" or "10"
max Maximum value (supports calc() expressions) "800" or "90"

Behavior Attributes

Attribute Description Default
invert Invert resize direction: "yes" or "no" "no"
live Update target element in real-time: "yes" or "no" "no"
position Position calculation: "absolute" or "relative" "absolute"
mode Resize mode: "single" or "multiple" "single"

Target Attributes

Attribute Description Default
selector CSS selector for target DOM element(s) -
element Target relative element: "parent", "parent.parent", "previousSibling", "nextSibling" -
property CSS property to modify "width" or "height"
aspectRatio Maintain aspect ratio for live DOM manipulation only (e.g., "16:9" or "1.5") -

Event Attributes

Attribute Description
actions Action string to execute on value change
onBeforeResizeStart Actions to execute before resize starts (useful for setup)
onResizeStart Actions to execute when resize starts
onResize Actions to execute during resize
onResizeEnd Actions to execute when resize ends
dblClickActions Custom actions to execute on double-click (overrides reset behavior)

Available Action Variables

The following variables are available within action strings:

Variable Description Available In
<<tv-action-value>> The numeric value in the widget's unit All actions
<<tv-action-value-pixels>> The value in pixels (always pixels regardless of unit) All actions
<<tv-action-formatted-value>> The value with unit (e.g., "350px", "50%") All actions
<<tv-action-direction>> The resize direction ("horizontal" or "vertical") All actions
<<tv-action-property>> The CSS property being modified All actions
<<tv-action-handle-size>> The computed size of the resize handle in pixels All actions
<<tv-action-parent-size>> The parent container width (horizontal) or height (vertical) in pixels All actions
<<tv-action-delta-x>> The horizontal mouse movement delta onResize only
<<tv-action-delta-y>> The vertical mouse movement delta onResize only

Styling Attributes

Attribute Description Default
class Additional CSS classes for the resizer ""
handlePosition Position of resize handle: "before", "after", "overlay" "after"
handleStyle Visual style of the handle: "solid", "dots", "lines", "chevron", "grip" "solid"
disable Disable the resizer: "yes" or "no" "no"
visiblePortion Calculate resize based only on visible portion when element is clipped: "yes" or "no" "no"

Reset Attributes

Attribute Description Default
resetTo What value to reset to on double-click: "default", "min", "max", "custom" (ignored if dblClickActions is set) "default"
resetValue Custom value to reset to when resetTo="custom" (ignored if dblClickActions is set) -
smoothReset Animate the reset transition: "yes" or "no" (ignored if dblClickActions is set) "yes"
onReset Action string to execute when resizer is reset (ignored if dblClickActions is set) -

Mobile/Touch Attributes

Attribute Description Default
hapticFeedback Enable haptic feedback on touch devices: "yes" or "no" "yes"

Advanced Examples

Multiple Tiddlers with Filter Expression

<$resizer
  direction="horizontal"
  tiddler="[tag[layout-metrics]]"
  min="100px"
  max="calc(100% - 200px)"
/>

Space-Separated Tiddler List (Filter)

<$resizer
  direction="horizontal"
  filter="$:/metrics/storyright $:/metrics/storywidth $:/metrics/tiddlerwidth"
  min="300px"
  max="calc(100vw - 350px)"
/>

Using Different Units

<!-- Percentage-based resizing -->
<$resizer
  direction="vertical"
  tiddler="$:/config/header/height"
  unit="%"
  min="5%"
  max="50%"
  default="20%"
/>

<!-- Using viewport units -->
<$resizer
  direction="horizontal"
  tiddler="$:/config/panel/width"
  unit="vw"
  min="20vw"
  max="80vw"
  default="50vw"
/>

With Actions and Events

<$resizer
  direction="horizontal"
  tiddler="$:/state/sidebar/width"
  actions="""
    <$action-setfield $tiddler="$:/state/sidebar/visible" text="yes"/>
  """
  onResizeEnd="""
    <$action-log message="Resize completed" value=<<value>>/>
  """
/>

Live DOM Manipulation

<$resizer
  direction="horizontal"
  selector=".tc-sidebar"
  property="width"
  tiddler="$:/config/sidebar/width"
  live="yes"
/>

Disabling the Resizer

The disable attribute allows you to temporarily disable the resizer functionality:

<!-- Disable resizer based on condition -->
<$resizer
  direction="horizontal"
  tiddler="$:/state/panel/width"
  disable={{{ [{$:/state/edit-mode}match[yes]then[yes]else[no]] }}}
/>

<!-- Always disabled resizer -->
<$resizer
  direction="horizontal"
  tiddler="$:/state/panel/width"
  disable="yes"
/>

When disabled:

  • The resizer handle remains visible but is non-interactive
  • The class tc-resizer-disabled is added for styling
  • No resize events or actions are triggered
  • The data-disabled="true" attribute is set on the DOM element

Double-Click Reset

Double-click any resizer handle to reset it to a specified value:

<!-- Reset to default value -->
<$resizer
  direction="horizontal"
  tiddler="$:/state/panel/width"
  default="300px"
  resetTo="default"
/>

<!-- Reset to minimum value -->
<$resizer
  direction="horizontal"
  tiddler="$:/state/panel/width"
  min="200px"
  resetTo="min"
/>

<!-- Reset to custom value with action -->
<$resizer
  direction="horizontal"
  tiddler="$:/state/panel/width"
  resetTo="custom"
  resetValue="400px"
  onReset="""
    <$action-log message="Panel reset to 400px"/>
  """
/>

<!-- Disable smooth animation on reset -->
<$resizer
  direction="horizontal"
  tiddler="$:/state/panel/width"
  smoothReset="no"
/>

<!-- Custom double-click actions (overrides reset behavior) -->
<$resizer
  direction="horizontal"
  tiddler="$:/state/panel/width"
  dblClickActions="""
    <$action-sendmessage $message="tm-modal" $param="$:/core/ui/ControlPanel/Settings"/>
    <$action-log message="Panel width on double-click" value=<<tv-action-value>>/>
  """
/>

When using dblClickActions, the following variables are available:

  • <<tv-action-value>> - The current value with unit
  • <<tv-action-value-pixels>> - The current value in pixels
  • <<tv-action-direction>> - The resize direction
  • <<tv-action-parent-size>> - The parent container size in pixels
  • <<tv-action-handle-size>> - The handle size in pixels

Handle Styles

Choose from different visual styles for the resizer handle:

<!-- Default solid bar -->
<$resizer handleStyle="solid" />

<!-- Dots pattern -->
<$resizer handleStyle="dots" />

<!-- Dashed lines -->
<$resizer handleStyle="lines" />

<!-- Chevron arrows (❯❯ for horizontal, ⌄⌄ for vertical) -->
<$resizer handleStyle="chevron" />

<!-- Grip dots (⋮⋮ for horizontal, ⋯⋯ for vertical) -->
<$resizer handleStyle="grip" />

Visible Portion Mode

The visiblePortion attribute allows the resizer to work correctly with elements that are partially clipped outside the viewport:

<!-- Enable visible portion mode for clipped elements -->
<$resizer
  direction="horizontal"
  tiddler="$:/state/panel/width"
  visiblePortion="yes"
/>

When enabled, this mode:

  • Calculates resize operations based only on the visible portion of the element
  • Automatically adjusts the resize ratio when elements extend beyond viewport boundaries
  • Ensures consistent resizing behavior for partially visible elements
  • Useful for panels that slide off-screen or are clipped by viewport edges

This is particularly helpful when working with:

  • Off-canvas navigation panels
  • Sliding drawers that extend beyond viewport
  • Elements with negative margins or transforms
  • Overflow-hidden containers with content outside bounds

Mobile Experience

The resizer includes enhanced support for touch devices:

<!-- Enable haptic feedback (default) -->
<$resizer
  direction="horizontal"
  tiddler="$:/state/panel/width"
  hapticFeedback="yes"
/>

<!-- Disable haptic feedback -->
<$resizer
  direction="horizontal"
  tiddler="$:/state/panel/width"
  hapticFeedback="no"
/>

Haptic feedback provides:

  • 5ms vibration on touch start (grab)
  • 3ms vibration on touch end (release)
  • Double pulse (10-50-10ms) on double-click reset

Real-World Example

The resizer is used in TiddlyWiki's sidebar implementation:

<$resizer
  class="tc-sidebar-resizer"
  direction="horizontal"
  filter="$:/themes/tiddlywiki/vanilla/metrics/storyright $:/themes/tiddlywiki/vanilla/metrics/storywidth $:/themes/tiddlywiki/vanilla/metrics/tiddlerwidth"
  min={{$:/themes/tiddlywiki/vanilla/metrics/storyminwidth}}
  max={{{ [[calc(100vw - ]addsuffix{$:/themes/tiddlywiki/vanilla/metrics/sidebarminwidth}addsuffix[)]] }}}
  default="350px"
  invert="no"
/>

This example demonstrates:

  • Multiple tiddlers being resized together
  • Dynamic min/max values from tiddlers
  • Complex calc() expression for maximum value
  • Integration with TiddlyWiki's theme system

CSS calc() Expression Support

The widget supports CSS calc() expressions in min, max, and default values, including special variables:

Special Variables

The following variables can be used within calc() expressions in the min, max, and default attributes:

  • handleSize - The computed width/height of the resize handle
  • handleWidth - Alias for handleSize
  • handleHeight - Alias for handleSize

These variables are automatically replaced with the actual pixel size of the resize handle when the calc() expression is evaluated.

<!-- Leave 350px for sidebar -->
<$resizer
  max="calc(100% - 350px)"
/>

<!-- Use viewport width -->
<$resizer
  max="calc(100vw - 400px)"
/>

<!-- Complex calculations -->
<$resizer
  min="calc(20% + 100px)"
  max="calc(80% - 50px)"
/>

<!-- Dynamic default value based on viewport -->
<$resizer
  default="calc(50vw - 100px)"
  min="200px"
  max="800px"
/>

<!-- Responsive default with fallback -->
<$resizer
  default="calc(100% / 3)"
  min="calc(100% / 6)"
  max="calc(100% / 2)"
/>

<!-- Using handle size in calculations -->
<$resizer
  min="calc(handleSize + 20px)"
  max="calc(100% - handleSize)"
  default="calc(50% - handleSize / 2)"
/>

<!-- Account for handle in panel layouts -->
<$resizer
  direction="horizontal"
  max="calc(100vw - 400px - handleWidth)"
  min="calc(200px + handleWidth)"
/>

Unit Conversion Features

The widget intelligently handles mixed units:

  • Tiddlers can store values in any unit (e.g., "2.5rem", "50vh", "300px")
  • Internal calculations are performed in pixels for consistency
  • Values are converted back to the original unit when saved
  • Maintains precision with appropriate decimal places per unit type

Constraint Behavior

When resizing multiple tiddlers:

  • If ANY tiddler would exceed min/max limits, NO tiddlers are updated
  • This preserves relative relationships between tiddler values
  • All tiddlers move together within the defined constraints

Styling

The widget creates a div element with the class tc-resizer plus any additional classes specified. You can style it with CSS:

.tc-resizer {
  cursor: ew-resize; /* or ns-resize for vertical */
  width: 5px;
  background: #ccc;
  position: relative;
}

.tc-resizer:hover {
  background: #999;
}

.tc-resizer-active {
  background: #666;
}

.tc-resizer-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

During resize operations:

  • .tc-resizing class is added to the body element
  • .tc-resizer-active class is added to the active resizer
  • .tc-resize-overlay overlay captures pointer events

Layout Procedures

The resizer plugin includes several pre-built layout procedures that make it easy to create common split-panel layouts:

horizontal-split-panel

Creates a horizontally split layout with a resizable divider between left and right panels.

<<horizontal-split-panel
  leftContent:"Content for left panel"
  rightContent:"Content for right panel"
  width:"50%"
  minWidth:"100px"
  maxWidth:"80%"
  stateTiddler:"$:/state/hsplit/width"
  class:"my-panel"
  leftClass:"left-panel-class"
  rightClass:"right-panel-class"
  splitterClass:"splitter-class"
>>
Parameter Description Default
leftContent Content for the left panel (variable or tiddler name) ""
rightContent Content for the right panel (variable or tiddler name) ""
width Initial width of the left panel "50%"
minHeight Minimum height of the panel container "100%"
minWidth Minimum width of the left panel "100px"
maxWidth Maximum width of the left panel "80%"
stateTiddler Tiddler to store the current width "$:/state/hsplit/width"
class Additional CSS classes for the container ""
leftClass Additional CSS classes for the left panel ""
rightClass Additional CSS classes for the right panel ""
splitterClass Additional CSS classes for the splitter ""

vertical-split-panel

Creates a vertically split layout with a resizable divider between top and bottom panels.

<<vertical-split-panel
  topContent:"Content for top panel"
  bottomContent:"Content for bottom panel"
  height:"50%"
  panelHeight:"100%"
  minHeight:"100px"
  maxHeight:"80%"
  stateTiddler:"$:/state/vsplit/height"
  class:"my-panel"
  topClass:"top-panel-class"
  bottomClass:"bottom-panel-class"
  splitterClass:"splitter-class"
>>
Parameter Description Default
topContent Content for the top panel (variable or tiddler name) ""
bottomContent Content for the bottom panel (variable or tiddler name) ""
panelHeight Height of the entire panel container "100%"
height Initial height of the top panel "50%"
minHeight Minimum height of the top panel "100px"
maxHeight Maximum height of the top panel "80%"
stateTiddler Tiddler to store the current height "$:/state/vsplit/height"
class Additional CSS classes for the container ""
topClass Additional CSS classes for the top panel ""
bottomClass Additional CSS classes for the bottom panel ""
splitterClass Additional CSS classes for the splitter ""

three-column-panels

Creates a three-column layout with resizable left and right panels, and a flexible center panel.

<<three-column-panels
  leftContent:"Left panel content"
  centerContent:"Center panel content"
  rightContent:"Right panel content"
  leftWidth:"200px"
  rightWidth:"200px"
  minWidth:"150px"
  maxWidth:"400px"
  minHeight:"100%"
  leftStateTiddler:"$:/state/three-col/left"
  rightStateTiddler:"$:/state/three-col/right"
  class:"my-three-col"
>>
Parameter Description Default
leftContent Content for the left panel (variable or tiddler name) ""
centerContent Content for the center panel (variable or tiddler name) ""
rightContent Content for the right panel (variable or tiddler name) ""
leftWidth Initial width of the left panel "200px"
rightWidth Initial width of the right panel "200px"
minWidth Minimum width for side panels "150px"
maxWidth Maximum width for side panels "400px"
minHeight Minimum height of the panel container "100%"
leftStateTiddler Tiddler to store the left panel width "$:/state/three-col/left"
rightStateTiddler Tiddler to store the right panel width "$:/state/three-col/right"
class Additional CSS classes for the container ""

Note: The center panel automatically adjusts its width based on the left and right panel sizes, with constraints to ensure all panels remain visible.

collapsible-master-detail-panel

Creates a master-detail layout where the master panel can be collapsed to save space.

<<collapsible-master-detail-panel
  masterContent:"Master panel content"
  detailContent:"Detail panel content"
  collapsed:"no"
  size:"300px"
  minSize:"200px"
  maxSize:"500px"
  minHeight:"100%"
  stateTiddler:"$:/state/cmd/size"
  collapseStateTiddler:"$:/state/cmd/collapsed"
  class:"my-master-detail"
>>
Parameter Description Default
masterContent Content for the master panel (variable or tiddler name) ""
detailContent Content for the detail panel (variable or tiddler name) ""
collapsed Initial collapsed state ("yes" or "no") "no"
size Initial width of the master panel "300px"
minSize Minimum width of the master panel "200px"
maxSize Maximum width of the master panel "500px"
minHeight Minimum height of the panel container "100%"
stateTiddler Tiddler to store the master panel width "$:/state/cmd/size"
collapseStateTiddler Tiddler to store the collapsed state "$:/state/cmd/collapsed"
class Additional CSS classes for the container ""

Features:

  • Collapse/expand buttons integrated into the master panel
  • Detail panel automatically expands when master panel is collapsed
  • State persistence for both size and collapse state

MediaQuery Filter

The plugin includes a mediaquery filter operator that allows you to evaluate CSS media queries within TiddlyWiki filters. This is particularly useful for creating responsive layouts and conditional content.

Syntax

[mediaquery<media-query>]

Examples

<!-- Show content only on mobile devices -->
<%if [mediaquery[(max-width: 768px)]] %>
  This content only appears on mobile devices
<% endif %>

<!-- Show different content for touch vs mouse devices -->
<%if [mediaquery[(pointer: coarse)]] %>
  <div class="touch-interface">
    Touch-optimized interface with larger buttons
  </div>
<% else %>
  <div class="mouse-interface">
    Mouse-optimized interface with hover states
  </div>
<% endif %>

<!-- Responsive layout based on screen size -->
<%if [mediaquery[(min-width: 1024px)]] %>
  <<three-column-panels
    leftContent:"Navigation"
    centerContent:"Main Content"
    rightContent:"Sidebar"
  >>
<% else %>
  <<vertical-split-panel
    topContent:"Navigation"
    bottomContent:"Main Content"
  >>
<% endif %>

<!-- Dark mode support -->
<%if [mediaquery[(prefers-color-scheme: dark)]] %>
  <style>
    .my-component { background: #1a1a1a; color: #ffffff; }
  </style>
<% endif %>

<!-- Responsive resizer configuration -->
<$let handleWidth={{{ [mediaquery[(pointer: coarse)]then[40px]else[10px]] }}}>
  <$resizer
    direction="horizontal"
    tiddler="$:/state/panel-width"
    default=<<handleWidth>>
  />
</$let>

<!-- Disable animations for users who prefer reduced motion -->
<%if [mediaquery[(prefers-reduced-motion: reduce)]] %>
  <style>
    * { animation: none !important; transition: none !important; }
  </style>
<% endif %>

Features

  • Reactive Updates: Automatically refreshes when media query state changes (e.g., window resize, device rotation)
  • Browser-Only: Returns empty results when running in Node.js
  • Error Handling: Invalid media queries return empty results
  • Negation Support: Use !mediaquery to invert the condition

Common Media Queries

  • (max-width: 768px) - Mobile devices
  • (min-width: 769px) - Tablets and desktops
  • (pointer: coarse) - Touch devices
  • (pointer: fine) - Mouse/trackpad devices
  • (prefers-color-scheme: dark) - Dark mode preference
  • (orientation: portrait) - Portrait orientation
  • (orientation: landscape) - Landscape orientation
  • (prefers-reduced-motion: reduce) - Reduced motion preference

Browser Compatibility

  • Modern browsers with ES5 support
  • Touch devices via pointer events
  • MediaQueryList API support for reactive media queries
  • Fallback handling for older viewport unit implementations
  • Cross-browser window object detection

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This plugin is released under the MIT License. See the LICENSE file for details.

Credits

Created for the TiddlyWiki community by BTC.