From 507267e2716ee083597ba7ee0cb7cc32d679c304 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Tue, 18 Jul 2023 14:31:18 +0100 Subject: [PATCH] wip --- .../app/Livewire/Infolists/EntriesDemo.php | 79 +++++++ docs-assets/app/app/Livewire/TablesDemo.php | 85 +++++++ docs-assets/app/app/Models/User.php | 1 + docs-assets/app/database/database.sqlite | Bin 90112 -> 90112 bytes .../2014_10_12_000000_create_users_table.php | 1 + docs-assets/screenshots/schema.js | 72 ++++++ .../forms/src/Components/CheckboxList.php | 15 ++ .../infolists/docs/03-entries/04-image.md | 162 ++++++++++--- .../views/components/image-entry.blade.php | 137 +++++++---- .../infolists/src/Components/ImageEntry.php | 115 +++++++++- .../SpatieMediaLibraryImageEntry.php | 38 ++-- .../Columns/SpatieMediaLibraryImageColumn.php | 16 +- packages/tables/docs/03-columns/04-image.md | 212 ++++++++++-------- .../views/columns/image-column.blade.php | 4 +- 14 files changed, 736 insertions(+), 201 deletions(-) diff --git a/docs-assets/app/app/Livewire/Infolists/EntriesDemo.php b/docs-assets/app/app/Livewire/Infolists/EntriesDemo.php index 7fa45b35d8..02534fb702 100644 --- a/docs-assets/app/app/Livewire/Infolists/EntriesDemo.php +++ b/docs-assets/app/app/Livewire/Infolists/EntriesDemo.php @@ -328,6 +328,7 @@ class EntriesDemo extends Component implements HasInfolists ]) ->schema([ ImageEntry::make('author.avatar') + ->height(40) ->state('https://picsum.photos/id/177/1200/800') ->square(), ]), @@ -338,9 +339,87 @@ class EntriesDemo extends Component implements HasInfolists ]) ->schema([ ImageEntry::make('author.avatar') + ->height(40) ->state('https://picsum.photos/id/433/1200/800') ->circular(), ]), + Group::make() + ->id('imageStacked') + ->extraAttributes([ + 'class' => 'p-16 max-w-3xl', + ]) + ->schema([ + ImageEntry::make('colleagues') + ->height(40) + ->state([ + 'https://avatars.githubusercontent.com/u/41837763?v=4', + 'https://avatars.githubusercontent.com/u/44533235?v=4', + 'https://avatars.githubusercontent.com/u/22632550?v=4', + 'https://avatars.githubusercontent.com/u/3596800?v=4', + 'https://avatars.githubusercontent.com/u/881938?v=4', + ]) + ->circular() + ->stacked(), + ]), + Group::make() + ->id('imageLimited') + ->extraAttributes([ + 'class' => 'p-16 max-w-3xl', + ]) + ->schema([ + ImageEntry::make('colleagues') + ->height(40) + ->state([ + 'https://avatars.githubusercontent.com/u/41837763?v=4', + 'https://avatars.githubusercontent.com/u/44533235?v=4', + 'https://avatars.githubusercontent.com/u/22632550?v=4', + 'https://avatars.githubusercontent.com/u/3596800?v=4', + 'https://avatars.githubusercontent.com/u/881938?v=4', + ]) + ->circular() + ->stacked() + ->limit(3), + ]), + Group::make() + ->id('imageLimitedRemainingText') + ->extraAttributes([ + 'class' => 'p-16 max-w-3xl', + ]) + ->schema([ + ImageEntry::make('colleagues') + ->height(40) + ->state([ + 'https://avatars.githubusercontent.com/u/41837763?v=4', + 'https://avatars.githubusercontent.com/u/44533235?v=4', + 'https://avatars.githubusercontent.com/u/22632550?v=4', + 'https://avatars.githubusercontent.com/u/3596800?v=4', + 'https://avatars.githubusercontent.com/u/881938?v=4', + ]) + ->circular() + ->stacked() + ->limit(3) + ->limitedRemainingText(), + ]), + Group::make() + ->id('imageLimitedRemainingTextSeparately') + ->extraAttributes([ + 'class' => 'p-16 max-w-3xl', + ]) + ->schema([ + ImageEntry::make('colleagues') + ->height(40) + ->state([ + 'https://avatars.githubusercontent.com/u/41837763?v=4', + 'https://avatars.githubusercontent.com/u/44533235?v=4', + 'https://avatars.githubusercontent.com/u/22632550?v=4', + 'https://avatars.githubusercontent.com/u/3596800?v=4', + 'https://avatars.githubusercontent.com/u/881938?v=4', + ]) + ->circular() + ->stacked() + ->limit(3) + ->limitedRemainingText(isSeparate: true), + ]), Group::make() ->id('color') ->extraAttributes([ diff --git a/docs-assets/app/app/Livewire/TablesDemo.php b/docs-assets/app/app/Livewire/TablesDemo.php index ea0ce24e52..05eeb6da30 100644 --- a/docs-assets/app/app/Livewire/TablesDemo.php +++ b/docs-assets/app/app/Livewire/TablesDemo.php @@ -498,6 +498,55 @@ class TablesDemo extends Component implements HasForms, HasTable ]); } + public function imageColumnStacked(Table $table): Table + { + return $this->usersTable($table) + ->columns([ + TextColumn::make('name'), + ImageColumn::make('colleagues') + ->circular() + ->stacked(), + ]); + } + + public function imageColumnLimited(Table $table): Table + { + return $this->usersTable($table) + ->columns([ + TextColumn::make('name'), + ImageColumn::make('avatars') + ->circular() + ->stacked() + ->limit(3), + ]); + } + + public function imageColumnLimitedRemainingText(Table $table): Table + { + return $this->usersTable($table) + ->columns([ + TextColumn::make('name'), + ImageColumn::make('avatars') + ->circular() + ->stacked() + ->limit(3) + ->limitedRemainingText(), + ]); + } + + public function imageColumnLimitedRemainingTextSeparately(Table $table): Table + { + return $this->usersTable($table) + ->columns([ + TextColumn::make('name'), + ImageColumn::make('avatars') + ->circular() + ->stacked() + ->limit(3) + ->limitedRemainingText(isSeparate: true), + ]); + } + public function colorColumn(Table $table): Table { return $this->postsTable($table) @@ -1338,6 +1387,13 @@ class TablesDemo extends Component implements HasForms, HasTable 'avatar' => 'https://avatars.githubusercontent.com/u/41773797?v=4', 'phone' => '+1 (555) 555-5555', 'job' => 'Developer', + 'colleagues' => json_encode([ + 'https://avatars.githubusercontent.com/u/41837763?v=4', + 'https://avatars.githubusercontent.com/u/44533235?v=4', + 'https://avatars.githubusercontent.com/u/22632550?v=4', + 'https://avatars.githubusercontent.com/u/3596800?v=4', + 'https://avatars.githubusercontent.com/u/881938?v=4', + ]), ], [ 'name' => 'Ryan Chandler', @@ -1347,6 +1403,13 @@ class TablesDemo extends Component implements HasForms, HasTable 'avatar' => 'https://avatars.githubusercontent.com/u/41837763?v=4', 'phone' => '+1 (555) 555-5555', 'job' => 'Developer', + 'colleagues' => json_encode([ + 'https://avatars.githubusercontent.com/u/41773797?v=4', + 'https://avatars.githubusercontent.com/u/44533235?v=4', + 'https://avatars.githubusercontent.com/u/22632550?v=4', + 'https://avatars.githubusercontent.com/u/3596800?v=4', + 'https://avatars.githubusercontent.com/u/881938?v=4', + ]), ], [ 'name' => 'Zep Fietje', @@ -1356,6 +1419,12 @@ class TablesDemo extends Component implements HasForms, HasTable 'avatar' => 'https://avatars.githubusercontent.com/u/44533235?v=4', 'phone' => '+1 (555) 555-5555', 'job' => 'Developer', + 'colleagues' => json_encode([ + 'https://avatars.githubusercontent.com/u/41773797?v=4', + 'https://avatars.githubusercontent.com/u/41837763?v=4', + 'https://avatars.githubusercontent.com/u/22632550?v=4', + 'https://avatars.githubusercontent.com/u/3596800?v=4', + ]), ], [ 'name' => 'Dennis Koch', @@ -1365,6 +1434,12 @@ class TablesDemo extends Component implements HasForms, HasTable 'avatar' => 'https://avatars.githubusercontent.com/u/22632550?v=4', 'phone' => '+1 (555) 555-5555', 'job' => 'Developer', + 'colleagues' => json_encode([ + 'https://avatars.githubusercontent.com/u/41773797?v=4', + 'https://avatars.githubusercontent.com/u/41837763?v=4', + 'https://avatars.githubusercontent.com/u/44533235?v=4', + 'https://avatars.githubusercontent.com/u/3596800?v=4', + ]), ], [ 'name' => 'Adam Weston', @@ -1374,6 +1449,12 @@ class TablesDemo extends Component implements HasForms, HasTable 'avatar' => 'https://avatars.githubusercontent.com/u/3596800?v=4', 'phone' => '+1 (555) 555-5555', 'job' => 'Developer', + 'colleagues' => json_encode([ + 'https://avatars.githubusercontent.com/u/41773797?v=4', + 'https://avatars.githubusercontent.com/u/41837763?v=4', + 'https://avatars.githubusercontent.com/u/44533235?v=4', + 'https://avatars.githubusercontent.com/u/22632550?v=4', + ]), ], [ 'name' => 'Ryan Scherler', @@ -1383,6 +1464,10 @@ class TablesDemo extends Component implements HasForms, HasTable 'avatar' => 'https://avatars.githubusercontent.com/u/881938?v=4', 'phone' => '+1 (555) 555-5555', 'job' => 'Developer', + 'colleagues' => json_encode([ + 'https://avatars.githubusercontent.com/u/41773797?v=4', + 'https://avatars.githubusercontent.com/u/41837763?v=4', + ]), ], ]); User::factory()->count(45)->create(); diff --git a/docs-assets/app/app/Models/User.php b/docs-assets/app/app/Models/User.php index fbd1b9dc2e..e1d34a33bb 100644 --- a/docs-assets/app/app/Models/User.php +++ b/docs-assets/app/app/Models/User.php @@ -42,6 +42,7 @@ class User extends Authenticatable implements FilamentUser * @var array */ protected $casts = [ + 'colleagues' => 'array', 'email_verified_at' => 'datetime', ]; diff --git a/docs-assets/app/database/database.sqlite b/docs-assets/app/database/database.sqlite index a348e87ba0e45a8e668460de09ce3ad0d76beeaf..6710be4d47c7d026684aabbb91bbc22550434c7d 100644 GIT binary patch literal 90112 zcmeHwZEPD!njWd|PtvoqW6rF{tF~;ft?{t=C5n{2bG9tWvSdrPCHZS-2HGN7BwK2B z)BPct-s~NEW)HYaa!7Kx!y<cOZdxY)bz*UId0~55 z+FrPRds&+1Wz*8dX{9nPDVjxV)R3Bn(jZ1l+M_Lr*p{wnWrH@TW=Wck|7=x#Uz)~+ zx^CY-3EcdC?Fwn5RHrD!cQc`-ACE2e7Krloyilc0Dovltsr=og~ScNMKdp8^x5s+1v^u9-XT zH-`C+)oRkAMy%Ge0?$SicaBaNU_tR7BSuw^u;rX{kD4f%Lfx^l6y};$*N+TmE1$^e-cjRf(-y(&GKi&E<{lo$b~2<*n_7^}939 zFANSR3Wew=r`*m#O!GiDDm&0Twc38F8CE#d4PWRz3`p95+Cy#eUT$JaSl7b%zq7Z!|K-yx_}@MFCC@5)H%FdA?`cwQv;)#7yW^US*R|_KBBVW zUxE*A)^)Apolt;&AG}`HRh5#O9dJc#sM#n{!!^6En|mMX&D`x9N?f`WeawMvM{Sd$ zr?D}f8^pb@{as)`ckv=a@O|7m|#=t$8u(0^rx|eiBa*xp%Vpe}DW>Bk}(l|I_%t zdmWT3WE2Pp1Ox&C0fB%(Kp-Fx5C{ka1Ofs9fq+1ugTQEiew)H1~Gzq>VfG@}<_gB0r|H`YnB#wSG#pao%*A4nOA8&b*9}va7F41U=`l816BREZ*LCce zR?2%vDERDZb#ZxZbwPO$B4tRClbZzIiygrsa|=RWC)P6+yPSwzB6S1%tB(}!+~ULL zR(kuEu7-#hQsi8kh(*q4I#O>RC@PV*nu@kZYI`=eF8Uak^+rewpT{_E?^PC73g2qo z*a~Slq{zvR!`?sWNV`E*Z2FZpsiLYa6?@$4@-8LV4Co7e_!hli&>rvaE;Z~YArgiZ zIhV$|A-<#5h;&P*DnKr4gwt~!A>t3+x@%D`S1;-UQt%*-E8w9%<{sdpBsVn`RAoyp&H#+=suNh61%+D zfVC*JFzXwMZqz~=`#hGd+_0`!l}s_S8`5w{ks}YovIByz=xPO4!cJ6BqXs9fYnkE^ z3cP)1ZEdA!Uw>K;kuId@lh_ahF62%>NliK5L9p6v5sgJ2YN&`V_+nnG!j-U-EzNpU z_HCn>OlPD_=5j85Ii2>B@^#B)F|45QbGuZC~YrgTH0)-HWUo6oD@Rh__Wz*=5!yn~kWiq5AC?@!elY(1$={p!)%cI%yAyvmmLLB|W_ zh6!Qe&~pikF*4@E8VO#$$SUsYbq$LMb7}tyq`$zfSXfW6RmJRpi7Ve=(jm=hnpE{` zK#229I*nL$s?Fwu7tS$!_YE6)%wX}^v;DWCb+-JbA<2t{iwc_pcI$JRwTT=ma-g|v zu=1Hx{a2!fU1`Y&wgxHvYYn7UieeBUNDBl1+=&yVeZftcLDP^jD@W`okMYHu@Ss!dXXh1P=YCD_wyWM9$b8dB(5w`rYZ zPc!sVsn|*WL&Ue3nUtVX*!qD&YcN*7O(6PJcDan+Mh%(Tuz00oMY#MXyNpD)0iGiH z8EvSfvo48O`3k#iDoxf+bORls+_3}HeuH%cwjkS*_o?0KnGoxQ)W6Ixy=<*E&Y%3! z710<2@iW#QCm9+qF7iz^Kp-Fx5C{ka1Ofs9fq+0jARrJB2nYne{0NBk|1ZCe z#TyC)1Ofs9fq+0jARrJB2nYlO0s;YnfIvXZ|Ah?*1Ox&C0fB%(Kp-Fx5C{ka1Ofs9 zfq+2Z%a4HA|NrIJv3Ns)fIvVXAP^7;2m}NI0s(=5KtLcM5D*B6^?zXl0s(=5KtLcM z5D*9m1Ox&C0fB%(Kp-Fx`0^tl=Ko)Q9g8;<2nYlO0s;YnfIvVXAP^7;2m}NI0s(=* zARy?A|C9l7GXCfBpHBRziQM>K8vF6s_0iuN`N_!T;lDTZ--k8^e>m`G18e=i-uJKj zrlWr)^3RVmZqDVzcqBUdB=McA(OfP;728Y_qg+=kTDJ70-p~{?sWh6jqF6~{TeOkX z?8XzDG?EQPQyQdVkg9cql&L`_YDtQ=Pc=)&m(Hgrwn8NQlG^P^7S*U_8#2Yc3M!#F zY7k4JPs_B43(9CVoy{fFg=D5EWwMv^*~^)1NRe~mB!nN?nYx;R@Fz9e(32HfQY^^K z9-1ucnn}?*+DtYLLQTk~*o~%LQWdjKEBEkV{&Yzr4M`;>-B5IsG-UVPAZVkh8$`m3 zkA*xxq{#W^TTGu;uR!imr+@0!Jmmh0-h>L1q{M_wHdXW|V!<@p zoe5y;X0k1<3p4;H%qoE%(P7ffHrj# z#%`NZg{p8r_&_b0-Ba$o8QvHsRw0dE1%LYl6WpF!JqwFI0lUyBIw0?G<+5!O?h%qo zB?;41l`?z)Yz6=&VGiiH5Y9K52dp1RCSKJdk`41^j=^;sz?Z)QvvWlYQFus^GaYB0 z?c*sW2~6X}WVQ>}$t{&X-ZSB*T~P?S2~7fxj=tOKB>>UuNG^~Sr3?u>jsveEyPXT( z*0lrDtD&Z2OvqY7S%Hu-u56NoRq4LYft72%95IGmyR&euZ`pvm3EVWLuBejIM3?8d zvF_F+=&j5W+MZSp7?x)${5tQU!yzDs6gh89GL>IV)N&Ac(lbpaCKH?D1G)||xp?;h z2mFqd*lGW z4WpFY$lQtVG8~aVrU2@_#{0quQ@1lYRc7*x;PC-JSW@A1&>0|7$MW5AfTATCQpt9| zeIXWYF@H{=1;s^_+e+K@AOyM2i1$Ld41d7hwCz}X_3P({8IXI453fM*VRYASrvxwB zc66PANoUmMqRL!&d#J@6xl-}*G}#xoGs(b&pqm8;TFig-DrbI(2l{BDR)C;G?pVr= zFdV`57|U}!0cqWvnuflwR4{PmGT)_!*+dw}h|iSJ7V|?a=J+VVA>47garqPn6<3%n(HgC4wC!@qn@`e*Ft51f+&Or*0@&Z)-`X#kIF&}0om zAT-+^a^A+^Mw9CL0Za_T2Gi0T$}{RWe!-OPuSy4sRY&mV^#GTN{y!f1?MQ5C;``$lMt?AJYItnu@!$^zeljrI|4V&8 z?mHDN;qr0#ac1MO$l2wb!*UjpGr8PN(t;O}7IjVArI^E1WV2k?DC<1e=u^^Ys#M0b zJQ%ZbmotUS`SiJL>s%&%Za%yE=>A4?Z}(2xxN|RGYOO2sV>+wf(w9kYQ=eDW#gweq zQpC#2cAY+4Nhvku@pbK>SiSLJ`EE$VAw|wyEEc)6&=GsxFlA{0frv1v3GAw|xmiCE-(rX%(C0lJm6)l{@SQrojtV)__kHW|{w=P{1kdzFQi!nayC zwn7>XDRQ#ovB-rFI?`@X)kN^yq>8Gxkm)GbX!bjn0=%#JPHvlq{z848jBRJb|AiM=v7$)&@hXK zw1g z(^Q2PW_=^kjao=!pU1M58`kxzk|}0(LmCb#a^zuHc0lkIU9G@M05Kd!4Nh9uGQ}el zc>B)U+Dg&B{ObfZ$YtfqN6l_LAZB5?Rfd)94CA2H_EHDrq|RzGE@ip9)) zu6T9-gV{?N>0&;gpONq@iU0CTP_L>ZgYx0D+uBE|k3PG-k5cwWso6}SkSoj=Sew)D zzhc#5u27iE@v0wx+BXRQpU<5(Q&XST0LTJzROm4nfufI-#u1>A%g@gh)9K>~$15T^ zo6qI4x%|(dglu*$m(Axn2-6>bIxqkw%;!$qPy&S#mZ*jW40zje9peUkJOVWYML2F| z`E%1lz!s-J{-n_#!;mxLtKnO;iNQ3rcIh+Pd|m~w>I7y3*7ADe9kiTRbUs~pfBIe@ ziyj>B?>+RK@nRkGp0+do+?Z!NzV0|+9x#Dd1n!V9f3=R)iCged^HJ70xR2t>T0-Ew zNes*lkXwFUQEuq{TtPD!;to%D9ea311q8eAD^~r&b;suaCnLWQiEqTdKl!gFXC|uS zzccn{V>d^CZRAfz-W{$F{m#(f;M%~i_5Wf2>AnZiza9B+xY_@4(&uB5WUezhT{2{8 z&7GESH8AAC=m~jH)gR{hgC=(&{1B$&`Ms_DlgH#~x!37{e6D+3dR@or2$62;6ys8j zATOoO=8sU|>OxU3-d@d=(;MUEMvBihF zTwxwV*L>&?e-87|EUg#q?fs2>h|D2HPU=i7G834?+{T&_^w8Eq$R#Wg2NODnZ#8TA+1>nV>B-7`h@2rs zj(i$+9&qDZO3gMZe6A<0FR6N~Ow&2vP!3<<%#9VB)SuXMH$$WgDRNTpVpbF|^V{|_ zHdRN+u~ejkTe4}<`cYnJxp)ddWd)-MNZ}va%KVRSSV3U;!KkzpZ1UoL7Gp4 zc^qYOQeAnpQCoVrQSLD#;uO9Xi=+aXv+D?u=#draE;c!suwta$E6AFWx=Aq5^K$No z-xsY$Wo4yUpDQecKpRrz%)W!HSs-S*IX`!84r}-;DqG#7-KomM7rT^cHfs9n#=~Za zj3Gr%@oo6OK*s$h(e|X}YLyz+Gil9`v6Y79h2<788Z*2y0?+(W6`l(ClrV? zylZ4%(c~I3ms((vpFCr%`FkqBdYQ^irwV zNd_OjzusJYQnR=7^OZ2Wnx$qn}2PLY87&X zLf_HW8V^>?Eu$17VMviP_m%Fo$7SpVBS0Jz2o`v-p~1igLHQYNsHC&@c=!fhov&ys zi;cN^n;}w$6gjhRV3rm@`?g(f`SH?Jn$*PhKf_?FHb;o}{HDF~Y_U))^*WGBoj{bI z?+jqEPl7qY>sX|zvo-3LypPR#zQl*$GwpS?YOR)cS9T#W4}51ZzjJn zVU7RZczkSo^skNl%aIGi+RzViQTzx51Ofs9fxzpFfb-29GDDbvI(dAUL1>-BzkFYk z^FBVE4^0188^|w8w^Y5nr#DI%+#G3Ej$B_UmC}vv2e(580wG0CF^j=uVDQTmEgKkz zVFbKsYr0xBS(b4L z7HN6etf;xta&EgorGRTCRb}oYkFN{Sl_KLX$JtldW169 z_n%+~7kRjSFT^N9ikw^uBlf^-dO>aRy;Yc)Vv>Y&dN1y}JA4x4y0*PqCzx%pVKBCs;Dn72{NYcPmN&I08%ullW=V*= zAw^CxiM&x@@V>4f&vl(BDy8mFxTYgdr_d5oN7#;kgrXnpKf1R!yS2OD>t@Z=`!5XO z(G{g-7>rfJ)y`l7)=1;xXohw>2S|;|vN{f6Rjg3|+ZOwP~h8_MasXV*3dGpERdTu4ed_#(y z%QNVdh0eUr%_pv5BNSVUla?DSD~f~>+ufyMXJN~S`|v%A*0?dZzf`hXy)Gc8F2*9K z1Kn|<(ZcdPsaZI_iUTn%8Lyyq(iVp=aoRFknS0yS&5aOjLyDaI1*GNzaC0BHMGT_& zOF_GCG6X6OIo zIR78<&i@~WvH@b_~QJ3#5@0gY>)qEopGH1k9g<*zv$!tS!WLC|0DkS|JUdE zKNODh{}J!}|FJ#(j}C?N{|NN$^Z&2Q@&Bw7$NB$=fByd%9RL3V=PjK7kND^R`SJbZ zaQr{wBys*f;-CLN_Q(IL&J51~NBr~u$NKocd;UN21)l%6u3nRJ{D;l|-;Vr7BL3~z z4`U;fs}p}~;>7r082c|{-yZ!ZqwkD78UCNc9}WHH;Qtt08Tj3SQ~gTc@AoC5cOt)W zgc$(GDW;h5IfIEBV?sgtkCFc{${u9aKv@PtKTay+k&3ybASNTgY4h-bGtXt`;bax- zob%1+wPAE@jJ1RFAz||lW6S~(6=OgF&k(j}NNnCMb@QVP1k*jA!QZzeGtu{^oGHeD z<&D%p-NG1fKoj8-pLpalffB~a;uAkCd&FH`j8QHNR~j70$McJNWu8z3f87nbou3&p?3KDIHQ{)QbZJ*zDKt5(roJ9he0wa0BUN^8(DiUlMU${e**;tz!MB5Se$dHwF zPG4Z)(FAA6G70=@kVnkZunUAwjtc5dml~B5VX;J-;&~sR<&;r11)PPo25~tAg8{&uZ+5&lIK0}tpp5vi?L3%&YIh$HBJoD>?^C4#Vpov< z_@;*(03&e<0&ZKf&z=YhJYlWqdo=C4~(8?LfYNjb$lJR zr;Rq1BUd!M4|AsyH~_FAnNxmpT34WqfCvd0l`8wMO{dzc0kqCB{jVenDL8i?TLWZh ztXDFh$?c7!w}oe5)q=C(dZcaa*{p+iD)#EB&z)tUkf}2BgRVPfv5^zK(r#DCcnv*Y z?strPaKL_C1s5w^JaaIf{!N$LjtK`IHt?k~2>l9nqpw#v&rUNrzdN-!50Er)&(b8e zvO^+QWds(7L5VL3$k{lNER&ZJeY%W-enh{^NLC>V7!=xMrEHABwCl2LcIW{?l*K4I zdlmEDGfYf->dtw<$#n-O9q>PHNAb|1$v|{-CH8?`Y{$tqp(W#BW3R|9kqg=;XX85S zksJ`&0g1_XHSr-+w>wvafS?cdxa>IlAGfmyoQ%{LwPV+u56E6GL^}mvttq4rGD($?8ixF#@sFkF#x;$o00so`U}ea_HV?cXY>03FmEK*o@;3-v)cN?hEfS zY3Njgc~F-hy0fq9tATin)r0{SWPV$_5P7qxkh`bKR*6Jh2>@x$uz!_gKAJ@Vjp_ z3tpc3kg>Q10y&>7aaLA$NYEKNFYw_#Uc)1(Wex|7$c$PZ;iDaXg5<4%;L$b)v)3NR z=KuY%H~Zo%v40SI^9X14qG^GEKtLcM5D*9m1Ox&C0fB%(Kp-FxIA#bqP9n>utXqkX zSt`Iyx^!|3?Oc$b^2Z+l<4K@Rcb%g>d-l?5JV)#;#=1FK_6N+`e-p+1YhcI;^q8e^ zPG)#IDDfdnc4F1XOAdDuDfp988k9?N9#OOb!m6 zJ@K1^R|b~)|8d`k{pI5h4vQbbfMDQ7Vc>HpYbupW{M@THD`~u8ShnT;d1e0Y;@sw< zyg7G$bx}U=m7SL_pI6K0WmUI%g`0B2RO`%a$~E4UnPVHOu9&>ebz9aA{5qQUfqWhZ zs)o7Mb{z^;YipY`+c7zAXRCS>x4fJi#}P|&D(B@rW-3)?(svuDS?widZYiePuvJ6H zwfw-wS+;6xy!|AT;jXw*3o1?lG-mVi7PHUGWqh*LI!Ce7pf87uVJNv;(do@Dx4pr* zZ1Qbxa$VsTF15I6o%bjimknJmbB)8uTHJs4ad&>CKV@7>xZhRvGCu$(>b9x?n4w!+ z{x{-$%WgJ!`**dx_2dPkt@>ELwXpb5KL3?(cAgM+UPje<2*!CZ(pH2@8~^;h;r`U6 zONs9dc@om`;EpfJPKoI_7D!I3Ufbn^-af3k_5*D=x;dv!z}oME+GW0&w3k8_4p;eC z6OeY%_a$I_Hrbb2yOfZ;y0dmQG`L&7R-p1*^&8ZFu7jNs_uZlX)W&SWUGXk%K&}j( zXJ{{Ga33yJTgyc%yir|siFXWh0P`CC<}_vE0NYqv~_q3c|U8n^ai z(0d0}NLA5Z++a=3VAPNvI=~MU-qudGU(>?h-WZ05(U5I3N3-Smdw1_H-rC&STwYt; z*qmFtJ??(%cz>!`Onm;9uN=&>_6@VVg_`Gf%T6`o3i~?gi#>;kq~vR_%tIO3u`METilN>+yHgsSZ{wSmrA((p4fVwf-UcHEs6C?j>pRC zlPCLP>Xk=yjDNn^Q~lF-1KFv{YZT6$M);SZy)PRUAdBYSjE7UEb~3+}EpBBew{lb2>B+*DKO?Z^Em>`O!}9F$ zUWs>{IXPLJnhDPAn2h(YnC(n>qB)pE=emIduNiRxay3&AT8tKbT1Am}B#=yqxd}2(RpXk~bUVpGo8Yp3y&* zM*m{;Pe=dkn^3s|qhLTVAQ%t~2nGZLf&syRU_dY+7!V8y1_T2U1A{$1sf0JxF*(?a zkKUj^Ka%JwV-WMTLRLw`P0Jn;*Ie>nK|fY$%RzMuA8 zJO0bPKkAixs>gmO@z;q(>F1-TJo(8jo=!?`4u2*JdH1;c0Y06Z#V1K}r&38N{gL-s zI+xGihIfz#7vbyNbg$x2VJ&3ivzj(PkufS0%%03RRsLXULanGX`>RYZP2H|7$J{uk$SvR8v^Rz9`(9Yv1;8B^qb_*zoRFNU~DFV`5_93{u|ZDjZ>}T+5W0 zQ!g>48lpdX*4OpzX(hKhd1pQbYD|$k@hVu41bnV+=&B`eFdcsUWkf0%0jeR}4*`ntYCUOEYHGR5u^5IWgd3 zirlGD82S%8Wvlw=p@VY<6o!rbl4;qUL6a znebKA%JRISfo8~ag%D@zD!%d%_|eP9&LW#DXJ_{wFUF7?Q{>JjlhVhL`CEWK-Zd<) zHFbHOnfAV^R;sqVe4kYuyMy$j$8Jwox0TKPnVJy;G^WT+4|mEo+f#Lw$!iAJx9ch# z6uG8_TpzuV>}(VdR@Vm|mrk9P7?4w71`+D|MrM@zK z_f`zEF-7i`!K8F47aEdH$3!PjzRThDZtB%0v+WS=(Tm1rF3)Dv>~fX&mfoxL2S>F<=Z9p@Zn@(^-;Ht;ij< z#24$@wyMG5TDKe3MkefT%%rn9IhVVd&tA=DgH_V1Z8xl|6BE9Nxs|D?cGW4-pa_8# zHf}-JPdF1ZGr8IP%*T5lO!mkWi$xQst({1z7Y9@h-4@VY0D4@(yc`LmXXYp%@d z@&j(!2+hLB=Kv~Sn4O-0Y3k&7XBVR144?^mSGYrCRnrJ_lTrlhgUhLg7n* z{cO)LMnzH!*QdN8-%z=|!yog;VN74Owx*^E=!54AU&2h!G00INK;X&Id=&<;B}dPU zA~%yS7N_%Hg5tFV@etMzg!v{jO$3K9{V)Pwg`Y`~r*g$&zBpTq0|jL|mByb}Qz!5@ ze_~e}{ocrL5B+xXe-58X&JF#$p@kuP=*sB-8vViWFD3us@O1L>Xny41k9;)rOC!4@ z-yeB9`B%fgKYVL+<-|W2`R~c!PyR6ZVC1icjT1i}c`z&`M@Qct`X57|o!A}z=c9i# zx-zPkrpjG2m2Jyu-c^*w-;0C!j zdu9{+hPIu-G{@s;EQ_&SmM+yWHbM~uWXsnsR+=EKfCE-;#dWnwWQ6|fj z4X1)M4dGE>d5=J9NW{a<$?5O_9`^3rJs%~QQ#N@sQ*)}S5+1}=7YPZ+J@i2Gs7?g zphs-u=-9cQrNmD7Xe1V5irOI>?_B6vNq`}ptEk+J#vBt1MV)s!OLWwmI8ZNktRB5h z2e!GYQN4t(jOV^}s%IjhFsF@ll)g!5YFH-GAjMQIbz}gFss*olhA6}YZ4)ose!n+} zp{Fl&rU*yeO5$k}s@V0qQ*=q(irDKU@L#aF%w6}$(Y5@-SiTOnE8_)x)(j=@Pxv#(>?jYn284R|3}h4@go=z z3Q`a z1_T3w0l|P^KrkQ}5DW+g1OtKr!GK^utp5uc5DW+g1OtKr!GK^uFd!HZ3sY*@U_dY+7!V8y1_T3w0l|P^KrkQ}5DW+g#Q0yxfM7r{AQ%t~2nGZL zf&syRU_dY+7!V8y23~#!g#Z7`U&rDN1p|Ts!GK^uFd!HZ3Rh72nGZLf&syRU_dY+ z7!V8y1_T3wf#aay*yvA)h}TB{a`Y#|KOTNG^v6RNPJCzZ4+q~L_{IJ|>A%|7IR0Ob zFZcdd&;RZDJI8)4@qZHElKzSGtE0asNrT@`?S7QV=hcSA(z<)bJ#}VCN(?Ngl;U7M ze`edMsLW1tJFV6mysUa34MkyGVYXT}*|vShQJKu#*WbW(gDYd7<#4w*SRI$RW8S^C z+%t4+U}cOI=-94fSZS`Q6|R@n^e!$>JDSGo>20QP6aVe0d|%Zoo4npIOts`#vc++l ztQe;0G;CGYSjjM*x~x0(U5C{(+_GDz>=_!)U^cHGpUh6?)7fG=o0oIhtGSu0D0s5S z&87%jbL`$6u%%4Am7(+W9@kYMvQ)kEjnkZ^gJ(GPfRqsO9fxt;LU!~rmvvq@WNyp+ zfSZa6P;}>d222Bbi%z_gPA})^wqy3k;EO48FP|pJ#@L;Y0r@nTNi$Qasy0__gZM1- zdRpg}qT;S;R!Wx*%`j1B(Rau0Kr8ENRn=t9WdAHn26v#H$+%Ib9i1Fh86D}1!4*^F zo;^iUF*mk53u!tPFb6Cxm})s)XBAxyF)bd10D`kU3E*tk;W8_$4HYjYD^5d&G@_z2 zxRcB@m2T(@hH6->9s?$($j!e_RPr%{YX9{>z|sxV*aMBA%V&ci_>x*TG)S9@Y7WHo zj-y(#QC1C~LDVgCAOSYfWPw=1%~4wB8Y;-KGCEhdWqATYvfZj38Mi$V12Lw^ojOUV zzdQDbP@nXK)TryK<=1ptH!K_Kk!~;(%IlbZF$$oQ%m$uxSygP-a7vnLRe2e2z+@;M zK$1GiBqz|PvK+oF#5fbln9MAQFtitX?|cPSJNp_5!`c}85U^isn2cMXSOqZM&_OS= zAju65(9o=M2yYSE4K)hnl9U~0iOF?U2Yr4q2Y%RrmQnHej%u!3TKiumd>^Nl<{+%E zI(AwIkd_o$zV@h&$y-*>D9gmW3}rD5kFMa!~d= z&98*^HVwLAlkm&SX-_v5o1=m~M01+TEL05$)T!2qk-)ro-^;wD+NzaN$ZGPAL#kuh zMsEy~m?C#+bQs|Hm^(&FJsK(vfy2YMp&BskRvHpcvPM{;3Z5+umFAuu@MDplz+%av z&dGbIM;qp|Lrq$y$a@n_Epc!TsqwVG)4-A<#lVg!a^D&unpaZ$S3vWKZP=)9(qxb4 zNZ!#J0FX~|XlO|!Nk;ZQtk^A;A5vq)>Q$s%tb7P>nHvDE3#M)`k$^<%Pqn-8JYbz8C zQg*)|Aam%I*)orQV+wr?nj}vSAOJFl>~`B4ogZq|_blKI1@F#%yO>sWuik@})2r`A zpQ#S!_yH7JYj`ar^IFc9We2C!YsipT1X>Z<%40Ev#T2>YC#c$;u@y4zCrEQ3(CMJ{ zNOO8f$Sf7>&}!d0DMq~xnnTNp?g)gCmB1cpJnltyUR6|2K{F&akW8n{JS!Y%BqX}q zp2)j{MCjt!cZkrzpmk0IlGnlqJr6uQ#56}O2GK)(yP$mxs{%x$b=4I}jSO32H2@YJ zCo2P)P|;eE&bOU27(-x8k((VLoS*qP2fP+H?Hdu#G&KxT3N-P)3E=3T;yIyA-fsyh zs=ZD;`c0*$=7)?_AkJiiGTvhXQp0w>TQQLONen(3yK@fI_EU!vT{qA8qw?$QK&|sM zD~A~Gdd)Xf-K*r5srU8RZzD2L95%?E{wTvJp!a&JPs98p(ZV?n^n$CU9KAYvTZzVo1l^7a*!L`>e_(Nhh(?iYl zg_OIt1zILlDJu+>z_kW3>Z9_x)%gKL!Zdb(JhS=5cf5A~SLo+g-GRig zG&q^=l3jV{l2OTt1iyrBYt0a2$jB3v+J0W^@>6|e_%16xqhcmYObl7M|{dyF&0_=t}&}=^Aj1P zGQsT0j8o+gmL}ASI*+GxBw2qPckh-QZ@O z={2TmyjdtdN8GhrdVXd(w^!C;K*kigxl>6gT?}zAnY;q+lh^gKsVX(LV^~#(Ri`4% zhFOWZ&$DoCEtT^3uFu%!dd!VuirlH!At({oi+fD3poa}HS#q?Bv2WGT4ON@0nu}ng zYZG&;XMtVZ<@@Eee17t7%#CA;+``GEG(I0vyp?JlXvE0pvDxr6R(2xNWkaH2D+{C2Gh~Dlq=km7@;#=4`Cm@6)7H+cN>#)%4WA#WO@XaFKTX z4KzcZD}*>xN2gQeA@HM@kDWy}SI*AvJzk6Sv)O(vy}BlEWaeY|T}Tx;s`JTvWm zQ>|2OdHFu8ICcl=M~~f}u5K%v`!h8o253x?n;!0zZMLUk$SbcIT;Hy%wgM}ngVt2q@w2RVYb`Kn1+Tfq!E&R^vG{t&wi@ZSEldYieWaU z$h|U{lrH5$vNs(Qojmz2*YS^DZ8F;q(H^~MZ07Q8M$IlanlaE~irn-78kT&B_P(ms znS4vNA6t-O3~OLPq8{qe<2ACmZFQ=+`*1r3TuhOh?+3e)y}QXCa}A1TGE=_4!yeb0 zI?ot2&bK3Dcl4-lm)OIHlZDks-8P1&`i7-T#r)abi7^Zd?lv(B*Z{w#hS@!Qv95(f zrfLIod#z61OgfvBbGfVe?A2_xf#rsM2!GYK8`jl{3AV>|MUx2^X7gtq57h!R2YsCtL#bjd3Ll>XuF1*i{A8iA~c{r9yzflcV`63}8!+o*6}M zCSNR0=f4ESYZ&!oXdBcIg!v{jO^orGei(tT!p|hgQ@LU>Uz{y|F(?}U9~=4I#OSw2 zeit84Y7`1{j7E|P2&33LzS{YEhhf%&i zbh9v}R)_mBH)$qNg9ma5{>j-BqU2e)w$@Ac=W=)I_Zl%sV~X6#iKKKcTDnjnNn?fHPg z`GmX&;rt8b3H@)%Sag9y58QAwp>L7rf)cp2T2Ii8f*AMku^eIhET!h*1A_^oHb*S@ ztzxOqhC?3KxZz-JrCZ(*o`1!H=X6c(IDzvYIndB|%X6~z{jR-Nh4Wp@qtW;4@%?1F zbjO5vOH$v^Kb1xoMt(8*pOUHJzl$&8M=&555DW+g1OtKr!GK^uFd!KC8X0g4d5rd> zQPDhxa`5w!uaj#8hRmj+)S8*Y9zRbGL7&wzb}w&MX0uu25j!;6d75MFG-fHrqao-5 zv#M-ghX-L3ZjU=$XBjw+!}|xWS8e_hNAHcY{%G^l#^Q>y)2+Wm5%Uynel+A}wZQf< zMIqlLR}`%E*SM8Iv;z5WMt(F$Pr0$Po-^wk&85Rr7EhzRh%sGpHXK0vzR1g1Y`%fe z3V5f;x2p^LhPIu7Bhce$zT(ND|Fa6q_B?wqzhT|HJ=v|WV7}r?is*jcPCZt@O;Zt#D&KHZ%Dt98g)i~ZzPrcdtJu=|Hl7EW9Ytt z1OQTy0N}-n|DQnozZAs(e-pz0v+gCt|4TvqKPHr3*zo_X`!?eLr6B%4nu(*RJIYe< zHHZIa-ATm%OF{g95VQG$hyQ2YJmUYQApZZG68;ZiK>WWH#Q(o=;s05;5Apv}5dZ%* zhW}^XGl>6}g82UzEBrs}-a!1n6vqFDO7o2i|IfN_BK}_r;{RW;@c*nkf%tzZi2wg4 zg#TyVw-Em?1@Zr3kh*W7zV`6{ta}de|6YAlxO&(cy-?x*S@$yH|D_=QpIZ0MRi_sy z{6FhnMEt)L#Q(pr;r~!2#Q$Rz&yWB2*4$q3@c-=3BK%(p!vDXH=>N&AJA(LsDTx1n z0mA>Y?kK$Wr7-^g1qlED1NRhM_N6fXA8s2jM)<$vrs1lcRy7K4hI5aXXaG@u zD#E%nrC}OXX4P1T`RK*s=H?FaK;EzmyIo?Q8po_;H1$4*q)8g1loZH+q*N;=Qt4Qk z9WUiT2>a+Ku+4c*TQDDNHoFZ_oV<*<{U|_j4cYwQI3=(05<)_2^1{LfW>K3g7X_0a zJ=asY{k2uqXw+7_|XB(F=5bw|djOzI!9L)+OR^T}(=Ik;AKR@5nunQXd5iVe<0w zGE$P6yqT#vRaFUJ;OHqY)pE79Y~$uew+4Ia0v2Z4G!nj~OC}PNA-@eLLoI(|W;o&0M?fL^`tUGByrT@96jUN8}qzyFuQ3U z_K|)X>M6OywR@F0$jP&++BWPr$p|euX37qtS0h0=dii!{n##hX{ez{cE(K+3tfQe^ zL#{~JL7mAP@Y=1J4iZ~x$ODU%gHb=~=wZ*67I*J19W2attCq!g(Cu3a*GpR}xrE#> z$TJlQcNMYniuycPdKG7X&RJQQpX@d|{_0r}z7iszM{ZdxE~5KqVtv#^Q>k-GB$q)$ zgsiw|Hbb*=^t6}mHy<9m>~)?r8Pr>SJ&t&Rj=o@~#gZ*HFX#&o7mT_2ZqulJ>kL?qRFOtt)Ud;uk2qA1)(d@B%XO zkmJj9ln`Zeo?Gik3g0DUfBnCb8vSJCUyPhdem?vc!%IWIIrPqn#^8S*92wyKzuxzg zzSZNu-upMbKhyKidfqs;k@(k%lTxLdDS){U1|sQr8k6T<0&!&X4RXmMECP8r zD8Ia)h#Z|DghvE%5#dyOJq+Pk$8vzYhfJXyS+u=$>0UBz?>d?C$5Nz3hVrJ<&ICc8 z$$>+r>1j#*FH8{DZ;w?dYQx83u?EuKBIPhmsYifXDPK_-gW@>61k{w27U`2Qti*7*>vY6f_f&>p&W~||N!eW)Zjx^8ZqUYV zm`M~}WyhBru%M7x!|7EGGF z3MMI&Y=*c+uha;0|Mzq!16gkppHWH>nUFD_%N0yl*$cc`bDo8 zP+K*QR}X@UkhVDE?duRE*TvmP0{Xhelf6Quy2r;J(H0rFvlj>iB|%bdFSl1(`DUhw^aWS0EpCq>VDAsh5NF|9&E3Xi^Xq#qBOrkZ0Yo_rc+SGRD#r znnme*p+AUtYR~gd>r=ku`uUNO9yrqTWnEFrw4VS~K(vKKppDpK06|ft@1DF`{+oP- zl;c_V>^MQZmug-DL`vO^STX2OkkuOb(J7FujY%&&Yn}E>Xqgp!AZ-t{JJ_1TGdyI0 z$YIIDPdSUdFJ3rWxAlJcG68IkeMW|}Z5usiqGy%ZK zE_%t7y~QkVhm2tP4MrgYyAUyef-dc5)_w01&B41pse6I&)&%C$oM3BoJh6DNZkv0xD@a$*Ho>B|YJ<|6`yRET}B7nkn zh9=$L9Dx$Fqtg8>1NIfCJP3Axr<_eKuyAk#bK zrORCD{A=i9qM%)F&*UHGQ1?6?zS3RyghKda86Hvs7sLXY?#;2pj}3~mWyi_JhsQ&1no`% z9|Tl9(z$nRCXpu9?jV|4NAd&kTDDL$=EB__z;&BKK~($=7+S49xwNb!wbBr*s` z4Fr-5Z!1fGw-)2g^F;Q3YK5$>zhg^qH&~2ddlwMg##~!>0WadQzQ}y80}~eA!^=bW zzw6ni(rq8Zo&p&I>aD^qp~nQ9MYMU0b&c@dcL?Ek#+J#T($(H>7-2epY=(o)i&~fi z5hQCvhKqbTsS%Gf$Lm7TMkK+;4WwaS-J^MG6Wi9-6vz|ab-!-pEPxJvm}2C`19L;o z6I2&8;%}=F_vJsdFhP^mwsizWj0Xm*1CYO;4{c4*b}a~(SPkNf@P#uE|AR8;LPh- z5l!@e%ah1^F7?^sx=C}eW55=4iCP=31zn{Kqz>Z_ofNg(aCJBJCRN|^SdDrZk$SZT z6`tUE+r0r}*e^#LO}jKesJy+YJo)jrwSfF|Y!gJsy%Pgf^-RRbqD`5*9f}QhrI0string('avatar')->nullable(); $table->string('phone')->nullable(); $table->string('job')->nullable(); + $table->json('colleagues')->nullable(); $table->rememberToken(); $table->timestamps(); }); diff --git a/docs-assets/screenshots/schema.js b/docs-assets/screenshots/schema.js index c9b4bfc5d5..6ec1df66ab 100644 --- a/docs-assets/screenshots/schema.js +++ b/docs-assets/screenshots/schema.js @@ -1336,6 +1336,42 @@ export default { deviceScaleFactor: 3, }, }, + 'infolists/entries/image/stacked': { + url: 'infolists/entries', + selector: '#imageStacked', + viewport: { + width: 1920, + height: 640, + deviceScaleFactor: 3, + }, + }, + 'infolists/entries/image/limited': { + url: 'infolists/entries', + selector: '#imageLimited', + viewport: { + width: 1920, + height: 640, + deviceScaleFactor: 3, + }, + }, + 'infolists/entries/image/limited-remaining-text': { + url: 'infolists/entries', + selector: '#imageLimitedRemainingText', + viewport: { + width: 1920, + height: 640, + deviceScaleFactor: 3, + }, + }, + 'infolists/entries/image/limited-remaining-text-separately': { + url: 'infolists/entries', + selector: '#imageLimitedRemainingTextSeparately', + viewport: { + width: 1920, + height: 640, + deviceScaleFactor: 3, + }, + }, 'infolists/entries/color/simple': { url: 'infolists/entries', selector: '#color', @@ -1881,6 +1917,42 @@ export default { deviceScaleFactor: 3, }, }, + 'tables/columns/image/stacked': { + url: 'tables?table=imageColumnStacked', + selector: 'body', + viewport: { + width: 1080, + height: 640, + deviceScaleFactor: 3, + }, + }, + 'tables/columns/image/limited': { + url: 'tables?table=imageColumnLimited', + selector: 'body', + viewport: { + width: 1080, + height: 640, + deviceScaleFactor: 3, + }, + }, + 'tables/columns/image/limited-remaining-text': { + url: 'tables?table=imageColumnLimitedRemainingText', + selector: 'body', + viewport: { + width: 1080, + height: 640, + deviceScaleFactor: 3, + }, + }, + 'tables/columns/image/limited-remaining-text-separately': { + url: 'tables?table=imageColumnLimitedRemainingTextSeparately', + selector: 'body', + viewport: { + width: 1080, + height: 640, + deviceScaleFactor: 3, + }, + }, 'tables/columns/color/simple': { url: 'tables?table=colorColumn', selector: 'body', diff --git a/packages/forms/src/Components/CheckboxList.php b/packages/forms/src/Components/CheckboxList.php index 23d0dfc277..558ef6584e 100644 --- a/packages/forms/src/Components/CheckboxList.php +++ b/packages/forms/src/Components/CheckboxList.php @@ -30,6 +30,9 @@ class CheckboxList extends Field implements Contracts\HasNestedRecursiveValidati protected string | Closure | null $relationship = null; + /** + * @var array | Arrayable | Closure + */ protected array | Arrayable | Closure $descriptions = []; protected bool | Closure $isBulkToggleable = false; @@ -277,6 +280,9 @@ class CheckboxList extends Field implements Contracts\HasNestedRecursiveValidati return (bool) $this->evaluate($this->isBulkToggleable); } + /** + * @param array | Arrayable | Closure $descriptions + */ public function descriptions(array | Arrayable | Closure $descriptions): static { $this->descriptions = $descriptions; @@ -284,16 +290,25 @@ class CheckboxList extends Field implements Contracts\HasNestedRecursiveValidati return $this; } + /** + * @param array-key $value + */ public function hasDescription($value): bool { return array_key_exists($value, $this->getDescriptions()); } + /** + * @param array-key $value + */ public function getDescription($value): ?string { return $this->getDescriptions()[$value] ?? null; } + /** + * @return array + */ public function getDescriptions(): array { $descriptions = $this->evaluate($this->descriptions); diff --git a/packages/infolists/docs/03-entries/04-image.md b/packages/infolists/docs/03-entries/04-image.md index 20c99b2c30..042164028a 100644 --- a/packages/infolists/docs/03-entries/04-image.md +++ b/packages/infolists/docs/03-entries/04-image.md @@ -39,32 +39,6 @@ ImageEntry::make('header_image') ->visibility('private') ``` -## Square image - -You may display the image using a 1:1 aspect ratio: - -```php -use Filament\Infolists\Components\ImageEntry; - -ImageEntry::make('author.avatar') - ->square() -``` - - - -## Circular image - -You may make the image fully rounded, which is useful for rendering avatars: - -```php -use Filament\Infolists\Components\ImageEntry; - -ImageEntry::make('author.avatar') - ->circular() -``` - - - ## Customizing the size You may customize the image size by passing a `width()` and `height()`, or both with `size()`: @@ -82,6 +56,34 @@ ImageEntry::make('author.avatar') ->size(40) ``` +## Square image + +You may display the image using a 1:1 aspect ratio: + +```php +use Filament\Infolists\Components\ImageEntry; + +ImageEntry::make('author.avatar') + ->height(40) + ->square() +``` + + + +## Circular image + +You may make the image fully rounded, which is useful for rendering avatars: + +```php +use Filament\Infolists\Components\ImageEntry; + +ImageEntry::make('author.avatar') + ->height(40) + ->circular() +``` + + + ## Adding a default image URL You can display a placeholder image if one doesn't exist yet, by passing a URL to the `defaultImageUrl()` method: @@ -93,6 +95,114 @@ ImageEntry::make('avatar') ->defaultImageUrl(url('/images/placeholder.png')) ``` +## Stacking images + +You may display multiple images as a stack of overlapping images by using `stacked()`: + +```php +use Filament\Infolists\Components\ImageEntry; + +ImageEntry::make('colleagues.avatar') + ->height(40) + ->circular() + ->stacked() +``` + + + +### Customizing the stacked ring width + +The default ring width is `3`, but you may customize it to be from `0` to `8`: + +```php +use Filament\Infolists\Components\ImageEntry; + +ImageEntry::make('colleagues.avatar') + ->height(40) + ->circular() + ->stacked() + ->ring(5) +``` + +### Customizing the stacked overlap + +The default overlap is `4`, but you may customize it to be from `0` to `8`: + +```php +use Filament\Infolists\Components\ImageEntry; + +ImageEntry::make('colleagues.avatar') + ->height(40) + ->circular() + ->stacked() + ->overlap(2) +``` + +## Setting a limit + +You may set a limit of the maximum number of images you want to display by passing `limit()`: + +```php +use Filament\Infolists\Components\ImageEntry; + +ImageEntry::make('colleagues.avatar') + ->height(40) + ->circular() + ->stacked() + ->limit(3) +``` + + + +### Showing the remaining images count + +When you set a limit you may also display the count of remaining images by passing `limitedRemainingText()`. + +```php +use Filament\Infolists\Components\ImageEntry; + +ImageEntry::make('colleagues.avatar') + ->height(40) + ->circular() + ->stacked() + ->limit(3) + ->limitedRemainingText() +``` + + + +#### Showing the limited remaining text separately + +By default, `limitedRemainingText()` will display the count of remaining images as a number stacked on the other images. If you prefer to show the count as a number after the images you may use the `isSeparate: true` parameter: + +```php +use Filament\Infolists\Components\ImageEntry; + +ImageEntry::make('colleagues.avatar') + ->height(40) + ->circular() + ->stacked() + ->limit(3) + ->limitedRemainingText(isSeparate: true) +``` + + + +#### Customizing the limited remaining text size + +By default, the size of the remaining text is `sm`. You can customize this to be `xs`, `md` or `lg` using the `size` parameter: + +```php +use Filament\Infolists\Components\ImageEntry; + +ImageEntry::make('colleagues.avatar') + ->height(40) + ->circular() + ->stacked() + ->limit(3) + ->limitedRemainingText(size: 'lg') +``` + ## Custom attributes You may customize the extra HTML attributes of the image using `extraImgAttributes()`: diff --git a/packages/infolists/resources/views/components/image-entry.blade.php b/packages/infolists/resources/views/components/image-entry.blade.php index b21e4e229e..5b4c9ee937 100644 --- a/packages/infolists/resources/views/components/image-entry.blade.php +++ b/packages/infolists/resources/views/components/image-entry.blade.php @@ -1,54 +1,105 @@ @php - $height = $getHeight(); + $limit = $getLimit(); + $state = collect($getState())->take($limit)->all(); $isCircular = $isCircular(); $isSquare = $isSquare(); - $path = $getImageUrl(); - $width = $getWidth() ?? (($isCircular || $isSquare) ? $height : null); + $height = $getHeight(); + $width = $getWidth() ?? ($isCircular || $isSquare ? $height : null); + $overlap = $isStacked() ? ($getOverlap() ?? 4) : null; + + $defaultImageUrl = $getDefaultImageUrl(); + if ((! count($state)) && filled($defaultImageUrl)) { + $state = [null]; + } + + $ringClasses = match ($getRing()) { + 0 => '', + 1 => 'ring-1', + 2 => 'ring-2', + 3 => 'ring-3', + 4 => 'ring-4', + 5 => 'ring-5', + 6 => 'ring-6', + 7 => 'ring-7', + 8 => 'ring-8', + default => 'ring', + }; + + $hasLimitedRemainingText = $hasLimitedRemainingText(); + $isLimitedRemainingTextSeparate = $isLimitedRemainingTextSeparate(); + $limitedRemainingTextSizeClasses = match ($getLimitedRemainingTextSize()) { + 'xs' => 'text-xs', + 'base', 'md' => 'text-base', + 'lg' => 'text-lg', + default => 'text-sm', + }; @endphp -
merge($getExtraAttributes(), escape: false) - ->class([ - 'fi-in-image flex', - match ($getAlignment()) { - 'center' => 'justify-center', - 'end' => 'justify-end', - 'left' => 'justify-left', - 'right' => 'justify-right', - 'start', null => 'justify-start', + @if (count($state)) +
+
'', + 1 => '-space-x-1', + 2 => '-space-x-2', + 3 => '-space-x-3', + 4 => '-space-x-4', + 5 => '-space-x-5', + 6 => '-space-x-6', + 7 => '-space-x-7', + 8 => '-space-x-8', + default => 'space-x-1', }, ]) - }} - > -
$isCircular || $isSquare, - 'rounded-full' => $isCircular, - ]) - @style([ - "height: {$height}" => $height, - "width: {$width}" => $width, - ]) - > - @if ($path) - class([ - 'object-cover object-center' => $isCircular || $isSquare, - ]) - ->style([ - "height: {$height}" => $height, - "width: {$width}" => $width, - ]) - }} - /> + > + @foreach ($state as $stateItem) + class([ + 'max-w-none ring-white object-cover object-center dark:ring-gray-900', + 'rounded-full' => $isCircular, + $ringClasses, + ]) }} + > + @endforeach + + @if ($hasLimitedRemainingText && ($loop->iteration < count($state)) && (! $isLimitedRemainingTextSeparate) && $isCircular) +
$isCircular, + $limitedRemainingTextSizeClasses, + $ringClasses, + ]) + > + + +{{ count($state) - $loop->iteration }} + +
+ @endif +
+ + @if ($hasLimitedRemainingText && ($loop->iteration < count($state)) && ($isLimitedRemainingTextSeparate || (! $isCircular))) +
+ +{{ count($state) - $loop->iteration }} +
@endif
-
+ @endif diff --git a/packages/infolists/src/Components/ImageEntry.php b/packages/infolists/src/Components/ImageEntry.php index 83dae0c53c..afaa063fbb 100644 --- a/packages/infolists/src/Components/ImageEntry.php +++ b/packages/infolists/src/Components/ImageEntry.php @@ -7,6 +7,7 @@ use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Filesystem\FilesystemAdapter; use Illuminate\Support\Facades\Storage; use Illuminate\View\ComponentAttributeBag; +use League\Flysystem\UnableToCheckFileExistence; use Throwable; class ImageEntry extends Entry @@ -35,6 +36,20 @@ class ImageEntry extends Entry protected string | Closure | null $defaultImageUrl = null; + protected bool | Closure $isStacked = false; + + protected int | Closure | null $overlap = null; + + protected int | Closure | null $ring = null; + + protected int | Closure | null $limit = null; + + protected bool | Closure $hasLimitedRemainingText = false; + + protected bool | Closure $isLimitedRemainingTextSeparate = false; + + protected string | Closure | null $limitedRemainingTextSize = null; + public function disk(string | Closure | null $disk): static { $this->disk = $disk; @@ -117,14 +132,8 @@ class ImageEntry extends Entry return $this; } - public function getImageUrl(): ?string + public function getImageUrl(?string $state = null): ?string { - $state = $this->getState(); - - if (! $state) { - return $this->getDefaultImageUrl(); - } - if (filter_var($state, FILTER_VALIDATE_URL) !== false) { return $state; } @@ -132,7 +141,11 @@ class ImageEntry extends Entry /** @var FilesystemAdapter $storage */ $storage = $this->getDisk(); - if (! $storage->exists($state)) { + try { + if (! $storage->exists($state)) { + return null; + } + } catch (UnableToCheckFileExistence $exception) { return null; } @@ -207,4 +220,90 @@ class ImageEntry extends Entry { return new ComponentAttributeBag($this->getExtraImgAttributes()); } + + public function stacked(bool | Closure $condition = true): static + { + $this->isStacked = $condition; + + return $this; + } + + public function isStacked(): bool + { + return (bool) $this->evaluate($this->isStacked); + } + + public function overlap(int | Closure | null $overlap): static + { + $this->overlap = $overlap; + + return $this; + } + + public function getOverlap(): ?int + { + return $this->evaluate($this->overlap); + } + + public function ring(string | Closure | null $ring): static + { + $this->ring = $ring; + + return $this; + } + + public function getRing(): ?int + { + return $this->evaluate($this->ring); + } + + public function limit(int | Closure | null $limit = 3): static + { + $this->limit = $limit; + + return $this; + } + + public function getLimit(): ?int + { + return $this->evaluate($this->limit); + } + + public function limitedRemainingText(bool | Closure $condition = true, bool | Closure $isSeparate = false, string | Closure | null $size = null): static + { + $this->hasLimitedRemainingText = $condition; + $this->limitedRemainingTextSeparate($isSeparate); + $this->limitedRemainingTextSize($size); + + return $this; + } + + public function limitedRemainingTextSeparate(bool | Closure $condition = true): static + { + $this->isLimitedRemainingTextSeparate = $condition; + + return $this; + } + + public function hasLimitedRemainingText(): bool + { + return (bool) $this->evaluate($this->hasLimitedRemainingText); + } + + public function isLimitedRemainingTextSeparate(): bool + { + return (bool) $this->evaluate($this->isLimitedRemainingTextSeparate); + } + + public function limitedRemainingTextSize(string | Closure | null $size): static + { + $this->limitedRemainingTextSize = $size; + + return $this; + } + + public function getLimitedRemainingTextSize(): ?string + { + return $this->evaluate($this->limitedRemainingTextSize); + } } diff --git a/packages/spatie-laravel-media-library-plugin/src/Infolists/Components/SpatieMediaLibraryImageEntry.php b/packages/spatie-laravel-media-library-plugin/src/Infolists/Components/SpatieMediaLibraryImageEntry.php index abaca26fa6..fc71e54046 100644 --- a/packages/spatie-laravel-media-library-plugin/src/Infolists/Components/SpatieMediaLibraryImageEntry.php +++ b/packages/spatie-laravel-media-library-plugin/src/Infolists/Components/SpatieMediaLibraryImageEntry.php @@ -3,6 +3,7 @@ namespace Filament\Infolists\Components; use Illuminate\Support\Collection; +use Spatie\MediaLibrary\MediaCollections\Models\Media; use Throwable; class SpatieMediaLibraryImageEntry extends ImageEntry @@ -35,14 +36,8 @@ class SpatieMediaLibraryImageEntry extends ImageEntry return $this->conversion ?? ''; } - public function getImageUrl(): ?string + public function getImageUrl(?string $state = null): ?string { - $state = $this->getState(); - - if ($state && (! $state instanceof Collection)) { - return $state; - } - $record = $this->getRecord(); if (! $record) { @@ -55,11 +50,17 @@ class SpatieMediaLibraryImageEntry extends ImageEntry $record = $record->getRelationValue($relationshipName); } - if ($this->getVisibility() === 'private' && method_exists($record, 'getFirstTemporaryUrl')) { + /** @var ?Media $media */ + $media = $record->media->first(fn (Media $media): bool => $media->uuid === $state); + + if (! $media) { + return null; + } + + if ($this->getVisibility() === 'private') { try { - return $record->getFirstTemporaryUrl( + return $media->getTemporaryUrl( now()->addMinutes(5), - $this->getCollection(), $this->getConversion(), ); } catch (Throwable $exception) { @@ -67,12 +68,19 @@ class SpatieMediaLibraryImageEntry extends ImageEntry } } - if (! method_exists($record, 'getFirstMediaUrl')) { - return $state; - } + return $media->getUrl($this->getConversion()); + } - $firstMediaUrl = $record->getFirstMediaUrl($this->getCollection(), $this->getConversion()); + /** + * @return array + */ + public function getState(): array + { + $collection = $this->getCollection(); - return filled($firstMediaUrl) ? $firstMediaUrl : $this->getDefaultImageUrl(); + return $this->getRecord()->getRelationValue('media') + ->filter(fn (Media $media): bool => blank($collection) || ($media->getAttributeValue('collection_name') === $collection)) + ->map(fn (Media $media): string => $media->uuid) + ->all(); } } diff --git a/packages/spatie-laravel-media-library-plugin/src/Tables/Columns/SpatieMediaLibraryImageColumn.php b/packages/spatie-laravel-media-library-plugin/src/Tables/Columns/SpatieMediaLibraryImageColumn.php index 01e2dbf60e..c3fcd7b702 100644 --- a/packages/spatie-laravel-media-library-plugin/src/Tables/Columns/SpatieMediaLibraryImageColumn.php +++ b/packages/spatie-laravel-media-library-plugin/src/Tables/Columns/SpatieMediaLibraryImageColumn.php @@ -48,8 +48,8 @@ class SpatieMediaLibraryImageColumn extends ImageColumn $record = $record->getRelationValue($this->getRelationshipName()); } - /** @var Media $media */ - $media = $record->media->first(fn (Media $media) => $media->uuid === $state); + /** @var ?Media $media */ + $media = $record->media->first(fn (Media $media): bool => $media->uuid === $state); if (! $media) { return null; @@ -69,12 +69,15 @@ class SpatieMediaLibraryImageColumn extends ImageColumn return $media->getUrl($this->getConversion()); } + /** + * @return array + */ public function getState(): array { $collection = $this->getCollection(); - return $this->getRecord()->media - ->filter(fn (Media $media): bool => blank($collection) || ($media->collection_name === $collection)) + return $this->getRecord()->getRelationValue('media') + ->filter(fn (Media $media): bool => blank($collection) || ($media->getAttributeValue('collection_name') === $collection)) ->map(fn (Media $media): string => $media->uuid) ->all(); } @@ -98,9 +101,4 @@ class SpatieMediaLibraryImageColumn extends ImageColumn return $query->with(['media']); } - - public function getRecord(): Model&HasMedia - { - return parent::getRecord(); - } } diff --git a/packages/tables/docs/03-columns/04-image.md b/packages/tables/docs/03-columns/04-image.md index 6f7bafbc53..60da3e407c 100644 --- a/packages/tables/docs/03-columns/04-image.md +++ b/packages/tables/docs/03-columns/04-image.md @@ -39,6 +39,23 @@ ImageColumn::make('header_image') ->visibility('private') ``` +## Customizing the size + +You may customize the image size by passing a `width()` and `height()`, or both with `size()`: + +```php +use Filament\Tables\Columns\ImageColumn; + +ImageColumn::make('header_image') + ->width(200) + +ImageColumn::make('header_image') + ->height(50) + +ImageColumn::make('author.avatar') + ->size(40) +``` + ## Square image You may display the image using a 1:1 aspect ratio: @@ -65,23 +82,6 @@ ImageColumn::make('avatar') -## Customizing the size - -You may customize the image size by passing a `width()` and `height()`, or both with `size()`: - -```php -use Filament\Tables\Columns\ImageColumn; - -ImageColumn::make('header_image') - ->width(200) - -ImageColumn::make('header_image') - ->height(50) - -ImageColumn::make('author.avatar') - ->size(40) -``` - ## Adding a default image URL You can display a placeholder image if one doesn't exist yet, by passing a URL to the `defaultImageUrl()` method: @@ -93,6 +93,103 @@ ImageColumn::make('avatar') ->defaultImageUrl(url('/images/placeholder.png')) ``` +## Stacking images + +You may display multiple images as a stack of overlapping images by using `stacked()`: + +```php +use Filament\Tables\Columns\ImageColumn; + +ImageColumn::make('colleagues.avatar') + ->circular() + ->stacked() +``` + + + +### Customizing the stacked ring width + +The default ring width is `3`, but you may customize it to be from `0` to `8`: + +```php +ImageColumn::make('colleagues.avatar') + ->circular() + ->stacked() + ->ring(5) +``` + +### Customizing the stacked overlap + +The default overlap is `4`, but you may customize it to be from `0` to `8`: + +```php +ImageColumn::make('colleagues.avatar') + ->circular() + ->stacked() + ->overlap(2) +``` + +## Setting a limit + +You may set a limit of the maximum number of images you want to display by passing `limit()`: + +```php +use Filament\Tables\Columns\ImageColumn; + +ImageColumn::make('colleagues.avatar') + ->circular() + ->stacked() + ->limit(3) +``` + + + +### Showing the remaining images count + +When you set a limit you may also display the count of remaining images by passing `limitedRemainingText()`. + +```php +use Filament\Tables\Columns\ImageColumn; + +ImageColumn::make('colleagues.avatar') + ->circular() + ->stacked() + ->limit(3) + ->limitedRemainingText() +``` + + + +#### Showing the limited remaining text separately + +By default, `limitedRemainingText()` will display the count of remaining images as a number stacked on the other images. If you prefer to show the count as a number after the images you may use the `isSeparate: true` parameter: + +```php +use Filament\Tables\Columns\ImageColumn; + +ImageColumn::make('colleagues.avatar') + ->circular() + ->stacked() + ->limit(3) + ->limitedRemainingText(isSeparate: true) +``` + + + +#### Customizing the limited remaining text size + +By default, the size of the remaining text is `sm`. You can customize this to be `xs`, `md` or `lg` using the `size` parameter: + +```php +use Filament\Tables\Columns\ImageColumn; + +ImageColumn::make('colleagues.avatar') + ->circular() + ->stacked() + ->limit(3) + ->limitedRemainingText(size: 'lg') +``` + ## Custom attributes You may customize the extra HTML attributes of the image using `extraImgAttributes()`: @@ -114,84 +211,3 @@ ImageColumn::make('logo') 'alt' => "{$record->name} logo", ]), ``` - -## Multiple images - -You may display multiple images from an array: - -```php -ImageColumn::make('images') - ->circular() -``` - -Be sure to add an `array` [cast](https://laravel.com/docs/eloquent-mutators#array-and-json-casting) to the model property: - -```php -use Illuminate\Database\Eloquent\Model; - -class Product extends Model -{ - protected $casts = [ - 'images' => 'array', - ]; - - // ... -} -``` - -## Stacking images - -You may display multiple images as a stack of overlapping images by using `stacked()`: - -```php -ImageColumn::make('images') - ->circular() - ->stacked() -``` - -### Setting a limit - -You may set a limit to the number of images you want to display by passing `limit()`: - -```php -ImageColumn::make('images') - ->circular() - ->stacked() - ->limit(3) -``` - -### Showing the remaining images count - -When you set a limit you may also display the count of remaining images by passing `showRemaining()`. - -```php -ImageColumn::make('images') - ->circular() - ->stacked() - ->limit(3) - ->showRemaining() -``` - -By default, `showRemaining()` will display the count of remaining images as a number stacked on the other images. If you prefer to show the count as a number after the images you may use `showRemainingAfterStack()`. You may also set the text size by using `remainingTextSize('xs')`; - -### Customizing the ring width - -The default ring width is `ring-3` but you may customize the ring width to be either `0`, `1`, `2`, or `4` which correspond to tailwinds `ring-widths`: `ring-0`, `ring-1`, `ring-2`, and `ring-4` respectively. - -```php -ImageColumn::make('users.avatar') - ->circular() - ->stacked() - ->ring(3) -``` - -### Customizing the overlap - -The default overlap is `-space-x-1` but you may customize the overlap to be either `0`, `1`, `2`, `3`, or `4` which correspond to tailwinds `space-x` options: `space-x-0`, `-space-x-1`, `-space-x-2`, `-space-x-3`, and `-space-x-4` respectively. - -```php -ImageColumn::make('users.avatar') - ->circular() - ->stacked() - ->overlap(3) -``` \ No newline at end of file diff --git a/packages/tables/resources/views/columns/image-column.blade.php b/packages/tables/resources/views/columns/image-column.blade.php index 0324665117..2208bf3f2a 100644 --- a/packages/tables/resources/views/columns/image-column.blade.php +++ b/packages/tables/resources/views/columns/image-column.blade.php @@ -39,7 +39,7 @@ $isLimitedRemainingTextSeparate = $isLimitedRemainingTextSeparate(); $limitedRemainingTextSizeClasses = match ($getLimitedRemainingTextSize()) { 'xs' => 'text-xs', - 'md' => 'text-md', + 'base', 'md' => 'text-base', 'lg' => 'text-lg', default => 'text-sm', }; @@ -99,7 +99,7 @@ @endif
- @if ($hasLimitedRemainingText && ($loop->iteration < count($state)) && ($isLimitedRemainingTextSeparate || $isCircular)) + @if ($hasLimitedRemainingText && ($loop->iteration < count($state)) && ($isLimitedRemainingTextSeparate || (! $isCircular)))