Migrate jqhtml slot syntax from <#name> to <Slot:name>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -55,10 +55,10 @@ Indexes all files in `/rsx/` for automatic discovery and loading.
|
||||
|
||||
## JQHTML Named Slots (v2.2.112+)
|
||||
|
||||
Child template syntax changed from `<#slotname />` tags to `content('slotname')` function:
|
||||
- Old: `<#header />` (deprecated)
|
||||
Child template syntax changed from `<Slot:slotname />` tags to `content('slotname')` function:
|
||||
- Old: `<Slot:header />` (deprecated)
|
||||
- New: `<%= content('header') %>` (v2.2.112+)
|
||||
- Parent syntax unchanged: `<#header>content</#header>`
|
||||
- Parent syntax: `<Slot:header>content</Slot:header>`
|
||||
|
||||
## JQHTML Slot-Based Template Inheritance (v2.2.108+)
|
||||
|
||||
|
||||
@@ -53,9 +53,9 @@ class JqhtmlBladeCompiler
|
||||
// If there's slot content, we need to output the div directly to allow blade processing of the content
|
||||
if ($slot_content !== null && trim($slot_content) !== '') {
|
||||
// Check for slot syntax - not allowed in Blade
|
||||
if (preg_match('/<#[a-zA-Z0-9_]+/', $slot_content)) {
|
||||
if (preg_match('/<Slot:[a-zA-Z0-9_]+/', $slot_content)) {
|
||||
throw new \RuntimeException(
|
||||
"JQHTML slot syntax (<#slotname>) is not allowed in Blade files.\n" .
|
||||
"JQHTML slot syntax (<Slot:slotname>) is not allowed in Blade files.\n" .
|
||||
"Component '{$component_name}' contains slot tags in its innerHTML.\n" .
|
||||
"Use standard innerHTML with content() function instead.\n\n" .
|
||||
"Blade usage:\n" .
|
||||
|
||||
@@ -218,8 +218,8 @@ class Jqhtml_ManifestModule extends ManifestModule_Abstract
|
||||
{
|
||||
$slots = [];
|
||||
|
||||
// Match <#slotname> syntax
|
||||
preg_match_all('/<#(\w+)>/', $content, $matches);
|
||||
// Match <Slot:slotname> syntax
|
||||
preg_match_all('/<Slot:(\w+)>/', $content, $matches);
|
||||
|
||||
foreach ($matches[1] as $slot) {
|
||||
if ($slot && !in_array($slot, $slots)) {
|
||||
|
||||
@@ -265,23 +265,23 @@ DATAGRID
|
||||
$per_page=15
|
||||
class="card DataGrid">
|
||||
|
||||
<#DG_Card_Header>
|
||||
<Slot:DG_Card_Header>
|
||||
<Card_Title>Client List</Card_Title>
|
||||
<Card_Header_Right>
|
||||
<Search_Input $sid="filter_input" $placeholder="Search..." />
|
||||
</Card_Header_Right>
|
||||
</#DG_Card_Header>
|
||||
</Slot:DG_Card_Header>
|
||||
|
||||
<#DG_Table_Header>
|
||||
<Slot:DG_Table_Header>
|
||||
<tr>
|
||||
<th data-sortby="id">ID</th>
|
||||
<th data-sortby="name">Name</th>
|
||||
<th data-sortby="created_at">Created</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</#DG_Table_Header>
|
||||
</Slot:DG_Table_Header>
|
||||
|
||||
<#row>
|
||||
<Slot:row>
|
||||
<tr data-href="<%= Rsx.Route('Clients_View_Action', row.id) %>">
|
||||
<td><%= row.id %></td>
|
||||
<td><%= row.name %></td>
|
||||
@@ -297,7 +297,7 @@ DATAGRID
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</#row>
|
||||
</Slot:row>
|
||||
|
||||
</Define:Clients_DataGrid>
|
||||
|
||||
|
||||
@@ -1316,25 +1316,25 @@ CONTENT AND SLOTS
|
||||
</div>
|
||||
</Define:Card_Layout>
|
||||
|
||||
Parent templates provide content using <#slotname> tags:
|
||||
Parent templates provide content using <Slot:slotname> tags:
|
||||
|
||||
<!-- Use with named slots -->
|
||||
<Card_Layout>
|
||||
<#header><h3>User Profile</h3></#header>
|
||||
<#body>
|
||||
<Slot:header><h3>User Profile</h3></Slot:header>
|
||||
<Slot:body>
|
||||
<p>Name: <%= this.data.name %></p>
|
||||
<p>Email: <%= this.data.email %></p>
|
||||
</#body>
|
||||
<#footer>
|
||||
</Slot:body>
|
||||
<Slot:footer>
|
||||
<button class="btn">Save</button>
|
||||
</#footer>
|
||||
</Slot:footer>
|
||||
</Card_Layout>
|
||||
|
||||
Critical rules:
|
||||
- Cannot mix regular content with named slots
|
||||
- If ANY named slots present, ALL content must be in slots
|
||||
- Child template syntax: <%= content('slotname') %>
|
||||
- Parent template syntax: <#slotname>content</#slotname>
|
||||
- Parent template syntax: <Slot:slotname>content</Slot:slotname>
|
||||
|
||||
Decision Guide:
|
||||
Use content() when:
|
||||
@@ -1378,17 +1378,17 @@ CONTENT AND SLOTS
|
||||
|
||||
Child template - slot-only (Users_DataGrid.jqhtml):
|
||||
<Define:Users_DataGrid>
|
||||
<#header>
|
||||
<Slot:header>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
</#header>
|
||||
</Slot:header>
|
||||
|
||||
<#row>
|
||||
<Slot:row>
|
||||
<td><%= row.id %></td>
|
||||
<td><%= row.name %></td>
|
||||
<td><%= row.email %></td>
|
||||
</#row>
|
||||
</Slot:row>
|
||||
</Define:Users_DataGrid>
|
||||
|
||||
Result: Users_DataGrid renders using DataGrid_Abstract HTML
|
||||
@@ -1399,9 +1399,9 @@ CONTENT AND SLOTS
|
||||
<%= content('slotname', data) %>
|
||||
|
||||
Child templates receive data via slot parameter:
|
||||
<#row>
|
||||
<Slot:row>
|
||||
<td><%= row.id %></td>
|
||||
</#row>
|
||||
</Slot:row>
|
||||
|
||||
The slot parameter name matches the slot name automatically.
|
||||
|
||||
@@ -1409,9 +1409,9 @@ CONTENT AND SLOTS
|
||||
Slot names cannot be JavaScript reserved words.
|
||||
Parser rejects with fatal error:
|
||||
|
||||
<#function>Content</#function> <!-- ERROR: reserved word -->
|
||||
<#if>Content</#if> <!-- ERROR: reserved word -->
|
||||
<#header>Content</#header> <!-- Valid -->
|
||||
<Slot:function>Content</Slot:function> <!-- ERROR: reserved word -->
|
||||
<Slot:if>Content</Slot:if> <!-- ERROR: reserved word -->
|
||||
<Slot:header>Content</Slot:header> <!-- Valid -->
|
||||
|
||||
Reserved words include: function, if, for, class, const, let,
|
||||
var, while, switch, return, try, catch, and others.
|
||||
|
||||
@@ -251,18 +251,18 @@ EXAMPLES
|
||||
<Define:DataGrid class="datagrid">
|
||||
<table>
|
||||
<thead>
|
||||
<#header>
|
||||
<Slot:header>
|
||||
<% for (let col of this.data.columns) { %>
|
||||
<th><%= col.title %></th>
|
||||
<% } %>
|
||||
</#header>
|
||||
</Slot:header>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% if (this.data.records.length === 0) { %>
|
||||
<tr><td colspan="100"><#empty>No records</#empty></td></tr>
|
||||
<tr><td colspan="100"><Slot:empty>No records</Slot:empty></td></tr>
|
||||
<% } else { %>
|
||||
<% for (let [idx, record] of this.data.records.entries()) { %>
|
||||
<tr><#row /></tr>
|
||||
<tr><Slot:row /></tr>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</tbody>
|
||||
|
||||
@@ -28,9 +28,9 @@ Slot tags for components with multiple content areas:
|
||||
```blade
|
||||
{{-- Slot tags are highlighted specially --}}
|
||||
<DataGrid>
|
||||
<#header>Name | Email | Status</#header>
|
||||
<#row><%= row.name %> | <%= row.email %></#row>
|
||||
<#empty>No data found</#empty>
|
||||
<Slot:header>Name | Email | Status</Slot:header>
|
||||
<Slot:row><%= row.name %> | <%= row.email %></Slot:row>
|
||||
<Slot:empty>No data found</Slot:empty>
|
||||
</DataGrid>
|
||||
```
|
||||
|
||||
@@ -65,7 +65,7 @@ The extension injects TextMate grammar rules into PHP and Blade files to:
|
||||
1. **Identify Component Tags**: Patterns match tags starting with uppercase letters
|
||||
2. **Highlight Component Names**: Apply distinctive coloring to component names
|
||||
3. **Preserve Blade Syntax**: Handle Blade expressions within component attributes
|
||||
4. **Support Slots**: Recognize and highlight `<#slotname>` syntax
|
||||
4. **Support Slots**: Recognize and highlight `<Slot:slotname>` syntax
|
||||
|
||||
## Supported Patterns
|
||||
|
||||
@@ -138,7 +138,7 @@ The grammar is injected into:
|
||||
### Pattern Matching
|
||||
- Component opening tags: `(<)([A-Z][\\w_]*)(?=\\s|>)`
|
||||
- Component closing tags: `(</)([A-Z][\\w_]*)(>)`
|
||||
- Slot tags: `(<#)(\\w+)(>)` and `(</#)(\\w+)(>)`
|
||||
- Slot tags: `(<Slot:)(\\w+)(>)` and `(</Slot:)(\\w+)(>)`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
Reference in New Issue
Block a user