Skip to content

Commit b4d1400

Browse files
committed
Merge branch '4.x' into 5.x
2 parents 676aaa8 + dd58029 commit b4d1400

19 files changed

Lines changed: 974 additions & 19 deletions

File tree

packages/forms/src/Components/Concerns/HasHint.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,10 @@ public function hintColor(string | array | Closure | null $color): static
109109
public function hintIcon(string | BackedEnum | Htmlable | Closure | null $icon, string | Closure | null $tooltip = null): static
110110
{
111111
$this->hintIcon = $icon;
112-
$this->hintIconTooltip($tooltip);
112+
113+
if (func_num_args() >= 2) {
114+
$this->hintIconTooltip($tooltip);
115+
}
113116

114117
return $this;
115118
}

packages/infolists/src/Components/Concerns/HasHint.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,10 @@ public function hintColor(string | array | Closure | null $color): static
108108
public function hintIcon(string | BackedEnum | Htmlable | Closure | null $icon, string | Closure | null $tooltip = null): static
109109
{
110110
$this->hintIcon = $icon;
111-
$this->hintIconTooltip($tooltip);
111+
112+
if (func_num_args() >= 2) {
113+
$this->hintIconTooltip($tooltip);
114+
}
112115

113116
return $this;
114117
}

packages/panels/resources/views/livewire/sidebar.blade.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,12 @@ class="fi-sidebar-close-collapse-sidebar-btn"
7373

7474
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIDEBAR_LOGO_BEFORE) }}
7575

76-
<div x-show="$store.sidebar.isOpen" class="fi-sidebar-header-logo-ctn">
76+
<div
77+
@if ($isSidebarCollapsibleOnDesktop || $isSidebarFullyCollapsibleOnDesktop)
78+
x-show="$store.sidebar.isOpen"
79+
@endif
80+
class="fi-sidebar-header-logo-ctn"
81+
>
7782
@if ($homeUrl = filament()->getHomeUrl())
7883
<a {{ \Filament\Support\generate_href_html($homeUrl) }}>
7984
<x-filament-panels::logo />

packages/query-builder/resources/lang/vi/query-builder.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,50 @@
146146

147147
],
148148

149+
'unit_labels' => [
150+
'second' => 'Giây',
151+
'minute' => 'Phút',
152+
'hour' => 'Giờ',
153+
'day' => 'Ngày',
154+
'week' => 'Tuần',
155+
'month' => 'Tháng',
156+
'quarter' => 'Quý',
157+
'year' => 'Năm',
158+
],
159+
160+
'presets' => [
161+
'past_decade' => 'Thập kỷ trước',
162+
'past_5_years' => '5 năm qua',
163+
'past_2_years' => '2 năm qua',
164+
'past_year' => 'Năm qua',
165+
'past_6_months' => '6 tháng qua',
166+
'past_quarter' => 'Quý trước',
167+
'past_month' => 'Tháng trước',
168+
'past_2_weeks' => '2 tuần trước',
169+
'past_week' => 'Tuần trước',
170+
'past_hour' => 'Giờ qua',
171+
'past_minute' => 'Phút qua',
172+
'this_decade' => 'Thập kỷ này',
173+
'this_year' => 'Năm nay',
174+
'this_quarter' => 'Quý này',
175+
'this_month' => 'Tháng này',
176+
'today' => 'Hôm nay',
177+
'this_hour' => 'Giờ này',
178+
'this_minute' => 'Phút này',
179+
'next_minute' => 'Phút tới',
180+
'next_hour' => 'Giờ tới',
181+
'next_week' => 'Tuần tới',
182+
'next_2_weeks' => '2 tuần tới',
183+
'next_month' => 'Tháng tới',
184+
'next_quarter' => 'Quý tới',
185+
'next_6_months' => '6 tháng tới',
186+
'next_year' => 'Năm tới',
187+
'next_2_years' => '2 năm tới',
188+
'next_5_years' => '5 năm tới',
189+
'next_decade' => 'Thập kỷ tới',
190+
'custom' => 'Tùy chỉnh',
191+
],
192+
149193
'form' => [
150194

151195
'date' => [
@@ -160,6 +204,40 @@
160204
'label' => 'Năm',
161205
],
162206

207+
'mode' => [
208+
209+
'label' => 'Kiểu ngày',
210+
211+
'options' => [
212+
'absolute' => 'Ngày cụ thể',
213+
'relative' => 'Khung thời gian tương đối',
214+
],
215+
216+
],
217+
218+
'preset' => [
219+
'label' => 'Khoảng thời gian',
220+
],
221+
222+
'relative_value' => [
223+
'label' => 'Bao nhiêu',
224+
],
225+
226+
'relative_unit' => [
227+
'label' => 'Đơn vị thời gian',
228+
],
229+
230+
'tense' => [
231+
232+
'label' => 'Thì',
233+
234+
'options' => [
235+
'past' => 'Quá khứ',
236+
'future' => 'Tương lai',
237+
],
238+
239+
],
240+
163241
],
164242

165243
],

packages/schemas/src/Components/Concerns/HasState.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,25 @@ public function dehydrateState(array &$state, bool $isDehydrated = true): void
317317
$statePath = $this->getStatePath();
318318

319319
if (! $this->getRootContainer()->hasDehydratedComponent($statePath)) {
320+
// When another component in the same scope shares this
321+
// `statePath`, removing the entire key would destroy that
322+
// sibling's data. Instead, only remove the state paths
323+
// owned by *this* component's descendants.
324+
if ($this->hasComponentWithStatePath($statePath)) {
325+
$descendantStatePathsToForget = $this->getDescendantStatePathsToForget($statePath);
326+
327+
Arr::forget($state, $descendantStatePathsToForget); /** @phpstan-ignore parameterByRef.type */
328+
329+
// Clean up the parent key when nothing meaningful
330+
// remains (e.g. all siblings sharing this path were
331+
// also non-dehydrated and removed their descendants).
332+
if (blank(Arr::get($state, $statePath))) {
333+
Arr::forget($state, $statePath); /** @phpstan-ignore parameterByRef.type */
334+
}
335+
336+
return;
337+
}
338+
320339
Arr::forget($state, $statePath); /** @phpstan-ignore parameterByRef.type */
321340

322341
return;
@@ -347,6 +366,63 @@ public function dehydrateState(array &$state, bool $isDehydrated = true): void
347366
}
348367
}
349368

369+
/**
370+
* Check whether another component in the same schema scope shares the
371+
* given absolute `statePath`. The scope is determined by walking up
372+
* through parent containers that don't introduce their own `statePath`
373+
* (e.g. `Section`, `Tabs`) until a `statePath`-bearing ancestor is
374+
* found — that ancestor's container defines the boundary.
375+
*/
376+
protected function hasComponentWithStatePath(string $statePath): bool
377+
{
378+
$container = $this->getContainer();
379+
380+
while ($parentComponent = $container->getParentComponent()) {
381+
if ($parentComponent->hasStatePath()) {
382+
break;
383+
}
384+
385+
$container = $parentComponent->getContainer();
386+
}
387+
388+
foreach ($container->getFlatComponents(withActions: false, withHidden: true) as $component) {
389+
if ($component === $this) {
390+
continue;
391+
}
392+
393+
if ($component->hasStatePath() && $component->getStatePath() === $statePath) {
394+
return true;
395+
}
396+
}
397+
398+
return false;
399+
}
400+
401+
/**
402+
* @return array<string>
403+
*/
404+
protected function getDescendantStatePathsToForget(string $statePath): array
405+
{
406+
$descendantStatePathPrefix = "{$statePath}.";
407+
$paths = [];
408+
409+
foreach ($this->getChildSchemas(withHidden: true) as $childSchema) {
410+
foreach ($childSchema->getFlatComponents(withActions: false, withHidden: true) as $component) {
411+
if (! $component->hasStatePath()) {
412+
continue;
413+
}
414+
415+
$childStatePath = $component->getStatePath();
416+
417+
if (filled($childStatePath) && str_starts_with($childStatePath, $descendantStatePathPrefix)) {
418+
$paths[] = $childStatePath;
419+
}
420+
}
421+
}
422+
423+
return $paths;
424+
}
425+
350426
public function dehydrateStateUsing(?Closure $callback): static
351427
{
352428
$this->dehydrateStateUsing = $callback;

packages/support/resources/views/components/section/index.blade.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
@if ($hasHeader)
7070
<header
7171
@if ($collapsible)
72-
x-on:click="isCollapsed = ! isCollapsed"
72+
x-on:click="if (! $event.target.closest('.fi-section-header-after-ctn')) isCollapsed = ! isCollapsed"
7373
@endif
7474
class="fi-section-header"
7575
>
@@ -95,7 +95,7 @@ class="fi-section-header"
9595
@endif
9696

9797
@if (! is_slot_empty($afterHeader))
98-
<div x-on:click.stop class="fi-section-header-after-ctn">
98+
<div class="fi-section-header-after-ctn">
9999
{{ $afterHeader }}
100100
</div>
101101
@endif

packages/tables/resources/views/index.blade.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
$hasColumnsLayout = $hasColumnsLayout();
4646
$hasPageSummary = $hasPageSummary();
4747
$hasAllTableSummary = $hasAllTableSummary();
48-
$hasSummary = ($hasPageSummary || $hasAllTableSummary) && $hasSummary($this->getAllTableSummaryQuery());
48+
$hasSummary = $hasSummary($this->getAllTableSummaryQuery());
49+
$hasTopLevelSummary = $hasSummary && ($hasPageSummary || $hasAllTableSummary);
4950
$header = $getHeader();
5051
$headerActions = array_filter(
5152
$getHeaderActions(),
@@ -1300,7 +1301,7 @@ class="fi-ta-record-collapse-btn fi-icon-btn"
13001301
@endphp
13011302
@endforeach
13021303

1303-
@if ($hasSummary && (! $isReordering) && filled($previousRecordGroupTitle) && ((! $records instanceof \Illuminate\Contracts\Pagination\Paginator) || (! $records->hasMorePages())))
1304+
@if ($hasSummary && (! $isReordering) && filled($previousRecordGroupTitle) && $this->shouldRenderTrailingGroupedTableSummary($previousRecord))
13041305
<table class="fi-ta-table">
13051306
<tbody>
13061307
@php
@@ -1330,7 +1331,7 @@ class="fi-ta-record-collapse-btn fi-icon-btn"
13301331
}}
13311332
@endif
13321333

1333-
@if ($hasSummary && (! $isReordering))
1334+
@if ($hasTopLevelSummary && (! $isReordering))
13341335
<table class="fi-ta-table">
13351336
<tbody>
13361337
<x-filament-tables::summary
@@ -2352,7 +2353,7 @@ class="fi-ta-record-checkbox fi-checkbox-input"
23522353
@endphp
23532354
@endforeach
23542355

2355-
@if ($hasSummary && (! $isReordering) && filled($previousRecordGroupTitle) && ((! $records instanceof \Illuminate\Contracts\Pagination\Paginator) || (! $records->hasMorePages())))
2356+
@if ($hasSummary && (! $isReordering) && filled($previousRecordGroupTitle) && $this->shouldRenderTrailingGroupedTableSummary($previousRecord))
23562357
@php
23572358
$groupColumn = $group->getColumn();
23582359
$groupScopedAllTableSummaryQuery = $group->scopeQuery($this->getAllTableSummaryQuery(), $previousRecord);

packages/tables/src/Columns/Summarizers/Summarizer.php

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,23 +113,35 @@ function (EloquentBuilder $relatedQuery) use ($baseQuery, $query): EloquentBuild
113113
return $relatedQuery;
114114
},
115115
);
116-
} elseif ($query && str($attribute)->startsWith('pivot.')) {
116+
} elseif ($query) {
117117
// https://github.com/filamentphp/filament/issues/12501
118+
// Handle pivot columns in `BelongsToMany` context.
119+
// This handles two cases:
120+
// 1. Columns defined as `pivot.quantity` (direct pivot access)
121+
// 2. Columns defined as `quantity` in a `RelationManager` (implicit pivot column)
118122

119-
$pivotAttribute = (string) str($attribute)
120-
->after('pivot.')
121-
->prepend('pivot_');
123+
$pivotAttribute = str($attribute)->startsWith('pivot.')
124+
? (string) str($attribute)->after('pivot.')->prepend('pivot_')
125+
: 'pivot_' . $attribute;
122126

123127
$isPivotAttributeSelected = collect($query->getQuery()->getColumns())
124128
->contains(fn (string $column): bool => str($column)->endsWith(" as {$pivotAttribute}"));
125129

126-
$attribute = $isPivotAttributeSelected ? $pivotAttribute : $attribute;
127-
128-
// Avoid duplicate columns in the subquery by selecting pivot columns individually.
129130
if ($isPivotAttributeSelected) {
131+
$attribute = $pivotAttribute;
132+
}
133+
134+
// Remove the join table's wildcard to prevent duplicate column
135+
// errors (e.g., both tables have `id`) when the query is used
136+
// as a subquery in MySQL. This applies to all columns in a
137+
// `BelongsToMany` context, not just pivot columns.
138+
$hasPivotColumns = collect($query->getQuery()->getColumns())
139+
->contains(fn (string $column): bool => str($column)->contains(' as pivot_'));
140+
141+
if ($hasPivotColumns && ($joinTable = ($query->getQuery()->joins[0]->table ?? null))) {
130142
$query->getQuery()->columns = array_filter(
131143
$query->getQuery()->columns,
132-
fn (mixed $column): bool => $column !== "{$query->getQuery()->joins[0]->table}.*",
144+
fn (mixed $column): bool => ! is_string($column) || $column !== "{$joinTable}.*",
133145
);
134146
}
135147
}

0 commit comments

Comments
 (0)