mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-02-04 05:35:44 +00:00
Compare commits
2480 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45d38494f2 | ||
|
|
5c03d0d14c | ||
|
|
8404294f56 | ||
|
|
7f519bb71f | ||
|
|
7a5995002d | ||
|
|
ba8fe8e830 | ||
|
|
949a5b19cc | ||
|
|
5a68a00f38 | ||
|
|
d52d91852a | ||
|
|
b8b39e1ebd | ||
|
|
e795e63d02 | ||
|
|
27dc58a90f | ||
|
|
d4679353af | ||
|
|
73a53c8829 | ||
|
|
eb51f78f8c | ||
|
|
8667443e64 | ||
|
|
0650472551 | ||
|
|
7653ea683a | ||
|
|
d633d2f424 | ||
|
|
98ec370545 | ||
|
|
51b9579d6e | ||
|
|
af42791fa2 | ||
|
|
f7e9a88575 | ||
|
|
331ac4787e | ||
|
|
4ac2e0aa49 | ||
|
|
0cb10c206a | ||
|
|
5fd031f518 | ||
|
|
48c204a7b0 | ||
|
|
e61fc1c84e | ||
|
|
fb64381af9 | ||
|
|
84954f1919 | ||
|
|
cdabe516eb | ||
|
|
e5ddb2cef9 | ||
|
|
963185b927 | ||
|
|
f7ef6a207e | ||
|
|
8088d14340 | ||
|
|
f710b782a3 | ||
|
|
b13921f9aa | ||
|
|
522191052a | ||
|
|
e9e8844342 | ||
|
|
ccbf7b24cf | ||
|
|
57f1417d4c | ||
|
|
d5318e6e88 | ||
|
|
978e3fc610 | ||
|
|
dcb5eef814 | ||
|
|
518ad838e7 | ||
|
|
ba6ced577e | ||
|
|
138253e728 | ||
|
|
ffd299962b | ||
|
|
354a147155 | ||
|
|
a623017325 | ||
|
|
654bd80abd | ||
|
|
3249591351 | ||
|
|
40f934d7a6 | ||
|
|
53717dcc46 | ||
|
|
f16a00fe2e | ||
|
|
ab1af0ce28 | ||
|
|
be444bbbc4 | ||
|
|
e161872604 | ||
|
|
e21fa015bf | ||
|
|
fb3275d89f | ||
|
|
f42e4ffc71 | ||
|
|
7b15c190d3 | ||
|
|
73c2d27620 | ||
|
|
288db125ce | ||
|
|
0effaae435 | ||
|
|
a71f1dd3c6 | ||
|
|
c08843d587 | ||
|
|
44094cd4f4 | ||
|
|
a4bd9d518c | ||
|
|
a739a6e24a | ||
|
|
955b52d54b | ||
|
|
004293008c | ||
|
|
9f26a0b512 | ||
|
|
b16f95a4ba | ||
|
|
c079531d2e | ||
|
|
773f2dc074 | ||
|
|
64d783179b | ||
|
|
3dafd6c277 | ||
|
|
fb2fdb10b2 | ||
|
|
50dedf84d5 | ||
|
|
d7b96a7dca | ||
|
|
1d1acdf7c8 | ||
|
|
440bc76f08 | ||
|
|
d2a598dbf5 | ||
|
|
931f9dab6f | ||
|
|
10ec2af398 | ||
|
|
46382ef89f | ||
|
|
c4116feda4 | ||
|
|
4c18342cf4 | ||
|
|
92f103c1dd | ||
|
|
8c9374e00a | ||
|
|
870f3d2ef9 | ||
|
|
d75b3596aa | ||
|
|
c388d84f57 | ||
|
|
782f6c9655 | ||
|
|
078d12f2a1 | ||
|
|
271e1aa26a | ||
|
|
c9c11c7ff7 | ||
|
|
75da4667b7 | ||
|
|
a2591f7f77 | ||
|
|
72b4e8bd39 | ||
|
|
740b0bc3b1 | ||
|
|
3e48b15db0 | ||
|
|
f1b8a22cc8 | ||
|
|
02b75fcb68 | ||
|
|
9b2541975f | ||
|
|
d3ff9a5b7c | ||
|
|
75acf7d132 | ||
|
|
d6a9430c20 | ||
|
|
5a89720f59 | ||
|
|
bee7133e81 | ||
|
|
8522f88d66 | ||
|
|
10ecc5c75d | ||
|
|
cdd722c975 | ||
|
|
7fd9b258aa | ||
|
|
5368d398fe | ||
|
|
30cc8c8711 | ||
|
|
f7d1fea2af | ||
|
|
ee5674bb6d | ||
|
|
5e1fa61c55 | ||
|
|
92f968f933 | ||
|
|
9eabd75864 | ||
|
|
df121e68b4 | ||
|
|
be0721fa5d | ||
|
|
d670872d73 | ||
|
|
68eb9162a9 | ||
|
|
a65ce23a10 | ||
|
|
bea9bca891 | ||
|
|
9f2c6fe8cc | ||
|
|
07e4276190 | ||
|
|
3db15c168d | ||
|
|
7e9f66ec61 | ||
|
|
c7ce4ec4e3 | ||
|
|
ad32aed204 | ||
|
|
2df9235516 | ||
|
|
a0efb0b9a4 | ||
|
|
a244741640 | ||
|
|
e3f5360a7f | ||
|
|
c280bae14f | ||
|
|
e99671ba81 | ||
|
|
73338462e5 | ||
|
|
1912e69375 | ||
|
|
60a3789e9c | ||
|
|
a89bc5a5ce | ||
|
|
08f3126f46 | ||
|
|
53e746146b | ||
|
|
1e7cc0d655 | ||
|
|
b37a81b297 | ||
|
|
6aec8118f2 | ||
|
|
756732803f | ||
|
|
a6b8abb9ce | ||
|
|
38feb54237 | ||
|
|
ee828de086 | ||
|
|
bc82638fea | ||
|
|
edb6f58f66 | ||
|
|
23b9a6e7bf | ||
|
|
0b3c6058e2 | ||
|
|
cb6572980e | ||
|
|
78e5bbb9dc | ||
|
|
12b52b3e79 | ||
|
|
e226749388 | ||
|
|
001cd0cdcd | ||
|
|
c3806348a0 | ||
|
|
c6a8d80d45 | ||
|
|
2babacd15a | ||
|
|
456ab29a34 | ||
|
|
1de201841a | ||
|
|
a5586fd4ea | ||
|
|
a14ac78957 | ||
|
|
1193ede1ca | ||
|
|
8ad75a1773 | ||
|
|
9ea1b44e64 | ||
|
|
b92ef7922d | ||
|
|
3d6eff639f | ||
|
|
011e761d7f | ||
|
|
a2fdb687a1 | ||
|
|
ff846e0cc1 | ||
|
|
df0ad0d9f1 | ||
|
|
716d1a6cba | ||
|
|
3e7468177d | ||
|
|
d4113e6715 | ||
|
|
7f7ce06d0e | ||
|
|
7cfea596fd | ||
|
|
7eae5d0f6e | ||
|
|
a390658ad9 | ||
|
|
ecc403cbbc | ||
|
|
e870ca856c | ||
|
|
e7b8fec063 | ||
|
|
7767878c4c | ||
|
|
63de20fe36 | ||
|
|
6114a9a8ed | ||
|
|
d5514ca0cd | ||
|
|
8cf0565211 | ||
|
|
32246b1d69 | ||
|
|
418dbf09ec | ||
|
|
a198d09b71 | ||
|
|
f3bb0e7b90 | ||
|
|
b3917e19a2 | ||
|
|
e7d8b7454f | ||
|
|
4d4660eed9 | ||
|
|
b80e47672d | ||
|
|
3b370841cc | ||
|
|
7ec2a9a0a5 | ||
|
|
e340a376ef | ||
|
|
a2709afc5c | ||
|
|
9c7ed95f23 | ||
|
|
524f1f99cb | ||
|
|
2ac6d7f2bb | ||
|
|
6b3fd9efae | ||
|
|
d99daa6538 | ||
|
|
13e0bba379 | ||
|
|
ed7c5bc537 | ||
|
|
03b7968b99 | ||
|
|
274d64246b | ||
|
|
4450053fd3 | ||
|
|
8c321f18e1 | ||
|
|
e015807edc | ||
|
|
4ebcd2f214 | ||
|
|
67cd8b7a61 | ||
|
|
0a802ca73b | ||
|
|
097f37bfbc | ||
|
|
524e42980a | ||
|
|
34eebc9406 | ||
|
|
65f20c232c | ||
|
|
d742fdae86 | ||
|
|
397e2baf6c | ||
|
|
4b1d951083 | ||
|
|
ab65a5a975 | ||
|
|
9947cbf47b | ||
|
|
f781ba0c6a | ||
|
|
e3c605a7a9 | ||
|
|
849e6761b8 | ||
|
|
407ef36b70 | ||
|
|
fef5ceb36d | ||
|
|
2e22f556d8 | ||
|
|
95b833402f | ||
|
|
c0e7418e7c | ||
|
|
4d72ef1efe | ||
|
|
a00bce399f | ||
|
|
2b8658b233 | ||
|
|
fc2784450b | ||
|
|
896d9bd3ae | ||
|
|
9199096f69 | ||
|
|
bdb2694734 | ||
|
|
98ab3cd3d0 | ||
|
|
22f7d3f9f4 | ||
|
|
7f27dafe32 | ||
|
|
37a1e6d4b5 | ||
|
|
91d5473bf2 | ||
|
|
b883cccb30 | ||
|
|
2ca7b3ba5f | ||
|
|
faada7ae7b | ||
|
|
3910fb778f | ||
|
|
4577d063cc | ||
|
|
1f5c70166e | ||
|
|
f920f9f08d | ||
|
|
e6272e88e6 | ||
|
|
4be24abf7f | ||
|
|
85cc05a144 | ||
|
|
7f726c4e2a | ||
|
|
5c86ffd2ac | ||
|
|
257444b640 | ||
|
|
c61d453de4 | ||
|
|
4b88b18e78 | ||
|
|
1125894e7a | ||
|
|
e8894360fa | ||
|
|
168c071ac3 | ||
|
|
d6dd67951e | ||
|
|
c7e4470a60 | ||
|
|
cb6789504a | ||
|
|
314664c4e2 | ||
|
|
58794f806c | ||
|
|
6a470a6448 | ||
|
|
27bf1713a0 | ||
|
|
c1e428dd3e | ||
|
|
661e5b50f8 | ||
|
|
749c2cf600 | ||
|
|
153e9e01bc | ||
|
|
c1797cc215 | ||
|
|
00eb31cd88 | ||
|
|
bd4b1e485b | ||
|
|
387eacf5ff | ||
|
|
5d00e79e0b | ||
|
|
3aac6a785d | ||
|
|
3506256dff | ||
|
|
5c49ab32e5 | ||
|
|
f128209c3a | ||
|
|
4a4254847a | ||
|
|
d21697367c | ||
|
|
1ceaab2788 | ||
|
|
0c56f20f55 | ||
|
|
261339c7e9 | ||
|
|
1d1335fed5 | ||
|
|
c9b5a53be3 | ||
|
|
11a037aeec | ||
|
|
0743bb5f54 | ||
|
|
9fe5163d86 | ||
|
|
acd107b1a2 | ||
|
|
1ff96df224 | ||
|
|
fb537c46d4 | ||
|
|
a588b454ae | ||
|
|
0f145800fa | ||
|
|
44dfa72f16 | ||
|
|
19152f498c | ||
|
|
056a61c9fe | ||
|
|
a0cfc5d267 | ||
|
|
eb6dbf0c67 | ||
|
|
af2083120e | ||
|
|
95d4c0e992 | ||
|
|
b14f67685a | ||
|
|
b82fa04aec | ||
|
|
93d1e8604a | ||
|
|
53d32dbcc2 | ||
|
|
da43d91b5a | ||
|
|
0694ab0777 | ||
|
|
207940426d | ||
|
|
58a204ed56 | ||
|
|
f68a2719f4 | ||
|
|
4311b019f4 | ||
|
|
c1b7ce0bcc | ||
|
|
fe0ca5403c | ||
|
|
c5c33ebfeb | ||
|
|
2c24843e4d | ||
|
|
b0e17e572c | ||
|
|
7cd5727ccc | ||
|
|
d02bafc7a6 | ||
|
|
ca4bdad32a | ||
|
|
4fab7f9180 | ||
|
|
224f54c676 | ||
|
|
fbc1dbb443 | ||
|
|
4da4e532c5 | ||
|
|
a16447faa2 | ||
|
|
a883b26dda | ||
|
|
b9937ee6ca | ||
|
|
40ed1d5841 | ||
|
|
0d3b86d06b | ||
|
|
f8464d6f23 | ||
|
|
d2995e3b6c | ||
|
|
eda37027d5 | ||
|
|
11f9e5cfa4 | ||
|
|
a59a02a6a7 | ||
|
|
26db867bd5 | ||
|
|
18a92cbaca | ||
|
|
62ac151847 | ||
|
|
0b3628e085 | ||
|
|
ffb23e7f34 | ||
|
|
678d861e22 | ||
|
|
652e08ebbe | ||
|
|
95672569c5 | ||
|
|
c9bedc9315 | ||
|
|
dbaeb39564 | ||
|
|
1e9801b0d4 | ||
|
|
eed82f2b6e | ||
|
|
a0f1545421 | ||
|
|
a69909ff23 | ||
|
|
65c99d3fbd | ||
|
|
687094fec0 | ||
|
|
757debaecc | ||
|
|
6d5dda80ee | ||
|
|
b9d9e965d5 | ||
|
|
e845df2a10 | ||
|
|
93cfc0e15d | ||
|
|
7a35bc2340 | ||
|
|
dc7eebb8ae | ||
|
|
1dfc967900 | ||
|
|
bb5217948d | ||
|
|
c2269711c5 | ||
|
|
e707b99860 | ||
|
|
ec43241ccc | ||
|
|
c45436dcf4 | ||
|
|
d41c3ee72b | ||
|
|
0058d0aa56 | ||
|
|
868638f031 | ||
|
|
a8250f17c3 | ||
|
|
c3bbc7e31c | ||
|
|
5d5565b832 | ||
|
|
5da0ff61b2 | ||
|
|
79efa87d39 | ||
|
|
a56981bc70 | ||
|
|
24f30bf18f | ||
|
|
0fa843fd78 | ||
|
|
aecee331a4 | ||
|
|
c1b7cc6d65 | ||
|
|
b8ddfa6537 | ||
|
|
fd4bd631e5 | ||
|
|
96b8901cde | ||
|
|
124033d122 | ||
|
|
cbb292b828 | ||
|
|
22b5fc2452 | ||
|
|
d549a314bc | ||
|
|
f32fe71dbc | ||
|
|
169cf14fa6 | ||
|
|
3befe007fd | ||
|
|
6d84bafa2e | ||
|
|
f3badc0ad4 | ||
|
|
df9887b629 | ||
|
|
cbecf5b954 | ||
|
|
040cccebe2 | ||
|
|
efffdf54c3 | ||
|
|
c3cb0c8968 | ||
|
|
d676c67b50 | ||
|
|
9316170c89 | ||
|
|
9101614d35 | ||
|
|
7312b2859d | ||
|
|
bf7d99ccba | ||
|
|
fb0d588cbf | ||
|
|
8e7bd6323a | ||
|
|
8bebd1933d | ||
|
|
2429b2c027 | ||
|
|
769820792f | ||
|
|
36f39b5023 | ||
|
|
bb675ee040 | ||
|
|
9b4aff7a3f | ||
|
|
e312d71286 | ||
|
|
a397c686b8 | ||
|
|
9b92005810 | ||
|
|
d4e0c8c3bb | ||
|
|
ae1ec9aaa2 | ||
|
|
e811bb9213 | ||
|
|
7de5d90c82 | ||
|
|
bf5265c15b | ||
|
|
49b111a0e9 | ||
|
|
85964d9f17 | ||
|
|
4f9075fa77 | ||
|
|
0856c9005c | ||
|
|
31d8bccfcf | ||
|
|
9213e3d3ef | ||
|
|
3665079cc9 | ||
|
|
44881e521c | ||
|
|
c136d8050b | ||
|
|
a5cc04b46f | ||
|
|
a9a8b6314b | ||
|
|
5c7be3bcfc | ||
|
|
e3893198af | ||
|
|
6a4e6dc5a1 | ||
|
|
643916f733 | ||
|
|
e964851d53 | ||
|
|
79e495dc14 | ||
|
|
13326879f0 | ||
|
|
293a871db4 | ||
|
|
4c9dc6350d | ||
|
|
4c54afdc55 | ||
|
|
d60841b040 | ||
|
|
7e640f47a5 | ||
|
|
f3d1b273f1 | ||
|
|
e80f5b85e9 | ||
|
|
1b5725caee | ||
|
|
820e8f8323 | ||
|
|
30672e52bb | ||
|
|
ac9796f50d | ||
|
|
66b7b4cad0 | ||
|
|
f417b9cc73 | ||
|
|
a304d4643c | ||
|
|
8386e5a2b5 | ||
|
|
27f57b96c0 | ||
|
|
d9a049d523 | ||
|
|
f53f724b04 | ||
|
|
a41105656a | ||
|
|
54269abfd1 | ||
|
|
e812ff1337 | ||
|
|
ae28f8f239 | ||
|
|
470f261dc0 | ||
|
|
80cfda23b6 | ||
|
|
5321c4d52b | ||
|
|
0466912f3e | ||
|
|
3d3932e657 | ||
|
|
d63117a17a | ||
|
|
ba06a72326 | ||
|
|
2671f6ca61 | ||
|
|
8dddfcbc92 | ||
|
|
ebc5793f36 | ||
|
|
4946221651 | ||
|
|
de9817a97f | ||
|
|
1218c4f385 | ||
|
|
e9cee62d03 | ||
|
|
fa7cf37eab | ||
|
|
b9ecb7556f | ||
|
|
661b38e1e6 | ||
|
|
769f4ba589 | ||
|
|
6118253ebf | ||
|
|
0f7f717330 | ||
|
|
0b16f1eb97 | ||
|
|
e733894c02 | ||
|
|
c47dcfbe27 | ||
|
|
8ae008ec9d | ||
|
|
45ef0c78a3 | ||
|
|
3ac46f239e | ||
|
|
1e106f7ada | ||
|
|
09738b2dd7 | ||
|
|
dc67a0b0fc | ||
|
|
1258bbc770 | ||
|
|
5960318a60 | ||
|
|
d4faf758f4 | ||
|
|
f88216b9a0 | ||
|
|
e21d648588 | ||
|
|
dbcbdc9bbc | ||
|
|
c5d16e8381 | ||
|
|
ac1aa6f3b1 | ||
|
|
866c222c74 | ||
|
|
06843477cb | ||
|
|
9a7a6fc6ab | ||
|
|
4986da415f | ||
|
|
cdbd20b794 | ||
|
|
49567ade65 | ||
|
|
4d857449d2 | ||
|
|
dc03621a14 | ||
|
|
b83cca17e2 | ||
|
|
343afffd40 | ||
|
|
163eac9511 | ||
|
|
167bfcb0b3 | ||
|
|
f0f94eb84c | ||
|
|
7123c9ca12 | ||
|
|
41e12d8e54 | ||
|
|
473ab14ae6 | ||
|
|
127ffd451c | ||
|
|
32bb43b66f | ||
|
|
765f5acbcc | ||
|
|
8ac137f22d | ||
|
|
10bbf6b157 | ||
|
|
390dc0605b | ||
|
|
6fda9f0691 | ||
|
|
0a1c5830d2 | ||
|
|
33efa2316d | ||
|
|
b3de9374a7 | ||
|
|
1d869e8d2b | ||
|
|
56156dd5ff | ||
|
|
35c9f55c41 | ||
|
|
e70e3559eb | ||
|
|
5957439475 | ||
|
|
322ed552ed | ||
|
|
bf848d5ef8 | ||
|
|
285057a9df | ||
|
|
4de62ffa55 | ||
|
|
f97fde1387 | ||
|
|
8a5bba625c | ||
|
|
108a209ac8 | ||
|
|
8390a4aba9 | ||
|
|
9740416e56 | ||
|
|
8cc82642f9 | ||
|
|
5f4804f4cf | ||
|
|
24dbee5d3d | ||
|
|
298f84d25e | ||
|
|
182c66f0a9 | ||
|
|
fd11c34646 | ||
|
|
6bcc7388bb | ||
|
|
60da0c0082 | ||
|
|
2b2b48bec4 | ||
|
|
d579f79399 | ||
|
|
bf85df68b3 | ||
|
|
e6e7ec49df | ||
|
|
e7801e1222 | ||
|
|
3327254022 | ||
|
|
d53d09e358 | ||
|
|
34c02ac391 | ||
|
|
cf4eb0c9cd | ||
|
|
ee6220b1fe | ||
|
|
a264638726 | ||
|
|
3e6a10a57d | ||
|
|
5258816770 | ||
|
|
fb4457f9a1 | ||
|
|
627a74038c | ||
|
|
54c297e898 | ||
|
|
a4d7201f7d | ||
|
|
b04cf69b90 | ||
|
|
ea654f0b8d | ||
|
|
9b16aaf9e4 | ||
|
|
87672de761 | ||
|
|
171a8def6d | ||
|
|
f09179f4cd | ||
|
|
03f62068b3 | ||
|
|
828b4230e3 | ||
|
|
2b23e5d472 | ||
|
|
17dbe70460 | ||
|
|
587c3a0647 | ||
|
|
83f0879b11 | ||
|
|
212b741828 | ||
|
|
ee81b608f9 | ||
|
|
ad17080ee8 | ||
|
|
6a8611c348 | ||
|
|
3ba3610b21 | ||
|
|
8deed23e01 | ||
|
|
356106263b | ||
|
|
0566244b61 | ||
|
|
17d76b4e6f | ||
|
|
9c3490f275 | ||
|
|
b1b6499d7c | ||
|
|
a36a74091f | ||
|
|
4dfbd676af | ||
|
|
f72f8b3e39 | ||
|
|
45cc6880f9 | ||
|
|
17281cd3b1 | ||
|
|
332b452b56 | ||
|
|
c647515186 | ||
|
|
6faf0ed9b9 | ||
|
|
fad024d9bb | ||
|
|
67718df278 | ||
|
|
273c3283c2 | ||
|
|
aa7306ebd1 | ||
|
|
56cb173ad5 | ||
|
|
174f0a9393 | ||
|
|
a1dbc1a6fb | ||
|
|
d9de2ee0bc | ||
|
|
cf1e0eda27 | ||
|
|
ccc82e5a61 | ||
|
|
0859f939dd | ||
|
|
519c133c0c | ||
|
|
9f62860821 | ||
|
|
238ed1ac2c | ||
|
|
cd5903b358 | ||
|
|
f78125c4fb | ||
|
|
6394241b9f | ||
|
|
d4dfc9bbab | ||
|
|
cf668be964 | ||
|
|
6ef443d724 | ||
|
|
75af217864 | ||
|
|
7a7343c3c8 | ||
|
|
18bfa8f562 | ||
|
|
a76a0815a0 | ||
|
|
332f384a65 | ||
|
|
3c5e1216c8 | ||
|
|
c7c11a1c1e | ||
|
|
0dd993c1d0 | ||
|
|
c977e9b59c | ||
|
|
81055b5500 | ||
|
|
c8df446846 | ||
|
|
97ac4621b9 | ||
|
|
9346c474cf | ||
|
|
d9a55965d3 | ||
|
|
50306343a8 | ||
|
|
08d7457620 | ||
|
|
b80a382d2f | ||
|
|
ab066bb441 | ||
|
|
4a64260d6e | ||
|
|
9390077007 | ||
|
|
baf5386e34 | ||
|
|
86e72b2f7f | ||
|
|
70bc02b12e | ||
|
|
6bce8b0195 | ||
|
|
dd4afb1d0a | ||
|
|
95493fe157 | ||
|
|
42b9ef3956 | ||
|
|
97ee7b7a7a | ||
|
|
230860f773 | ||
|
|
5f7f6d975e | ||
|
|
e7c95fdcc1 | ||
|
|
933367a685 | ||
|
|
4857307533 | ||
|
|
1ee50e42ca | ||
|
|
5bbc108331 | ||
|
|
11d62516d9 | ||
|
|
ef714a8bfc | ||
|
|
c6b8441a79 | ||
|
|
200ad86575 | ||
|
|
54fe2f260f | ||
|
|
e056cf743d | ||
|
|
8dbc747928 | ||
|
|
969b37e4e9 | ||
|
|
79a111668e | ||
|
|
9c90e3110b | ||
|
|
59237fb9c6 | ||
|
|
fc3705f019 | ||
|
|
15adbc0823 | ||
|
|
ca6472f5f2 | ||
|
|
de38322f34 | ||
|
|
ed45d30639 | ||
|
|
f99fc6f15f | ||
|
|
56aaa082da | ||
|
|
0aaa2f20bd | ||
|
|
bac589e3ec | ||
|
|
1dc7b02a3c | ||
|
|
83965afe68 | ||
|
|
0e9b110023 | ||
|
|
44f01a77fd | ||
|
|
58552b7a50 | ||
|
|
ddf3feac51 | ||
|
|
c2d59fcee1 | ||
|
|
ee3b2592aa | ||
|
|
ed79dfb220 | ||
|
|
6fbb942ad9 | ||
|
|
94a9340ee3 | ||
|
|
ef6d2b7d2b | ||
|
|
7f0b603f60 | ||
|
|
04ec31ea5c | ||
|
|
d1ff36c44a | ||
|
|
d703443469 | ||
|
|
5455a02c4d | ||
|
|
4bbb186f63 | ||
|
|
b62e8bf976 | ||
|
|
7ccc4fe33a | ||
|
|
b52d1cf1e2 | ||
|
|
6ee9db0eb8 | ||
|
|
36ac2a5bfb | ||
|
|
e1841c652a | ||
|
|
6fc2c88482 | ||
|
|
b8a13a5ba4 | ||
|
|
d36623c1fb | ||
|
|
06ab88556c | ||
|
|
b552790035 | ||
|
|
6610dd22df | ||
|
|
04dddf92d8 | ||
|
|
bedd9c97c2 | ||
|
|
81c859ba31 | ||
|
|
041e662e07 | ||
|
|
65c7c66b50 | ||
|
|
287f2762f9 | ||
|
|
3750e94ff5 | ||
|
|
e127d60345 | ||
|
|
5189298884 | ||
|
|
5a9c370b29 | ||
|
|
ec4f370f29 | ||
|
|
da194dd88d | ||
|
|
91e6b86eb3 | ||
|
|
550e2d59b4 | ||
|
|
80b7ef08c8 | ||
|
|
7c9ddf0c3f | ||
|
|
9abdb1d47b | ||
|
|
a5171dd9b4 | ||
|
|
4e7c04efbe | ||
|
|
d3c66dc750 | ||
|
|
7e14357902 | ||
|
|
92f6c2448f | ||
|
|
b05ebe8bdf | ||
|
|
0b62390a54 | ||
|
|
8c9b5f2a18 | ||
|
|
960894c329 | ||
|
|
a5786ecccf | ||
|
|
33a83928e7 | ||
|
|
149c6271b3 | ||
|
|
031d23df0c | ||
|
|
30134bf3b5 | ||
|
|
7907150f17 | ||
|
|
e5f9639d1e | ||
|
|
47bad16b54 | ||
|
|
efa944ef8c | ||
|
|
68d4cfff16 | ||
|
|
c9c6c4c5ea | ||
|
|
792b3a1267 | ||
|
|
5b64cf6925 | ||
|
|
097878b4b2 | ||
|
|
32eca9aabf | ||
|
|
7a27432885 | ||
|
|
92e4249eec | ||
|
|
6fac0aa0f5 | ||
|
|
cd2ede5ce8 | ||
|
|
ac407375ab | ||
|
|
1730effc55 | ||
|
|
21c063ac53 | ||
|
|
2ba2ef9617 | ||
|
|
cfe2424cff | ||
|
|
397fe2a0c8 | ||
|
|
e846cc532c | ||
|
|
d273f99940 | ||
|
|
f0763c2b67 | ||
|
|
48d6293ab6 | ||
|
|
0e270595df | ||
|
|
db6770159e | ||
|
|
e6bf1ed6f7 | ||
|
|
996dd11e24 | ||
|
|
4ea1f20022 | ||
|
|
a56cfc5688 | ||
|
|
d71d05374f | ||
|
|
99b82b24d0 | ||
|
|
5cfe624e25 | ||
|
|
62066e04e3 | ||
|
|
5478c014f1 | ||
|
|
aea3147454 | ||
|
|
e466de4d0e | ||
|
|
0de3cf72b8 | ||
|
|
41fa97994e | ||
|
|
d03a563e0d | ||
|
|
0f89c0b112 | ||
|
|
07ae096af2 | ||
|
|
11e4d482f4 | ||
|
|
f959e3b869 | ||
|
|
ec19bfb556 | ||
|
|
fa4edf9845 | ||
|
|
ab52430846 | ||
|
|
6045869260 | ||
|
|
c08a598658 | ||
|
|
340483bf7f | ||
|
|
87395e0ec4 | ||
|
|
694194dd01 | ||
|
|
aa6935820d | ||
|
|
57c038dca6 | ||
|
|
fe69b1419f | ||
|
|
0cf7365755 | ||
|
|
7e96f776ff | ||
|
|
e5ce3d46de | ||
|
|
f097cb3c3a | ||
|
|
23aea7b794 | ||
|
|
c2839fdc29 | ||
|
|
3fbd64338c | ||
|
|
36a2917b17 | ||
|
|
9f3fc43791 | ||
|
|
4774d2d1fd | ||
|
|
91de8a5a8f | ||
|
|
652005264b | ||
|
|
98967410cb | ||
|
|
55b2e3f1ff | ||
|
|
cf8ddc98af | ||
|
|
f259fa85a5 | ||
|
|
5ea6c7739f | ||
|
|
eac071af9d | ||
|
|
2b0f424af6 | ||
|
|
fcbfd28025 | ||
|
|
37982fe416 | ||
|
|
6824c85820 | ||
|
|
bd9f76222a | ||
|
|
349e771f74 | ||
|
|
83c2993cef | ||
|
|
dbb91fa140 | ||
|
|
849ca6803d | ||
|
|
f41288f441 | ||
|
|
9e974d5c30 | ||
|
|
62d25ee917 | ||
|
|
652a5dd27d | ||
|
|
1051bfa661 | ||
|
|
3a3c481e3b | ||
|
|
9642c58d94 | ||
|
|
72d37ab218 | ||
|
|
a2047f9498 | ||
|
|
22a94130f2 | ||
|
|
6a907ff0c2 | ||
|
|
11e0ca29d4 | ||
|
|
47cd4ef790 | ||
|
|
25572a9d57 | ||
|
|
02b94e5672 | ||
|
|
23790fefd6 | ||
|
|
3c8e4a24c6 | ||
|
|
0f754055ab | ||
|
|
ac7c2612b2 | ||
|
|
7b54ff2046 | ||
|
|
c7743ffdbe | ||
|
|
7a604d39bd | ||
|
|
7c67f8bba1 | ||
|
|
6f2ca41eb6 | ||
|
|
b26ac16201 | ||
|
|
71b7ba70d1 | ||
|
|
96bd62e0cc | ||
|
|
c2ce598d0f | ||
|
|
cf56069804 | ||
|
|
7834b535d6 | ||
|
|
4b440199a2 | ||
|
|
4facfa5c7a | ||
|
|
99ab247c06 | ||
|
|
5cf12fced5 | ||
|
|
5abc92308a | ||
|
|
ed50ca5b53 | ||
|
|
3e8eb1a6eb | ||
|
|
d360c584c1 | ||
|
|
55482bf28a | ||
|
|
bcf5b4c1b2 | ||
|
|
7098e28532 | ||
|
|
ab8aa2e1ae | ||
|
|
97fc9a8b37 | ||
|
|
5f91a4561b | ||
|
|
b5dd56f187 | ||
|
|
819f0b7d7e | ||
|
|
16399d4512 | ||
|
|
86df184463 | ||
|
|
a4a2d66b85 | ||
|
|
1fb972cf2a | ||
|
|
f1ab0e3449 | ||
|
|
5032646090 | ||
|
|
2facce9633 | ||
|
|
bce50b991e | ||
|
|
bd1a5ef046 | ||
|
|
47e9b4b587 | ||
|
|
676e6127cd | ||
|
|
7b192a4e14 | ||
|
|
e3239ed0b3 | ||
|
|
1423454191 | ||
|
|
285b6367ed | ||
|
|
2907403f82 | ||
|
|
9a735639d4 | ||
|
|
375e3c4636 | ||
|
|
ab3d7078bf | ||
|
|
3b191b917b | ||
|
|
4be129917a | ||
|
|
507f508497 | ||
|
|
4b063b3493 | ||
|
|
8e457a20bc | ||
|
|
b9716cf2a3 | ||
|
|
b05a4ab83a | ||
|
|
ca75eb651d | ||
|
|
a6329fd3c0 | ||
|
|
89e33e984b | ||
|
|
40c0b85a10 | ||
|
|
7cdb199855 | ||
|
|
8c165dc17f | ||
|
|
43fe21a87d | ||
|
|
4b7559f2ec | ||
|
|
9e026034d7 | ||
|
|
9466ccb12a | ||
|
|
f53f314f0b | ||
|
|
9b53a3a052 | ||
|
|
7f8a95646a | ||
|
|
88d452a9fa | ||
|
|
ee2e1412d6 | ||
|
|
f2ea6af1c3 | ||
|
|
76bd5d7518 | ||
|
|
70626ccb79 | ||
|
|
124fca6d1f | ||
|
|
37154cc1ef | ||
|
|
9f42ad5746 | ||
|
|
94c19c8724 | ||
|
|
c3dd9a5f14 | ||
|
|
8f2a6acb7b | ||
|
|
821817fc89 | ||
|
|
0f3097590f | ||
|
|
20826fff54 | ||
|
|
9067e1d8d1 | ||
|
|
dbf983e287 | ||
|
|
f81043133a | ||
|
|
cab9b0bd1e | ||
|
|
d9b42016dc | ||
|
|
5ee5a23b29 | ||
|
|
a1f33d93c1 | ||
|
|
1f9a19c37a | ||
|
|
9e9d371a3f | ||
|
|
b97136a606 | ||
|
|
f1df463a07 | ||
|
|
e59c335cac | ||
|
|
a9280edf97 | ||
|
|
68eca70a31 | ||
|
|
dd68c35295 | ||
|
|
57530af792 | ||
|
|
4947fa91ef | ||
|
|
c605cc77d5 | ||
|
|
6fe6b681ac | ||
|
|
08884af624 | ||
|
|
2adfbb5169 | ||
|
|
831c3c4fa4 | ||
|
|
df87b51e63 | ||
|
|
a17fceac39 | ||
|
|
f6e05a27d8 | ||
|
|
19cb4ee7a4 | ||
|
|
8d4f853432 | ||
|
|
76eaac5d31 | ||
|
|
5c02943ab3 | ||
|
|
09a38e2dcd | ||
|
|
4a772953dd | ||
|
|
be544e09cf | ||
|
|
8a817980db | ||
|
|
1dbfbe5d5f | ||
|
|
53ed0dc12d | ||
|
|
1bbff65f5d | ||
|
|
8e5e9ab517 | ||
|
|
c2d37932e1 | ||
|
|
7e7bbd4591 | ||
|
|
6d2d3c4c3b | ||
|
|
a1ba7f9e5f | ||
|
|
b89949e361 | ||
|
|
72ad111198 | ||
|
|
00b4e9a9b4 | ||
|
|
a460fe12a1 | ||
|
|
7e4fdcd9a3 | ||
|
|
0f4d06b4d4 | ||
|
|
bfbf6edc87 | ||
|
|
5027e2f03a | ||
|
|
70ea05c0ca | ||
|
|
5e9d9abe10 | ||
|
|
128495dd91 | ||
|
|
314a8c0741 | ||
|
|
8c6ca6f8f7 | ||
|
|
421bb3b701 | ||
|
|
a8e9ef49f4 | ||
|
|
2474ad8d1d | ||
|
|
dd58ac01ed | ||
|
|
fd4fc377a1 | ||
|
|
3b31cec3e7 | ||
|
|
28fc95dd1f | ||
|
|
d7dc51980a | ||
|
|
1fd1f08f33 | ||
|
|
f43ec50369 | ||
|
|
d4b6b5744d | ||
|
|
3ae9ddf0f9 | ||
|
|
4a61e5cbdb | ||
|
|
44b031af15 | ||
|
|
ad197f85de | ||
|
|
e7c2990b79 | ||
|
|
21aa33c862 | ||
|
|
3f8cd143b6 | ||
|
|
5d25fec242 | ||
|
|
8aec9f3f25 | ||
|
|
81d14cbecc | ||
|
|
6f1f4cc576 | ||
|
|
f81be0a075 | ||
|
|
2aa57404f2 | ||
|
|
d43cb4b097 | ||
|
|
7d69100d2d | ||
|
|
c04a975dba | ||
|
|
59f920445c | ||
|
|
72425b243b | ||
|
|
8af777fac4 | ||
|
|
510d94b21c | ||
|
|
476cfec461 | ||
|
|
642869ffed | ||
|
|
4e6a552689 | ||
|
|
707581f103 | ||
|
|
ef68d43acb | ||
|
|
50f15f7539 | ||
|
|
9f2ae92236 | ||
|
|
854f14a014 | ||
|
|
cdeef6aae0 | ||
|
|
5000a56620 | ||
|
|
e5438a7c37 | ||
|
|
ed984a0642 | ||
|
|
077c88b778 | ||
|
|
2077b0a35f | ||
|
|
8cbffd7c11 | ||
|
|
3bc3038c2f | ||
|
|
ee3c54ecd0 | ||
|
|
89b10a8ccf | ||
|
|
31d702c550 | ||
|
|
a5f98cb46b | ||
|
|
b88156a672 | ||
|
|
311a53e4ce | ||
|
|
bd55bb88b9 | ||
|
|
7461c5ee1f | ||
|
|
6daf5b6afa | ||
|
|
32c9678513 | ||
|
|
2f74296de6 | ||
|
|
2d32d53075 | ||
|
|
9fa389bb99 | ||
|
|
c9151306da | ||
|
|
5d11777dd8 | ||
|
|
eab9ced72f | ||
|
|
929a72be79 | ||
|
|
c62c8d338e | ||
|
|
524af22cb0 | ||
|
|
b7f891ce39 | ||
|
|
178a20c6d4 | ||
|
|
83e13ae782 | ||
|
|
dc654b89fb | ||
|
|
bee2e084ba | ||
|
|
9ea3f9fd80 | ||
|
|
5d78740f6f | ||
|
|
9cda942dea | ||
|
|
2b728114a2 | ||
|
|
6f1ab140a6 | ||
|
|
8655349f00 | ||
|
|
d75edae498 | ||
|
|
6d5970adcb | ||
|
|
40f925e660 | ||
|
|
d9aab7db19 | ||
|
|
6f7a12c8be | ||
|
|
48d7d5a0b9 | ||
|
|
d459270d7d | ||
|
|
9e7a7ad920 | ||
|
|
ffcd3aa3c2 | ||
|
|
ecd933f94a | ||
|
|
9db8365930 | ||
|
|
64187169a0 | ||
|
|
f016ba700c | ||
|
|
02076b680e | ||
|
|
a6e16ad99f | ||
|
|
3f2de363c9 | ||
|
|
511d8d0cb4 | ||
|
|
4798d6b7e5 | ||
|
|
47775d2758 | ||
|
|
789e22ea08 | ||
|
|
fb8badbfd9 | ||
|
|
1c36f33a69 | ||
|
|
58dd31611d | ||
|
|
7d9033a212 | ||
|
|
564255978d | ||
|
|
cc87b603fb | ||
|
|
118a0b4214 | ||
|
|
b56580e3d1 | ||
|
|
2a1b77ff6b | ||
|
|
d94a54e392 | ||
|
|
1ab5db3183 | ||
|
|
560348fcfe | ||
|
|
e9f74bfb4d | ||
|
|
13eec1c31a | ||
|
|
aa64ca58f4 | ||
|
|
018c3a1cc6 | ||
|
|
e0bf4394b7 | ||
|
|
d684a3ede2 | ||
|
|
0bb1ab04d3 | ||
|
|
f35f5ccfe4 | ||
|
|
dc58737310 | ||
|
|
f246415f27 | ||
|
|
e7badbf124 | ||
|
|
caddb16b53 | ||
|
|
0d18206ae6 | ||
|
|
71c82c0f0d | ||
|
|
cc52fdcf5a | ||
|
|
6d31991fda | ||
|
|
7988f8cc1b | ||
|
|
59366d6f33 | ||
|
|
4f7970cb0e | ||
|
|
5794b2e977 | ||
|
|
291feff903 | ||
|
|
19ce81e995 | ||
|
|
4b20d5b954 | ||
|
|
90221a0e16 | ||
|
|
085036bc93 | ||
|
|
21186d0858 | ||
|
|
22e8d15f60 | ||
|
|
f258e6d17f | ||
|
|
a6a4185585 | ||
|
|
9924a574a8 | ||
|
|
7282d02c22 | ||
|
|
1ea62c3c60 | ||
|
|
5929a37d73 | ||
|
|
f4a0ec30ec | ||
|
|
c5c1e3c1ca | ||
|
|
449c74eb66 | ||
|
|
016717c8ef | ||
|
|
75bb80d343 | ||
|
|
c04c32e876 | ||
|
|
09025e0a8f | ||
|
|
13e845a5da | ||
|
|
2f4284f806 | ||
|
|
d4af5926cb | ||
|
|
0ad2f463ab | ||
|
|
3b49f9947f | ||
|
|
514b536f57 | ||
|
|
040143f557 | ||
|
|
c75d0cc989 | ||
|
|
701ac3f85d | ||
|
|
f283cff6bc | ||
|
|
d8e62cc541 | ||
|
|
8853c8a124 | ||
|
|
027fa4119b | ||
|
|
5424375e0d | ||
|
|
6007658c72 | ||
|
|
e0916858a8 | ||
|
|
1f397978d6 | ||
|
|
518f58885e | ||
|
|
ece24fa24b | ||
|
|
6524eb2b55 | ||
|
|
b16a6b1ab1 | ||
|
|
cda2ca43dd | ||
|
|
3375d30fad | ||
|
|
6b61c462a0 | ||
|
|
226fab5904 | ||
|
|
be2791462e | ||
|
|
9b38e6cb8f | ||
|
|
bce7830b86 | ||
|
|
4e6a3311a4 | ||
|
|
f8c1c87b01 | ||
|
|
94c3ae6533 | ||
|
|
8ca1ed17d4 | ||
|
|
a645363091 | ||
|
|
7db3c29e14 | ||
|
|
4c28475c0d | ||
|
|
c509f17789 | ||
|
|
6690ea7c0e | ||
|
|
347942668d | ||
|
|
1a3643d6f8 | ||
|
|
0af64210c5 | ||
|
|
7989b8eb0e | ||
|
|
4d902a03ca | ||
|
|
8af5b826b2 | ||
|
|
953b8e4fb2 | ||
|
|
95c4af6f84 | ||
|
|
f2be35ee94 | ||
|
|
056bc1af22 | ||
|
|
46501093ce | ||
|
|
0344e3f633 | ||
|
|
7882607c6e | ||
|
|
684c02b1c5 | ||
|
|
7d8889d045 | ||
|
|
a05b293582 | ||
|
|
c37085a1c7 | ||
|
|
bb8d7e2339 | ||
|
|
84c87d2dee | ||
|
|
710566e817 | ||
|
|
a1ca96017c | ||
|
|
686868519c | ||
|
|
f16f0e7e08 | ||
|
|
f96bf404e6 | ||
|
|
6d6a80dd4d | ||
|
|
94532e8598 | ||
|
|
254449ec1a | ||
|
|
3c69021c6c | ||
|
|
68955d67bb | ||
|
|
bc1eadad26 | ||
|
|
ebe6245243 | ||
|
|
d12c624b23 | ||
|
|
11c1668d65 | ||
|
|
158c27e52b | ||
|
|
36b777da8f | ||
|
|
012010957d | ||
|
|
b0f041328d | ||
|
|
94a12d9767 | ||
|
|
06cbb3e7f8 | ||
|
|
47de40856b | ||
|
|
c044fa2932 | ||
|
|
10f2145c15 | ||
|
|
de89355b05 | ||
|
|
54f9eed786 | ||
|
|
d8e73e9cb4 | ||
|
|
ba7f07e1c8 | ||
|
|
9353015ddc | ||
|
|
780e300815 | ||
|
|
4ed6464bbe | ||
|
|
8a9ed91051 | ||
|
|
784fabc288 | ||
|
|
1a25d1b866 | ||
|
|
b4bd2c3785 | ||
|
|
c72a18c7e4 | ||
|
|
51095f4a9f | ||
|
|
f2af835a02 | ||
|
|
fa8965f7bd | ||
|
|
e4da8a26b0 | ||
|
|
45351a504f | ||
|
|
48ce3fd4d0 | ||
|
|
98825a7d5f | ||
|
|
7011688812 | ||
|
|
ea8a3049cd | ||
|
|
d2c1e7aca5 | ||
|
|
2d80e831be | ||
|
|
c83e7b2b14 | ||
|
|
c7c71bc785 | ||
|
|
8c0e27b83b | ||
|
|
d275837da5 | ||
|
|
c1ecb703bf | ||
|
|
f804b56f92 | ||
|
|
d7a04c86f3 | ||
|
|
652b76ba50 | ||
|
|
359de1a1e5 | ||
|
|
eecec3106b | ||
|
|
f233b23569 | ||
|
|
0862a81322 | ||
|
|
6421fc9519 | ||
|
|
b1ef59bff9 | ||
|
|
cd7670ad11 | ||
|
|
79fa482f70 | ||
|
|
11705f7ccd | ||
|
|
11da4794b5 | ||
|
|
5cc6ae012a | ||
|
|
938c321a54 | ||
|
|
867422a2d9 | ||
|
|
8a547877b5 | ||
|
|
3e301af80c | ||
|
|
41ad6dc339 | ||
|
|
2f17f0d4ae | ||
|
|
f1f99a1d92 | ||
|
|
ad6fbe2bf3 | ||
|
|
e0bfad4807 | ||
|
|
3572ab9452 | ||
|
|
30ed285dcf | ||
|
|
2881491cc2 | ||
|
|
96f4d3e136 | ||
|
|
9e9ab6ae13 | ||
|
|
029cad9224 | ||
|
|
0029315c2e | ||
|
|
247c3b58ec | ||
|
|
7a165adfb8 | ||
|
|
5777319bfa | ||
|
|
9219c5a1ad | ||
|
|
096295d258 | ||
|
|
aa26e0c44d | ||
|
|
8fcd0c5e4c | ||
|
|
1a26487df3 | ||
|
|
7afedf59bf | ||
|
|
e81267995d | ||
|
|
9c206629b1 | ||
|
|
1e8dcdf4ea | ||
|
|
027e120c87 | ||
|
|
09de9e5fda | ||
|
|
b1c64bbd98 | ||
|
|
7ec6f6caad | ||
|
|
85babbeb01 | ||
|
|
0006223494 | ||
|
|
5f1bf408f7 | ||
|
|
8e8fd019c3 | ||
|
|
c7b98005bb | ||
|
|
508e95b9d8 | ||
|
|
5245df3ce3 | ||
|
|
ec66dd5649 | ||
|
|
d28c3f9ba3 | ||
|
|
64705b393a | ||
|
|
42b4557de7 | ||
|
|
d089a06566 | ||
|
|
4bbfdf8e85 | ||
|
|
1e6db0f0a5 | ||
|
|
0778c150b6 | ||
|
|
30a7170f50 | ||
|
|
b9ca828522 | ||
|
|
5f73d1e57a | ||
|
|
b0f4c4b8e0 | ||
|
|
10e343b8bb | ||
|
|
0ab5630ebf | ||
|
|
86a47ad533 | ||
|
|
fecf36a7ff | ||
|
|
16b392d360 | ||
|
|
dd6709c14a | ||
|
|
69622ad25a | ||
|
|
7e6a5642c1 | ||
|
|
ed9923dbf9 | ||
|
|
bb13d0b6f9 | ||
|
|
423e00eccd | ||
|
|
90ffa50718 | ||
|
|
4f0ed25e8e | ||
|
|
3f99cc7a6d | ||
|
|
a7e9d43411 | ||
|
|
c79bc6e32f | ||
|
|
a2b18aaec4 | ||
|
|
886e96f5d6 | ||
|
|
88388fc5b1 | ||
|
|
7cd4dec4df | ||
|
|
567c43f2f0 | ||
|
|
566a2509c9 | ||
|
|
dbaad2e169 | ||
|
|
2fdf9f9a8c | ||
|
|
ebfcef12a7 | ||
|
|
a0fc26afd7 | ||
|
|
5c8c8b6502 | ||
|
|
7e38749e40 | ||
|
|
e538336c5e | ||
|
|
f14b519756 | ||
|
|
034a1b3991 | ||
|
|
a2144d21c2 | ||
|
|
4a50a3161d | ||
|
|
d983b1f329 | ||
|
|
c1d5429245 | ||
|
|
c094016c33 | ||
|
|
5caf503784 | ||
|
|
93cd9f6880 | ||
|
|
7c7f5220da | ||
|
|
09e916fe18 | ||
|
|
471d9eed35 | ||
|
|
4457385281 | ||
|
|
c37d999ff3 | ||
|
|
6813b01c20 | ||
|
|
2e6032f81b | ||
|
|
001e9989b8 | ||
|
|
45dc0a2a72 | ||
|
|
dbb511b2f9 | ||
|
|
cbd13b49ef | ||
|
|
5902a7f113 | ||
|
|
70e71fc1da | ||
|
|
91d941b663 | ||
|
|
75ca1ff498 | ||
|
|
b0e5ad3ef5 | ||
|
|
0c12ff7662 | ||
|
|
d44c6c593d | ||
|
|
cdce35ec7a | ||
|
|
20210d7d54 | ||
|
|
8ade843108 | ||
|
|
faab0e841e | ||
|
|
efae6afd5a | ||
|
|
bb8f13d57b | ||
|
|
ff1903b1cc | ||
|
|
db193bd94a | ||
|
|
0ad1d57785 | ||
|
|
0c340286ef | ||
|
|
e60d681992 | ||
|
|
fff48260be | ||
|
|
2e7752559d | ||
|
|
aa000e46ed | ||
|
|
8ed441d968 | ||
|
|
c711a0c32c | ||
|
|
2bdfe2e12a | ||
|
|
1023fd90f1 | ||
|
|
059a59c746 | ||
|
|
2363a1fbb6 | ||
|
|
26b2cc816e | ||
|
|
93fc4950df | ||
|
|
f042076cab | ||
|
|
9a6fd69885 | ||
|
|
ffbf4b07f1 | ||
|
|
88ac570031 | ||
|
|
70c5d4eb43 | ||
|
|
1899f97b1e | ||
|
|
56cbb44f94 | ||
|
|
42c6b70f2b | ||
|
|
f673a36ccd | ||
|
|
80f319311e | ||
|
|
d256d5f7da | ||
|
|
0b7737a054 | ||
|
|
89f0c01acc | ||
|
|
d66cde252d | ||
|
|
c9103bd521 | ||
|
|
d2e87b0e13 | ||
|
|
a7eb042276 | ||
|
|
114796006f | ||
|
|
38932f14cc | ||
|
|
3ee1286cf4 | ||
|
|
ffef5923c6 | ||
|
|
e670c679df | ||
|
|
06ba235947 | ||
|
|
db72bb33f2 | ||
|
|
0c700cd1f8 | ||
|
|
721b63b197 | ||
|
|
41ca477b8a | ||
|
|
9fd5220e8c | ||
|
|
65be8cd2ce | ||
|
|
4b24ed4a21 | ||
|
|
93bdb4236e | ||
|
|
db82a14837 | ||
|
|
b6cb460085 | ||
|
|
358bb18f8f | ||
|
|
bd2cc60aae | ||
|
|
f00bd31eaf | ||
|
|
191ca88daa | ||
|
|
611571d566 | ||
|
|
11550edcd0 | ||
|
|
3c51f44190 | ||
|
|
ee3fbcae78 | ||
|
|
63ad119e63 | ||
|
|
bfcae3fc70 | ||
|
|
21b261b2d3 | ||
|
|
9ac94167d2 | ||
|
|
170c782ba2 | ||
|
|
7465259340 | ||
|
|
c871e8b64e | ||
|
|
1dfaa93568 | ||
|
|
866c480634 | ||
|
|
ae6fbc1acb | ||
|
|
1cbf9c5eb8 | ||
|
|
210da6c94b | ||
|
|
5be889d169 | ||
|
|
03f992bfc5 | ||
|
|
db6afdbfbb | ||
|
|
624c562c1f | ||
|
|
4809eccb9e | ||
|
|
1f9f46175c | ||
|
|
71b16e1824 | ||
|
|
3975b2c6e3 | ||
|
|
48bcb02f82 | ||
|
|
9fd83e56e2 | ||
|
|
a40742a45c | ||
|
|
4d10e7e290 | ||
|
|
83f1a1ef8e | ||
|
|
47581eb453 | ||
|
|
045ac03837 | ||
|
|
6439845d32 | ||
|
|
0b0721b9d3 | ||
|
|
f1ba65affa | ||
|
|
e60bdbf80c | ||
|
|
2269b500f3 | ||
|
|
089f2e964c | ||
|
|
1e477f71ef | ||
|
|
430e8efd42 | ||
|
|
bddd7e5417 | ||
|
|
10deb4c51a | ||
|
|
530c3a5576 | ||
|
|
4e5cb34051 | ||
|
|
e7c5eb6bee | ||
|
|
f2cb6ed1a6 | ||
|
|
ce745204f7 | ||
|
|
8b3c9f2f72 | ||
|
|
2aa1c85972 | ||
|
|
766e5db363 | ||
|
|
c1504e104d | ||
|
|
3705b740c9 | ||
|
|
85889ee338 | ||
|
|
32254fbed8 | ||
|
|
62fbac2d19 | ||
|
|
6b02bb973f | ||
|
|
1ad15035de | ||
|
|
dd7a21010a | ||
|
|
f69ab641f7 | ||
|
|
27308b59ea | ||
|
|
f1aa9fe906 | ||
|
|
112b28342a | ||
|
|
2f9fe92b6c | ||
|
|
4a45c78512 | ||
|
|
0c78f66c21 | ||
|
|
be6f934e7b | ||
|
|
79fa11f2c9 | ||
|
|
349966a7c3 | ||
|
|
4867825367 | ||
|
|
36686afe04 | ||
|
|
9547896074 | ||
|
|
4ecb737ea7 | ||
|
|
0147f398ec | ||
|
|
3fd0420b52 | ||
|
|
decba39373 | ||
|
|
15972a9203 | ||
|
|
94c0a52824 | ||
|
|
d583f52d7e | ||
|
|
9b243f90ee | ||
|
|
18168e4577 | ||
|
|
a18e5454ce | ||
|
|
7bf16f2891 | ||
|
|
8491387a15 | ||
|
|
123ae18a64 | ||
|
|
f3a19597fc | ||
|
|
6c6ccbf07c | ||
|
|
09af9a2576 | ||
|
|
cf813d7eb4 | ||
|
|
f0ca523dfb | ||
|
|
6a08443853 | ||
|
|
030b9b0c82 | ||
|
|
8a0b511649 | ||
|
|
7853b830ae | ||
|
|
77eb7f7821 | ||
|
|
65e71ac56f | ||
|
|
a2a1bcb77b | ||
|
|
e8fadc638c | ||
|
|
507a8330b7 | ||
|
|
1dc1c99c33 | ||
|
|
0cd89585e2 | ||
|
|
c54148c291 | ||
|
|
f7d7fa5031 | ||
|
|
d959bd6c34 | ||
|
|
92240d3e8b | ||
|
|
0c634a4fa6 | ||
|
|
c8e2414a70 | ||
|
|
a6956b34db | ||
|
|
055c2ec202 | ||
|
|
1c0e03eadc | ||
|
|
f8c44cf2d3 | ||
|
|
d99eeb1e1e | ||
|
|
c42a4d9d1c | ||
|
|
2b8eb11bc3 | ||
|
|
f4f62c6edf | ||
|
|
142f75e662 | ||
|
|
1bc19921ff | ||
|
|
d901b795f7 | ||
|
|
de19e3abec | ||
|
|
ee1ab72d0a | ||
|
|
c699dd71af | ||
|
|
3452bafd4b | ||
|
|
0aabac338d | ||
|
|
a9a25a9197 | ||
|
|
aa295e9688 | ||
|
|
9cf21b1192 | ||
|
|
7efb6fc125 | ||
|
|
a050af7985 | ||
|
|
25e3fd90c5 | ||
|
|
e79384a3c4 | ||
|
|
c784af67da | ||
|
|
74d4681f33 | ||
|
|
eaa939eda3 | ||
|
|
a3b21debb0 | ||
|
|
db9168e179 | ||
|
|
d68a5106a5 | ||
|
|
5244d7af1e | ||
|
|
0ee095617e | ||
|
|
8d7d022576 | ||
|
|
098cf78753 | ||
|
|
4131811e32 | ||
|
|
260cd62329 | ||
|
|
b6b8ce55c6 | ||
|
|
1d3e5a60e2 | ||
|
|
b2397cfd54 | ||
|
|
15ef3bdadb | ||
|
|
c353e713cc | ||
|
|
1eaaad320f | ||
|
|
e0c0071d8b | ||
|
|
0a7c274f33 | ||
|
|
d4621f8b8a | ||
|
|
5b0c1fd16e | ||
|
|
6b5847f149 | ||
|
|
b227d89a01 | ||
|
|
a4191556ea | ||
|
|
a7d90c0b39 | ||
|
|
6edef4ec97 | ||
|
|
17f7ae7daa | ||
|
|
03d33f2074 | ||
|
|
8787d8ad43 | ||
|
|
69f98aa756 | ||
|
|
9620faafe1 | ||
|
|
2ba0b164fc | ||
|
|
77b9b2e8ce | ||
|
|
58be436aaa | ||
|
|
5995eb8b8e | ||
|
|
0e4f942b15 | ||
|
|
c14f773ed4 | ||
|
|
3ef08a53d1 | ||
|
|
67d32c5a96 | ||
|
|
e351793043 | ||
|
|
f0a987e7a2 | ||
|
|
c0354961f7 | ||
|
|
4c8c8dcdab | ||
|
|
cae80096e1 | ||
|
|
45d4fcc0bf | ||
|
|
da5380a472 | ||
|
|
b27544663e | ||
|
|
75c35aa9e5 | ||
|
|
ad0fd8d5c8 | ||
|
|
c60cd1fabb | ||
|
|
7cfb6258d5 | ||
|
|
ff1bc2577f | ||
|
|
07ab3621b3 | ||
|
|
0af3f3fcce | ||
|
|
cb8a70744d | ||
|
|
c6a8d3e251 | ||
|
|
d871292d39 | ||
|
|
255febcd70 | ||
|
|
c7d8f77fb4 | ||
|
|
248de4ef0f | ||
|
|
56ae5d2c33 | ||
|
|
2cc575d74b | ||
|
|
b0b68d16a4 | ||
|
|
45fd510e15 | ||
|
|
0376a58374 | ||
|
|
b1d1d2c7f8 | ||
|
|
6de14c91e4 | ||
|
|
a04cbeba78 | ||
|
|
82610e281d | ||
|
|
e9b3213d8b | ||
|
|
313ee50bf2 | ||
|
|
fca72e3fad | ||
|
|
97657da95d | ||
|
|
f32240ba65 | ||
|
|
1d8d20a7d7 | ||
|
|
5fba2685b1 | ||
|
|
2c39551560 | ||
|
|
265ca8b433 | ||
|
|
8c4b33ec17 | ||
|
|
937bf6f57a | ||
|
|
e2f142ed28 | ||
|
|
9a835690aa | ||
|
|
5f4ec1c446 | ||
|
|
7b11516520 | ||
|
|
4a6543b0c6 | ||
|
|
fa197c1b7f | ||
|
|
ab4ab7e63b | ||
|
|
b88a8e605a | ||
|
|
519d8fc64a | ||
|
|
8032fde262 | ||
|
|
02b4b0b9b6 | ||
|
|
933c820f20 | ||
|
|
8476fdb77f | ||
|
|
ae1f280062 | ||
|
|
d58dd28051 | ||
|
|
a212382da6 | ||
|
|
b1819ff755 | ||
|
|
2317305f0e | ||
|
|
da5f7e27c4 | ||
|
|
4c4dd71195 | ||
|
|
61c3d3c34a | ||
|
|
59d4e30469 | ||
|
|
e0dd34df2b | ||
|
|
41366d9376 | ||
|
|
1779b8d67e | ||
|
|
b673cbe411 | ||
|
|
1a595c5043 | ||
|
|
bce5ae88ee | ||
|
|
b81d15cdef | ||
|
|
ec6747ac09 | ||
|
|
5cd35bbfb5 | ||
|
|
1b07509e18 | ||
|
|
35211b94db | ||
|
|
0e9338a0e4 | ||
|
|
95b099d62c | ||
|
|
b295963d8d | ||
|
|
884ca797d8 | ||
|
|
3664b4acec | ||
|
|
95f33310bf | ||
|
|
a1500051b9 | ||
|
|
be1a6cc985 | ||
|
|
32525a0204 | ||
|
|
8b64c8afd3 | ||
|
|
2795413fc1 | ||
|
|
8db9fc3f17 | ||
|
|
d7c2fb8a02 | ||
|
|
6c9d09f6c3 | ||
|
|
1482fe3e1b | ||
|
|
c2f0422a68 | ||
|
|
4b3a7af308 | ||
|
|
7b2cd6e4b0 | ||
|
|
60dbe28dca | ||
|
|
3dde8c5905 | ||
|
|
9207ef357f | ||
|
|
ce7ea27255 | ||
|
|
c3eb701c56 | ||
|
|
180c1fe185 | ||
|
|
9b3138102d | ||
|
|
e43412c02f | ||
|
|
a223fad28c | ||
|
|
9ade31a813 | ||
|
|
fd91024b8d | ||
|
|
0d9526ed72 | ||
|
|
d4e51a1b51 | ||
|
|
ded26df5d0 | ||
|
|
a3ba44771f | ||
|
|
83f4ac980e | ||
|
|
5da102c058 | ||
|
|
27608327af | ||
|
|
fcbbd53dd8 | ||
|
|
76aaab319c | ||
|
|
6ea6a46c15 | ||
|
|
c0f3e62a10 | ||
|
|
5f71660c7a | ||
|
|
e6b15de5a4 | ||
|
|
bf74569906 | ||
|
|
b70f1f84be | ||
|
|
a2209e73fc | ||
|
|
f77ebe3af7 | ||
|
|
8c4370780d | ||
|
|
e4b352f1ce | ||
|
|
a679e33d54 | ||
|
|
e477ce3d9a | ||
|
|
f15d7ac445 | ||
|
|
d36af364bf | ||
|
|
b0ebcf86e6 | ||
|
|
247ec75c9e | ||
|
|
3b43bf3579 | ||
|
|
2be6d5cdef | ||
|
|
fef7526af4 | ||
|
|
88ee26ecf8 | ||
|
|
947c602cb0 | ||
|
|
fd0cf4e4e3 | ||
|
|
0d11bffa82 | ||
|
|
c9a9c48de3 | ||
|
|
fbbcb58e3f | ||
|
|
ed9dbea34a | ||
|
|
9ccca42011 | ||
|
|
d7bc92e465 | ||
|
|
5d29ebfde2 | ||
|
|
efa14f2dcd | ||
|
|
2e07802a05 | ||
|
|
34a83a46d9 | ||
|
|
9db5c68a64 | ||
|
|
5dac84c511 | ||
|
|
ce834c9b18 | ||
|
|
0af5f626bc | ||
|
|
0d68186dfe | ||
|
|
65e226d02d | ||
|
|
15d0654cd7 | ||
|
|
f7c36efab8 | ||
|
|
09e372f37b | ||
|
|
a020e06c3d | ||
|
|
650f2caf42 | ||
|
|
bfaa5a9878 | ||
|
|
7d22fc727b | ||
|
|
e78db0d549 | ||
|
|
4a610d9868 | ||
|
|
2c08ab1e52 | ||
|
|
70c26493c4 | ||
|
|
e5f45550d4 | ||
|
|
80c196d863 | ||
|
|
516ee4b762 | ||
|
|
7371e15ebb | ||
|
|
b6f6f79c00 | ||
|
|
d8dd38dcdd | ||
|
|
1384c2b477 | ||
|
|
2006a44fdd | ||
|
|
f7c04c9f7e | ||
|
|
1dbb36bd77 | ||
|
|
54cabebbfa | ||
|
|
f31c2296dd | ||
|
|
6f66fd9597 | ||
|
|
8c33bd828a | ||
|
|
068352de91 | ||
|
|
f5c166906b | ||
|
|
514772f5c2 | ||
|
|
2e0c2aa8bc | ||
|
|
bfd641f1b1 | ||
|
|
1c45af855d | ||
|
|
5d7cf75bed | ||
|
|
04ee23b7b5 | ||
|
|
99c3c71be8 | ||
|
|
0c2fb54d14 | ||
|
|
eaf875dfd5 | ||
|
|
29e61087ac | ||
|
|
ef989e7909 | ||
|
|
ab1fd464cf | ||
|
|
4a042b73b8 | ||
|
|
b416a7de09 | ||
|
|
ebec631cde | ||
|
|
cd6a751a68 | ||
|
|
f90692b526 | ||
|
|
b4ff72d424 | ||
|
|
61de002e0e | ||
|
|
6c063c856c | ||
|
|
a075a0b29b | ||
|
|
0e85e70470 | ||
|
|
0e84d1dad1 | ||
|
|
092b3bf9c2 | ||
|
|
87cf688e27 | ||
|
|
892cab3adf | ||
|
|
c547f50dc8 | ||
|
|
952bcecb12 | ||
|
|
1da97c3cd6 | ||
|
|
b9abc13364 | ||
|
|
fcd2ffa630 | ||
|
|
1f769bc7b5 | ||
|
|
e84a5ad62a | ||
|
|
5a481aeb5e | ||
|
|
24cea1d8d2 | ||
|
|
134f368953 | ||
|
|
8a6749e38b | ||
|
|
0722639001 | ||
|
|
e372dd8ddf | ||
|
|
c66022eaa8 | ||
|
|
3b873fc06c | ||
|
|
f2ac7cca28 | ||
|
|
d21d909de6 | ||
|
|
7c476ec104 | ||
|
|
3716772a06 | ||
|
|
c1100a5c62 | ||
|
|
0ae1332480 | ||
|
|
222debc335 | ||
|
|
7702ac5475 | ||
|
|
b4f4b10332 | ||
|
|
fb795a22c8 | ||
|
|
e5a4af88cc | ||
|
|
f68508a9ef | ||
|
|
1e4adf115c | ||
|
|
dbc8a2b7b2 | ||
|
|
f339bfe45d | ||
|
|
a78b88c2a4 | ||
|
|
4a338ad94e | ||
|
|
03d0e38f15 | ||
|
|
7da16b469c | ||
|
|
c80fcc5494 | ||
|
|
0286396175 | ||
|
|
edf381c914 | ||
|
|
58d85518f0 | ||
|
|
32e8ebf4f8 | ||
|
|
c8cba6ba91 | ||
|
|
fba5487231 | ||
|
|
9d81fbcbcb | ||
|
|
40c55b3515 | ||
|
|
e5aaaff32e | ||
|
|
6071b138f0 | ||
|
|
71174f5d40 | ||
|
|
bb4ca4ff9e | ||
|
|
93f42e9a67 | ||
|
|
2b6c1ec771 | ||
|
|
958546fd0c | ||
|
|
d263494229 | ||
|
|
12d51318a8 | ||
|
|
aa8861643f | ||
|
|
0f0e4e35ce | ||
|
|
79f63a794d | ||
|
|
47e01dadb7 | ||
|
|
3bfa2bd9e4 | ||
|
|
b1aac49824 | ||
|
|
16fdfa3cb1 | ||
|
|
9c43badbfc | ||
|
|
96689b8e67 | ||
|
|
95b2a56f80 | ||
|
|
6c33f6ff08 | ||
|
|
ab515920de | ||
|
|
46fd65f98a | ||
|
|
e9986c9667 | ||
|
|
fec40dd91c | ||
|
|
e8bffd8dc6 | ||
|
|
59e6271373 | ||
|
|
0dbb0b6e12 | ||
|
|
5d1c166606 | ||
|
|
366463ac4e | ||
|
|
ed5d4f5fcd | ||
|
|
787bc2243f | ||
|
|
8b5fccdd0d | ||
|
|
fa79467c19 | ||
|
|
5a4577b7ec | ||
|
|
0c82c60c03 | ||
|
|
84e33ec18a | ||
|
|
779be53910 | ||
|
|
cda6e3c204 | ||
|
|
c73aee1633 | ||
|
|
d486b8b4cf | ||
|
|
31909ba17b | ||
|
|
27dc30ca57 | ||
|
|
f5b7ddcf0e | ||
|
|
f1aac3e9e2 | ||
|
|
0f626dada2 | ||
|
|
74f4c74f47 | ||
|
|
3e93ab0fac | ||
|
|
4ac4ec58a8 | ||
|
|
af552414ac | ||
|
|
728d7c5d24 | ||
|
|
ee17b54872 | ||
|
|
373a557ad2 | ||
|
|
062eeb8ac0 | ||
|
|
473a7fc647 | ||
|
|
4ddd9dd874 | ||
|
|
05dd048410 | ||
|
|
c2c4afa094 | ||
|
|
be2567f478 | ||
|
|
e9e9c9f702 | ||
|
|
1e3d653d1c | ||
|
|
63f3f3899e | ||
|
|
08362d1bb6 | ||
|
|
f1d3d78f89 | ||
|
|
5f084b9d7c | ||
|
|
224d694ffa | ||
|
|
1be4b06d33 | ||
|
|
155c3a4b50 | ||
|
|
371f912c1d | ||
|
|
41c47fbc58 | ||
|
|
167030c15c | ||
|
|
7403a07614 | ||
|
|
fd0bad9a91 | ||
|
|
c2381ac3f5 | ||
|
|
ee18fbac19 | ||
|
|
b83ec99e4f | ||
|
|
72bd03d368 | ||
|
|
a6ee68efe8 | ||
|
|
795ca64ac0 | ||
|
|
ee26df3b68 | ||
|
|
bc0cf84297 | ||
|
|
049f846980 | ||
|
|
67308b8e3a | ||
|
|
8c9cdb28d0 | ||
|
|
dd30b30b34 | ||
|
|
6446097840 | ||
|
|
589cf2afd8 | ||
|
|
fb1928d5ec | ||
|
|
552b41f855 | ||
|
|
e2a036192a | ||
|
|
b6c5f670b3 | ||
|
|
ecac1c5b78 | ||
|
|
1d3f124845 | ||
|
|
d0b39df3e2 | ||
|
|
beb52670fa | ||
|
|
c924407569 | ||
|
|
bb72002452 | ||
|
|
78f045ddf2 | ||
|
|
ba49220e43 | ||
|
|
857bceba76 | ||
|
|
859280c2d7 | ||
|
|
e8ac5dbe8d | ||
|
|
86ec75b05e | ||
|
|
6b00fdb19e | ||
|
|
b9707ea486 | ||
|
|
f9dd71c05a | ||
|
|
0720a9b8f0 | ||
|
|
0289733f7f | ||
|
|
50ed8ad1e5 | ||
|
|
a10fabd927 | ||
|
|
0dd9fdf7e6 | ||
|
|
ade5f386b6 | ||
|
|
315b135a3b | ||
|
|
bc3d1daf11 | ||
|
|
50d738c7b6 | ||
|
|
0a23ac1401 | ||
|
|
9cbfb4ccbe | ||
|
|
7c1a76d38e | ||
|
|
b501b86656 | ||
|
|
f118cff5c0 | ||
|
|
66d7a9e520 | ||
|
|
a3424ce020 | ||
|
|
7cb72b25be | ||
|
|
f3584b02ac | ||
|
|
f2aaf9cc26 | ||
|
|
ea0bab5bd6 | ||
|
|
78be6f9839 | ||
|
|
a7df5c7fac | ||
|
|
516a6be98e | ||
|
|
7077e30d94 | ||
|
|
21b4053b8b | ||
|
|
85c634480d | ||
|
|
54eefe2295 | ||
|
|
b32dbc3ac5 | ||
|
|
4a5d089e5d | ||
|
|
22cad5a2ab | ||
|
|
acbebd7102 | ||
|
|
301ae12169 | ||
|
|
f04bdcee69 | ||
|
|
d4b78fbf87 | ||
|
|
500fbc9190 | ||
|
|
420f7e9b4e | ||
|
|
b273c6083f | ||
|
|
8877c705c5 | ||
|
|
1e30f794fe | ||
|
|
29ad82db7c | ||
|
|
a9046be00c | ||
|
|
a70efc95c6 | ||
|
|
b4b8774110 | ||
|
|
d87e03b0cf | ||
|
|
af07ccb87e | ||
|
|
dc43918a77 | ||
|
|
a04b131394 | ||
|
|
874f18fa6a | ||
|
|
fdc93e5130 | ||
|
|
6109e18fe6 | ||
|
|
27aca28523 | ||
|
|
2ac4aacee5 | ||
|
|
824a741634 | ||
|
|
819722e7da | ||
|
|
959d442690 | ||
|
|
b20868328f | ||
|
|
679c729985 | ||
|
|
bc2ca273a5 | ||
|
|
8a2612878d | ||
|
|
dd76d149ee | ||
|
|
2770d0fbec | ||
|
|
55b2c1e186 | ||
|
|
c4c291056b | ||
|
|
b80b81ada6 | ||
|
|
a0f3d44832 | ||
|
|
d9e4c00ccf | ||
|
|
c34a186eaf | ||
|
|
be27c930d3 | ||
|
|
66818872c2 | ||
|
|
cc1ac368b3 | ||
|
|
03e22098ca | ||
|
|
724b5eb1e0 | ||
|
|
45a1ce4b55 | ||
|
|
3e5cb2e30b | ||
|
|
d0a4c6aaf1 | ||
|
|
be4bcb7390 | ||
|
|
a63c3aa4c2 | ||
|
|
e8e43d97f2 | ||
|
|
9939ff4452 | ||
|
|
b69bbc899e | ||
|
|
f0cba24cc7 | ||
|
|
fd5fa0e798 | ||
|
|
1ba1d53884 | ||
|
|
00a118b901 | ||
|
|
ba2ea55884 | ||
|
|
70b4c3dd0f | ||
|
|
412e19252c | ||
|
|
73e1974583 | ||
|
|
25c57d645b | ||
|
|
a6c6021923 | ||
|
|
2886ac9d7c | ||
|
|
c121414767 | ||
|
|
35f02ef7a9 | ||
|
|
ffeb972a82 | ||
|
|
f20a74674f | ||
|
|
03396fbbe2 | ||
|
|
54a5c0805e | ||
|
|
486cb66b6c | ||
|
|
e5d7a344c7 | ||
|
|
60fe0ed25f | ||
|
|
04741c702a | ||
|
|
6e190fe903 | ||
|
|
5bb3e65eb9 | ||
|
|
c3b29ffcd1 | ||
|
|
a889c3e97b | ||
|
|
bd578ca7cd | ||
|
|
2a9a115c31 | ||
|
|
4d1848a5e1 | ||
|
|
4bd8f777e0 | ||
|
|
4654a9011b | ||
|
|
22a5dc148a | ||
|
|
88dc4c0607 | ||
|
|
0a7c47fa70 | ||
|
|
e6f311ebba | ||
|
|
88d09f0fef | ||
|
|
7467d87e36 | ||
|
|
b50e793a9c | ||
|
|
1147390576 | ||
|
|
539785f079 | ||
|
|
b99b516346 | ||
|
|
c0dc4f4236 | ||
|
|
5b8f664530 | ||
|
|
cc490692ad | ||
|
|
5ef415c87d | ||
|
|
ff3c67409d | ||
|
|
582a9bae31 | ||
|
|
95f51ed935 | ||
|
|
0dca22c219 | ||
|
|
eaf67e197b | ||
|
|
b8db918348 | ||
|
|
c865073605 | ||
|
|
192ccbec25 | ||
|
|
b8b3b29d01 | ||
|
|
00ea9a4393 | ||
|
|
267392d0b7 | ||
|
|
e5f041208f | ||
|
|
82010353c6 | ||
|
|
7daf4d1f58 | ||
|
|
ebb940ee0e | ||
|
|
1ce8c98837 | ||
|
|
257948cbe1 | ||
|
|
8971e0aa1a | ||
|
|
2209b050b0 | ||
|
|
b5a876a93e | ||
|
|
4c64ee24d5 | ||
|
|
1a3376374f | ||
|
|
ee88560090 | ||
|
|
aae7eba068 | ||
|
|
6715bc34d8 | ||
|
|
55d0ea5e22 | ||
|
|
11c692b146 | ||
|
|
ab47bd2e7b | ||
|
|
6dd9933d55 | ||
|
|
d08d398b47 | ||
|
|
aa3903d8f0 | ||
|
|
26f9030cef | ||
|
|
0500a10671 | ||
|
|
c3ae2d3eda | ||
|
|
e502c51886 | ||
|
|
063c1aa6c0 | ||
|
|
5e5db9193e | ||
|
|
26a38fc56c | ||
|
|
aad0558a9c | ||
|
|
a911f975da | ||
|
|
a99c8fbc88 | ||
|
|
79525227a9 | ||
|
|
66b8eb72bf | ||
|
|
1eff269b80 | ||
|
|
2dc01b528c | ||
|
|
30e200896d | ||
|
|
7d2e827e6f | ||
|
|
94e6b3086b | ||
|
|
0849e90574 | ||
|
|
d3eef33371 | ||
|
|
1cee0d10e5 | ||
|
|
3f044b407a | ||
|
|
b25ce3760b | ||
|
|
6e810761f8 | ||
|
|
6dea80e13e | ||
|
|
a715616a6a | ||
|
|
e87db432d5 | ||
|
|
0b4b89dcbd | ||
|
|
0e980051e3 | ||
|
|
4bd489a530 | ||
|
|
ed02c0dbc1 | ||
|
|
81e2432426 | ||
|
|
3a14099831 | ||
|
|
84712967b0 | ||
|
|
1fc70b7591 | ||
|
|
b9f8345964 | ||
|
|
fcd9e674d5 | ||
|
|
2cf2dfaea5 | ||
|
|
797be4e57c | ||
|
|
2478f1478f | ||
|
|
58cd508be4 | ||
|
|
9ca094f4f8 | ||
|
|
9ed9878193 | ||
|
|
c878a47f92 | ||
|
|
42d7a8860a | ||
|
|
718824f359 | ||
|
|
3b5b649530 | ||
|
|
8702af0c42 | ||
|
|
b038157f2f | ||
|
|
0033bdd570 | ||
|
|
99b40cac43 | ||
|
|
7fa91f98c6 | ||
|
|
a6e3f48044 | ||
|
|
d25921f6c8 | ||
|
|
e1986dc8eb | ||
|
|
ebba5b860f | ||
|
|
4beff8932e | ||
|
|
e26bd9adfb | ||
|
|
2eadc7b5ea | ||
|
|
a15aeade6a | ||
|
|
1990b7fad5 | ||
|
|
d81d5a37f8 | ||
|
|
1c1da0b080 | ||
|
|
4063bce178 | ||
|
|
b4d33c6472 | ||
|
|
44005c607d | ||
|
|
c17852a583 | ||
|
|
f94b22666b | ||
|
|
3c8c690a79 | ||
|
|
6f416ffe79 | ||
|
|
e4f8fe9ea2 | ||
|
|
003738149e | ||
|
|
6f39223787 | ||
|
|
06649c2207 | ||
|
|
18f51becd8 | ||
|
|
916792abea | ||
|
|
c8f8e019f6 | ||
|
|
7b6c6bb930 | ||
|
|
b826993a0d | ||
|
|
00385c8e15 | ||
|
|
5b982766fb | ||
|
|
be27dfd918 | ||
|
|
a82089aff3 | ||
|
|
215ba8bfbf | ||
|
|
7b1e9185f7 | ||
|
|
8c756e99c1 | ||
|
|
d2384e74cd | ||
|
|
b2ff2852bd | ||
|
|
3b4505af27 | ||
|
|
87eee126e5 | ||
|
|
6234f04457 | ||
|
|
c8e808082f | ||
|
|
b2f2642c82 | ||
|
|
0f77730011 | ||
|
|
b062d56928 | ||
|
|
a3f8552b80 | ||
|
|
8faf5a81ca | ||
|
|
7d7c0ae320 | ||
|
|
2bd8b71927 | ||
|
|
19a323ec77 | ||
|
|
29ee95165e | ||
|
|
c618a31e44 | ||
|
|
f972de507f | ||
|
|
51a0be31d5 | ||
|
|
d986c12564 | ||
|
|
1ae17b121e | ||
|
|
afb294f170 | ||
|
|
2b63fa343a | ||
|
|
35121f1141 | ||
|
|
9d0f26fede | ||
|
|
f0289b73b8 | ||
|
|
1009619ecd | ||
|
|
f30b1b1e08 | ||
|
|
d151f39a22 | ||
|
|
be6d4accff | ||
|
|
69e71202ee | ||
|
|
fece284aee | ||
|
|
4bcb44bcb2 | ||
|
|
0891dbf5a8 | ||
|
|
b5b5a86025 | ||
|
|
3fcbe831d7 | ||
|
|
d253151296 | ||
|
|
4e9beb86b0 | ||
|
|
7f85b49f3f | ||
|
|
1ea8c56631 | ||
|
|
5d76cbee26 | ||
|
|
682e4af619 | ||
|
|
c446476fbc | ||
|
|
e18a05001e | ||
|
|
0685f8b9c0 | ||
|
|
bdd1d4e8c1 | ||
|
|
bf6923f909 | ||
|
|
faa37c3322 | ||
|
|
1fe5b07f86 | ||
|
|
7a9ad3933b | ||
|
|
df2e447362 | ||
|
|
b2033f3dcd | ||
|
|
186bce852d | ||
|
|
e1494441b0 | ||
|
|
1b67c130c7 | ||
|
|
628b980655 | ||
|
|
0e04a17380 | ||
|
|
cee1a14ebd | ||
|
|
3862205053 | ||
|
|
9696f044a0 | ||
|
|
b61a0f2c94 | ||
|
|
afb2e3a8a2 | ||
|
|
65cc0a0ed5 | ||
|
|
dba6f2ec6a | ||
|
|
b39ea03ba6 | ||
|
|
528483b905 | ||
|
|
fb7434f1b4 | ||
|
|
a239e6405b | ||
|
|
d5c00b81d7 | ||
|
|
590f6c8c8b | ||
|
|
edf2495a9f | ||
|
|
ed0a034414 | ||
|
|
3e33e0d0e1 | ||
|
|
c9e1c2711e | ||
|
|
1f0420dc1f | ||
|
|
f0320c6ff9 | ||
|
|
904eb1364c | ||
|
|
e5ef184515 | ||
|
|
2a4111643c | ||
|
|
53ffe057b2 | ||
|
|
b2632e1727 | ||
|
|
84554d6dea | ||
|
|
6fd8f8380b | ||
|
|
5330929efd | ||
|
|
c67bc9938c | ||
|
|
f41f0e94be | ||
|
|
74b1c6f4ed | ||
|
|
54d7e20baa | ||
|
|
221d88c52c | ||
|
|
eb3c8050fe | ||
|
|
fdab854799 | ||
|
|
ba32761a0c | ||
|
|
6d002838a5 | ||
|
|
d8f53db387 | ||
|
|
f15db1637e | ||
|
|
5f98e2fdc5 | ||
|
|
603dc9f9c6 | ||
|
|
0692079384 | ||
|
|
e046cdce45 | ||
|
|
595419df77 | ||
|
|
1a641afaa8 | ||
|
|
29c73ae90f | ||
|
|
e055f99f63 | ||
|
|
888d4797d0 | ||
|
|
b95d63e974 | ||
|
|
195abb59ff | ||
|
|
306ceba474 | ||
|
|
2be1476130 | ||
|
|
ca80356c27 | ||
|
|
eed34d9b7d | ||
|
|
1144ab32b7 | ||
|
|
0061e609d4 | ||
|
|
680b64c98f | ||
|
|
325048efcb | ||
|
|
aedb293d50 | ||
|
|
6cdc1943fb | ||
|
|
9ff40875c2 | ||
|
|
3a851d59fe | ||
|
|
64de7abada | ||
|
|
6d9feebedd | ||
|
|
441658eb7a | ||
|
|
01a441a99c | ||
|
|
1d10141c1d | ||
|
|
952313acee | ||
|
|
d537b742dc | ||
|
|
f3b706bcc2 | ||
|
|
2a57af944f | ||
|
|
3f42b93300 | ||
|
|
59a58eb886 | ||
|
|
d91d5bcd9b | ||
|
|
c6a08c79e1 | ||
|
|
d550b65bc7 | ||
|
|
42418ef393 | ||
|
|
12f4156ae0 | ||
|
|
ff58873fcb | ||
|
|
925c72f6aa | ||
|
|
345d853e2d | ||
|
|
10b064dc40 | ||
|
|
75a7187a87 | ||
|
|
6182e00c31 | ||
|
|
e3ba93a6b8 | ||
|
|
109d8cacbc | ||
|
|
3bcc18f035 | ||
|
|
92baa1affb | ||
|
|
32a9457de3 | ||
|
|
32c147fa16 | ||
|
|
d61d75f8bb | ||
|
|
eae6cd5396 | ||
|
|
6f16f230d1 | ||
|
|
f458a057e8 | ||
|
|
d013300fa3 | ||
|
|
d6b8394044 | ||
|
|
c1fa630602 | ||
|
|
28a03dadaf | ||
|
|
29cfc2ea6b | ||
|
|
f4c776f10e | ||
|
|
04be6bc38b | ||
|
|
4f0bbeeab0 | ||
|
|
5a356a8d75 | ||
|
|
7f35f46eaa | ||
|
|
ea7be67d83 | ||
|
|
8f0d65766d | ||
|
|
a140f318a2 | ||
|
|
d0a2d1644f | ||
|
|
e69b153e1c | ||
|
|
d19d383738 | ||
|
|
253f288323 | ||
|
|
d6ce0536ca | ||
|
|
0168c01915 | ||
|
|
7329fec67a | ||
|
|
5fea22294e | ||
|
|
669124a6b3 | ||
|
|
c44d141c07 | ||
|
|
0b30e00b8c | ||
|
|
1e7bd1bf48 | ||
|
|
c2c4d6aa02 | ||
|
|
7e0c64191c | ||
|
|
323e4e971a | ||
|
|
99b4f8f8fc | ||
|
|
7c1cf76c5c | ||
|
|
305e1b7af6 | ||
|
|
6880a70227 | ||
|
|
0482969755 | ||
|
|
4c9b429dae | ||
|
|
60b8400e29 | ||
|
|
7260be98d6 | ||
|
|
be94094de7 | ||
|
|
645077f39b | ||
|
|
b42940441d | ||
|
|
fc7071c04e | ||
|
|
de887a4e3f | ||
|
|
7f629309c7 | ||
|
|
fa45e209d5 | ||
|
|
40aec6cd0d | ||
|
|
969ae7aeca | ||
|
|
a64bbd34f3 | ||
|
|
79acf83d6f | ||
|
|
786e8d0be1 | ||
|
|
2757850f28 | ||
|
|
7c6f39f3b5 | ||
|
|
70723d0437 | ||
|
|
bd9b0f798b | ||
|
|
0c406b2ad8 | ||
|
|
822891541c | ||
|
|
bf064fd4e3 | ||
|
|
6c9e055d42 | ||
|
|
7455d1bfb2 | ||
|
|
7b923b6625 | ||
|
|
bcd18e9395 | ||
|
|
082d577834 | ||
|
|
68bf4f9df3 | ||
|
|
9348698aac | ||
|
|
979025f7d9 | ||
|
|
9aa09050da | ||
|
|
f331de4185 | ||
|
|
1ce067c854 | ||
|
|
e6d537f0f8 | ||
|
|
6a69a8d21a | ||
|
|
415cc09a06 | ||
|
|
fb482e133e | ||
|
|
c8134ce2ec | ||
|
|
a59c062dda | ||
|
|
fe604d3439 | ||
|
|
53ad8466ad | ||
|
|
9e3c844231 | ||
|
|
85a10e7c72 | ||
|
|
902dc5bdd1 | ||
|
|
666cbc9e47 | ||
|
|
745fae3f76 | ||
|
|
bb2c4d4c3f | ||
|
|
f8c8d6725c | ||
|
|
54747dc800 | ||
|
|
63b6c0cab4 | ||
|
|
92c85a0791 | ||
|
|
83f47bfe6e | ||
|
|
2f034e98c0 | ||
|
|
cb416c3583 | ||
|
|
584353a240 | ||
|
|
729456c2a0 | ||
|
|
a02a2e5332 | ||
|
|
78f0204d86 | ||
|
|
b76ef5ca80 | ||
|
|
788fc01cfb | ||
|
|
4dc9360b34 | ||
|
|
6c0e1b7f01 | ||
|
|
27b91642f2 | ||
|
|
0c7be4b2c7 | ||
|
|
e41ba71828 | ||
|
|
2b2b6b98f1 | ||
|
|
8f6b20abd1 | ||
|
|
21a69c4a61 | ||
|
|
e7b671283a | ||
|
|
d46a7a50b5 | ||
|
|
dfae4e0848 | ||
|
|
a0f7e924e5 | ||
|
|
fd2f3c7dbe | ||
|
|
da442dc02b | ||
|
|
eaea900100 | ||
|
|
449662f858 | ||
|
|
def2219f41 | ||
|
|
4670fc477d | ||
|
|
cea4572843 | ||
|
|
b769d4644a | ||
|
|
3d9b117a74 | ||
|
|
3b1a5f227e | ||
|
|
a66a2d936e | ||
|
|
8b5c334d47 | ||
|
|
5597cf6753 | ||
|
|
9d2d83d8d1 | ||
|
|
bc695479fa | ||
|
|
c0f7f2accd | ||
|
|
08309d6162 | ||
|
|
b217036ad0 | ||
|
|
a276652cbc | ||
|
|
39f643330a | ||
|
|
2967cc917c | ||
|
|
78d22b3165 | ||
|
|
989036bdf2 | ||
|
|
926e9ff92c | ||
|
|
4696567514 | ||
|
|
e153a30186 | ||
|
|
e8bbd3dace | ||
|
|
d2d1344858 | ||
|
|
6be828079d | ||
|
|
7a07a5c646 | ||
|
|
ee649ebeb9 | ||
|
|
67fe2a5a67 | ||
|
|
6ac193c139 | ||
|
|
b7492a6dfa | ||
|
|
224e86f673 | ||
|
|
f014e155d6 | ||
|
|
7aa37cd6cb | ||
|
|
b1cc09fcb6 | ||
|
|
4717293666 | ||
|
|
a80cd0720f | ||
|
|
e238676efc | ||
|
|
e1df231137 | ||
|
|
f40d10b35c | ||
|
|
ea20e5445e | ||
|
|
4a6f2dbdc3 | ||
|
|
11ff01bc61 | ||
|
|
2a292e6a82 | ||
|
|
dc11242b77 | ||
|
|
4ec8f5fc28 | ||
|
|
690f3ed87c | ||
|
|
04e8f6f2e3 | ||
|
|
e96ab86198 | ||
|
|
eae010a23b | ||
|
|
d9d829e37d | ||
|
|
53680fc774 | ||
|
|
46b8378297 | ||
|
|
a26b4f779e | ||
|
|
c2e8c87f3b | ||
|
|
c99e82bd8e | ||
|
|
1398e54896 | ||
|
|
2d93e5bb1c | ||
|
|
6b901d42f7 | ||
|
|
0cc8a968ed | ||
|
|
3f2914c6fa | ||
|
|
9e01786278 |
244
.editorconfig
Normal file
244
.editorconfig
Normal file
@@ -0,0 +1,244 @@
|
||||
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
||||
root = true
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
|
||||
#### Core EditorConfig Options ####
|
||||
|
||||
# Indentation and spacing
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# New line preferences
|
||||
end_of_line = crlf
|
||||
insert_final_newline = false
|
||||
|
||||
#### .NET Code Actions ####
|
||||
|
||||
# Type members
|
||||
dotnet_hide_advanced_members = false
|
||||
dotnet_member_insertion_location = with_other_members_of_the_same_kind
|
||||
dotnet_property_generation_behavior = prefer_throwing_properties
|
||||
|
||||
# Symbol search
|
||||
dotnet_search_reference_assemblies = true
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
|
||||
# Organize usings
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = false
|
||||
file_header_template = unset
|
||||
|
||||
# this. and Me. preferences
|
||||
dotnet_style_qualification_for_event = false
|
||||
dotnet_style_qualification_for_field = false
|
||||
dotnet_style_qualification_for_method = false
|
||||
dotnet_style_qualification_for_property = false
|
||||
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||
dotnet_style_predefined_type_for_member_access = true
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_prefer_system_hash_code = true
|
||||
dotnet_style_coalesce_expression = true
|
||||
dotnet_style_collection_initializer = true
|
||||
dotnet_style_explicit_tuple_names = true
|
||||
dotnet_style_namespace_match_folder = true
|
||||
dotnet_style_null_propagation = true
|
||||
dotnet_style_object_initializer = true
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true
|
||||
dotnet_style_prefer_collection_expression = when_types_loosely_match
|
||||
dotnet_style_prefer_compound_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_return = true
|
||||
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
||||
dotnet_style_prefer_inferred_tuple_names = true
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||
dotnet_style_prefer_simplified_interpolation = true
|
||||
|
||||
# Field preferences
|
||||
dotnet_style_readonly_field = true
|
||||
|
||||
# Parameter preferences
|
||||
dotnet_code_quality_unused_parameters = all
|
||||
|
||||
# Suppression preferences
|
||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||
|
||||
# New line preferences
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = true
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
|
||||
# var preferences
|
||||
csharp_style_var_elsewhere = false
|
||||
csharp_style_var_for_built_in_types = false
|
||||
csharp_style_var_when_type_is_apparent = false
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_accessors = true
|
||||
csharp_style_expression_bodied_constructors = false
|
||||
csharp_style_expression_bodied_indexers = true
|
||||
csharp_style_expression_bodied_lambdas = true
|
||||
csharp_style_expression_bodied_local_functions = false
|
||||
csharp_style_expression_bodied_methods = false
|
||||
csharp_style_expression_bodied_operators = false
|
||||
csharp_style_expression_bodied_properties = true
|
||||
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||
csharp_style_prefer_extended_property_pattern = true
|
||||
csharp_style_prefer_not_pattern = true
|
||||
csharp_style_prefer_pattern_matching = true
|
||||
csharp_style_prefer_switch_expression = true
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true
|
||||
|
||||
# Modifier preferences
|
||||
csharp_prefer_static_anonymous_function = true
|
||||
csharp_prefer_static_local_function = true
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
|
||||
csharp_style_prefer_readonly_struct = true
|
||||
csharp_style_prefer_readonly_struct_member = true
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = true
|
||||
csharp_prefer_simple_using_statement = true
|
||||
csharp_prefer_system_threading_lock = true
|
||||
csharp_style_namespace_declarations = block_scoped
|
||||
csharp_style_prefer_method_group_conversion = true
|
||||
csharp_style_prefer_primary_constructors = true
|
||||
csharp_style_prefer_top_level_statements = true
|
||||
|
||||
# Expression-level preferences
|
||||
csharp_prefer_simple_default_expression = true
|
||||
csharp_style_deconstructed_variable_declaration = true
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true
|
||||
csharp_style_inlined_variable_declaration = true
|
||||
csharp_style_prefer_index_operator = true
|
||||
csharp_style_prefer_local_over_anonymous_function = true
|
||||
csharp_style_prefer_null_check_over_type_check = true
|
||||
csharp_style_prefer_range_operator = true
|
||||
csharp_style_prefer_tuple_swap = true
|
||||
csharp_style_prefer_utf8_string_literals = true
|
||||
csharp_style_throw_expression = true
|
||||
csharp_style_unused_value_assignment_preference = discard_variable
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace
|
||||
|
||||
# New line preferences
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
|
||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
# New line preferences
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = no_change
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### Naming styles ####
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -15,11 +15,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build Radzen.Blazor/Radzen.Blazor.csproj
|
||||
- name: Test
|
||||
|
||||
@@ -17,7 +17,7 @@ COPY RadzenBlazorDemos.Host /app/RadzenBlazorDemos.Host
|
||||
WORKDIR /app
|
||||
RUN docfx DocFX/docfx.json
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:7.0
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0
|
||||
|
||||
COPY --from=0 /app/RadzenBlazorDemos.Host /app/RadzenBlazorDemos.Host
|
||||
COPY --from=0 /app/RadzenBlazorDemos /app/RadzenBlazorDemos
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2022 Radzen Ltd
|
||||
Copyright (c) 2018-2025 Radzen Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
103
README.md
103
README.md
@@ -1,45 +1,19 @@
|
||||

|
||||

|
||||
|
||||
<h1 align="center">
|
||||
Radzen Blazor Components
|
||||
</h1>
|
||||
Radzen Blazor Components
|
||||
========================
|
||||
|
||||
<p align="center">
|
||||
A set of <strong>70+ free and open source</strong> native Blazor UI controls.
|
||||
</p>
|
||||
A set of **90+ free and open source** native Blazor UI controls.
|
||||
|
||||
<div align="center">
|
||||
See Online Demos or Read the Docs
|
||||
|
||||
[See Online Demos](https://blazor.radzen.com) or [Read the Docs](https://blazor.radzen.com/docs/)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/radzenhq/radzen-blazor/blob/master/LICENSE">
|
||||
<img alt="License - MIT" src="https://img.shields.io/github/license/radzenhq/radzen-blazor?logo=github&style=for-the-badge" />
|
||||
</a>
|
||||
<a href="https://www.nuget.org/packages/Radzen.Blazor">
|
||||
<img alt="Nuget Downloads" src="https://img.shields.io/nuget/dt/Radzen.Blazor?color=%232694F9&label=nuget%20downloads&logo=nuget&style=for-the-badge" />
|
||||
</a>
|
||||
<img alt="Last Commit" src="https://img.shields.io/github/last-commit/radzenhq/radzen-blazor?logo=github&style=for-the-badge" />
|
||||
<a href="https://github.com/radzenhq/radzen-blazor/graphs/contributors">
|
||||
<img alt="Github Contributors" src="https://img.shields.io/github/contributors/radzenhq/radzen-blazor?logo=github&style=for-the-badge" />
|
||||
</a>
|
||||
<a href="https://blazor.radzen.com">
|
||||
<img alt="Radzen Blazor Components - Online Demos" src="https://img.shields.io/badge/demos-online-brightgreen?color=%232694F9&logo=blazor&style=for-the-badge" />
|
||||
</a>
|
||||
<a href="https://blazor.radzen.com/docs">
|
||||
<img alt="Radzen Blazor Components - Documentation" src="https://img.shields.io/badge/docs-online-brightgreen?color=%232694F9&logo=blazor&style=for-the-badge" />
|
||||
</a>
|
||||
</p>
|
||||
[](https://github.com/radzenhq/radzen-blazor/blob/master/LICENSE)[ ](https://www.nuget.org/packages/Radzen.Blazor) [ ](https://github.com/radzenhq/radzen-blazor/graphs/contributors)[ ](https://blazor.radzen.com)[](https://blazor.radzen.com/docs)
|
||||
|
||||
## Why choose Radzen Blazor Components?
|
||||
|
||||
### :sparkles: Free
|
||||
|
||||
Radzen Blazor Components are open source and free for commercial use. You can install them from [nuget](https://www.nuget.org/packages/Radzen.Blazor) or build your own copy from source.
|
||||
Radzen Blazor Components are open source and free for commercial use. You can install them from [NuGet](https://www.nuget.org/packages/Radzen.Blazor) or build your own copy from source.
|
||||
|
||||
Paid support is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
|
||||
|
||||
@@ -76,67 +50,8 @@ Our flagship product [Radzen Blazor Studio](https://www.radzen.com/blazor-studio
|
||||
|
||||
## Get started with Radzen Blazor Components
|
||||
|
||||
### 1. Install
|
||||
Check the [getting started](https://blazor.radzen.com/getting-started) instructions to start making awesome Blazor applications.
|
||||
|
||||
Radzen Blazor Components are distributed as a [Radzen.Blazor nuget package](https://www.nuget.org/packages/Radzen.Blazor). You can add them to your project in one of the following ways
|
||||
- Install the package from command line by running `dotnet add package Radzen.Blazor`
|
||||
- Add the project from the Visual Nuget Package Manager
|
||||
- Manually edit the .csproj file and add a project reference
|
||||
|
||||
### 2. Import the namespace
|
||||
|
||||
Open the `_Imports.razor` file of your Blazor application and add this line `@using Radzen.Blazor`.
|
||||
|
||||
### 3. Include a theme
|
||||
|
||||
Radzen Blazor components come with five free themes: Material, Standard, Default, Dark, Software and Humanistic.
|
||||
|
||||
To use a theme
|
||||
1. Pick a theme. The [online demos](https://blazor.radzen.com/colors) allow you to preview the available options via the theme dropdown located in the header. The Material theme is currently selected by default.
|
||||
1. Include the theme CSS file in your Blazor application. Open `Pages\_Layout.cshtml` (Blazor Server .NET 6), `Pages\_Host.cshtml` (Blazor Server .NET 7) or `wwwroot/index.html` (Blazor WebAssembly) and include a theme CSS file by adding this snippet
|
||||
```html
|
||||
<link rel="stylesheet" href="_content/Radzen.Blazor/css/material-base.css">
|
||||
```
|
||||
|
||||
To include a different theme (i.e. Standard) just change the name of the CSS file:
|
||||
```
|
||||
<link rel="stylesheet" href="_content/Radzen.Blazor/css/standard-base.css">
|
||||
```
|
||||
|
||||
### 4. Include Radzen.Blazor.js
|
||||
|
||||
Open `Pages\_Layout.cshtml` (Blazor Server .NET 6), `Pages\_Host.cshtml` (Blazor Server .NET 7) or `wwwroot/index.html` (Blazor WebAssembly) and include this snippet:
|
||||
|
||||
```html
|
||||
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
|
||||
```
|
||||
|
||||
### 5. Use a component
|
||||
Use any Radzen Blazor component by typing its tag name in a Blazor page e.g.
|
||||
```html
|
||||
<RadzenButton Text="Hi"></RadzenButton>
|
||||
```
|
||||
|
||||
#### Data-binding a property
|
||||
```razor
|
||||
<RadzenButton Text=@text />
|
||||
<RadzenTextBox @bind-Value=@text />
|
||||
@code {
|
||||
string text = "Hi";
|
||||
}
|
||||
```
|
||||
|
||||
#### Handing events
|
||||
|
||||
```razor
|
||||
<RadzenButton Click="@ButtonClicked" Text="Hi"></RadzenButton>
|
||||
@code {
|
||||
void ButtonClicked()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
## Run demos locally
|
||||
|
||||
Use Radzen.Server.sln to open and run demos as Blazor server application or Radzen.WebAssembly.sln to open and run demos as Blazor WebAssembly application. Radzen.sln has reference to all projects including tests.
|
||||
Use Radzen.Server.sln to open and run demos as Blazor server application or Radzen.WebAssembly.sln to open and run demos as Blazor WebAssembly application. Radzen.sln has reference to all projects including tests.
|
||||
|
||||
74
Radzen.Blazor.Tests/AutoCompleteTests.cs
Normal file
74
Radzen.Blazor.Tests/AutoCompleteTests.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class AutoCompleteTests
|
||||
{
|
||||
[Fact]
|
||||
public void AutoComplete_Enum_Converts_To_Attr_Value()
|
||||
{
|
||||
// Options
|
||||
Assert.Equal("off", AutoCompleteType.Off.GetAutoCompleteValue());
|
||||
Assert.Equal("on", AutoCompleteType.On.GetAutoCompleteValue());
|
||||
Assert.Equal("name", AutoCompleteType.Name.GetAutoCompleteValue());
|
||||
Assert.Equal("honorific-prefix", AutoCompleteType.HonorificPrefix.GetAutoCompleteValue());
|
||||
Assert.Equal("given-name", AutoCompleteType.GivenName.GetAutoCompleteValue());
|
||||
Assert.Equal("additional-name", AutoCompleteType.AdditionalName.GetAutoCompleteValue());
|
||||
Assert.Equal("family-name", AutoCompleteType.FamilyName.GetAutoCompleteValue());
|
||||
Assert.Equal("honorific-suffix", AutoCompleteType.HonorificSuffix.GetAutoCompleteValue());
|
||||
Assert.Equal("nickname", AutoCompleteType.Nickname.GetAutoCompleteValue());
|
||||
Assert.Equal("email", AutoCompleteType.Email.GetAutoCompleteValue());
|
||||
Assert.Equal("username", AutoCompleteType.Username.GetAutoCompleteValue());
|
||||
Assert.Equal("new-password", AutoCompleteType.NewPassword.GetAutoCompleteValue());
|
||||
Assert.Equal("current-password", AutoCompleteType.CurrentPassword.GetAutoCompleteValue());
|
||||
Assert.Equal("one-time-code", AutoCompleteType.OneTimeCode.GetAutoCompleteValue());
|
||||
Assert.Equal("organization-title", AutoCompleteType.OrganizationTitle.GetAutoCompleteValue());
|
||||
Assert.Equal("organization", AutoCompleteType.Organization.GetAutoCompleteValue());
|
||||
Assert.Equal("street-address", AutoCompleteType.StreetAddress.GetAutoCompleteValue());
|
||||
Assert.Equal("address-line1", AutoCompleteType.AddressLine1.GetAutoCompleteValue());
|
||||
Assert.Equal("address-line2", AutoCompleteType.AddressLine2.GetAutoCompleteValue());
|
||||
Assert.Equal("address-line3", AutoCompleteType.AddressLine3.GetAutoCompleteValue());
|
||||
Assert.Equal("address-level1", AutoCompleteType.AddressLevel1.GetAutoCompleteValue());
|
||||
Assert.Equal("address-level2", AutoCompleteType.AddressLevel2.GetAutoCompleteValue());
|
||||
Assert.Equal("address-level3", AutoCompleteType.AddressLevel3.GetAutoCompleteValue());
|
||||
Assert.Equal("address-level4", AutoCompleteType.AddressLevel4.GetAutoCompleteValue());
|
||||
Assert.Equal("country", AutoCompleteType.Country.GetAutoCompleteValue());
|
||||
Assert.Equal("country-name", AutoCompleteType.CountryName.GetAutoCompleteValue());
|
||||
Assert.Equal("postal-code", AutoCompleteType.PostalCode.GetAutoCompleteValue());
|
||||
Assert.Equal("cc-name", AutoCompleteType.CcName.GetAutoCompleteValue());
|
||||
Assert.Equal("cc-given-name", AutoCompleteType.CcGivenName.GetAutoCompleteValue());
|
||||
Assert.Equal("cc-additional-name", AutoCompleteType.CcAdditionalName.GetAutoCompleteValue());
|
||||
Assert.Equal("cc-family-name", AutoCompleteType.CcFamilyName.GetAutoCompleteValue());
|
||||
Assert.Equal("cc-number", AutoCompleteType.CcNumber.GetAutoCompleteValue());
|
||||
Assert.Equal("cc-exp", AutoCompleteType.CcExp.GetAutoCompleteValue());
|
||||
Assert.Equal("cc-exp-month", AutoCompleteType.CcExpMonth.GetAutoCompleteValue());
|
||||
Assert.Equal("cc-exp-year", AutoCompleteType.CcExpYear.GetAutoCompleteValue());
|
||||
Assert.Equal("cc-csc", AutoCompleteType.CcCsc.GetAutoCompleteValue());
|
||||
Assert.Equal("cc-type", AutoCompleteType.CcType.GetAutoCompleteValue());
|
||||
Assert.Equal("transaction-currency", AutoCompleteType.TransactionCurrency.GetAutoCompleteValue());
|
||||
Assert.Equal("transaction-amount", AutoCompleteType.TransactionAmount.GetAutoCompleteValue());
|
||||
Assert.Equal("language", AutoCompleteType.Language.GetAutoCompleteValue());
|
||||
Assert.Equal("bday", AutoCompleteType.Bday.GetAutoCompleteValue());
|
||||
Assert.Equal("bday-day", AutoCompleteType.BdayDay.GetAutoCompleteValue());
|
||||
Assert.Equal("bday-month", AutoCompleteType.BdayMonth.GetAutoCompleteValue());
|
||||
Assert.Equal("bday-year", AutoCompleteType.BdayYear.GetAutoCompleteValue());
|
||||
Assert.Equal("sex", AutoCompleteType.Sex.GetAutoCompleteValue());
|
||||
Assert.Equal("tel", AutoCompleteType.Tel.GetAutoCompleteValue());
|
||||
Assert.Equal("tel-country-code", AutoCompleteType.TelCountryCode.GetAutoCompleteValue());
|
||||
Assert.Equal("tel-national", AutoCompleteType.TelNational.GetAutoCompleteValue());
|
||||
Assert.Equal("tel-area-code", AutoCompleteType.TelAreaCode.GetAutoCompleteValue());
|
||||
Assert.Equal("tel-local", AutoCompleteType.TelLocal.GetAutoCompleteValue());
|
||||
Assert.Equal("tel-extension", AutoCompleteType.TelExtension.GetAutoCompleteValue());
|
||||
Assert.Equal("impp", AutoCompleteType.Impp.GetAutoCompleteValue());
|
||||
Assert.Equal("url", AutoCompleteType.Url.GetAutoCompleteValue());
|
||||
Assert.Equal("photo", AutoCompleteType.Photo.GetAutoCompleteValue());
|
||||
// Synonyms
|
||||
Assert.Equal("address-level1", AutoCompleteType.State.GetAutoCompleteValue());
|
||||
Assert.Equal("address-level1", AutoCompleteType.Province.GetAutoCompleteValue());
|
||||
Assert.Equal("postal-code", AutoCompleteType.ZipCode.GetAutoCompleteValue());
|
||||
Assert.Equal("given-name", AutoCompleteType.FirstName.GetAutoCompleteValue());
|
||||
Assert.Equal("additional-name", AutoCompleteType.MiddleName.GetAutoCompleteValue());
|
||||
Assert.Equal("family-name", AutoCompleteType.LastName.GetAutoCompleteValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -48,7 +48,7 @@ namespace Radzen.Blazor.Tests
|
||||
);
|
||||
|
||||
// does not render the actual icon when busy
|
||||
Assert.DoesNotContain(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.DoesNotContain(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
|
||||
// renders the icon with busy spin animation
|
||||
Assert.Contains(@"<i style=""animation: rotation", component.Markup);
|
||||
@@ -71,7 +71,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add(p => p.Icon, icon);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Image, image));
|
||||
|
||||
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" />", component.Markup);
|
||||
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""button"" />", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -103,9 +103,10 @@ namespace Radzen.Blazor.Tests
|
||||
{
|
||||
parameters.Add(p => p.Text, text);
|
||||
parameters.Add(p => p.Image, image);
|
||||
parameters.Add(p => p.ImageAlternateText, text);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" />", component.Markup);
|
||||
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""{text}"" />", component.Markup);
|
||||
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
|
||||
}
|
||||
|
||||
|
||||
58
Radzen.Blazor.Tests/ChartTests.cs
Normal file
58
Radzen.Blazor.Tests/ChartTests.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bunit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Radzen.Blazor.Tests;
|
||||
|
||||
public class ChartTests
|
||||
{
|
||||
private readonly ITestOutputHelper output;
|
||||
public ChartTests(ITestOutputHelper output)
|
||||
{
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
[Fact(Timeout = 30000)]
|
||||
public async Task Chart_Tooltip_Performance()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.Setup<Rect>("Radzen.createChart", _ => true).SetResult(new Rect {Left = 0, Top = 0, Width = 200, Height = 200});
|
||||
ctx.Services.AddScoped<TooltipService>();
|
||||
ctx.JSInterop.SetupVoid("Radzen.openChartTooltip", _ => true);
|
||||
ctx.RenderComponent<RadzenChartTooltip>();
|
||||
|
||||
var seriesData = Enumerable.Range(0, 5000).Select(i => new Point { X = i, Y = i });
|
||||
var chart = ctx.RenderComponent<RadzenChart>(chartParameters =>
|
||||
chartParameters
|
||||
.AddChildContent<RadzenLineSeries<Point>>(seriesParameters =>
|
||||
seriesParameters
|
||||
.Add(p => p.CategoryProperty, nameof(Point.X))
|
||||
.Add(p => p.ValueProperty, nameof(Point.Y))
|
||||
.Add(p => p.Data, seriesData))
|
||||
.AddChildContent<RadzenCategoryAxis>(axisParameters =>
|
||||
axisParameters
|
||||
.Add(p => p.Step, 100)
|
||||
.Add(p => p.Formatter, x =>
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
return $"{x}";
|
||||
})));
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
foreach (var invocation in Enumerable.Range(0, 10))
|
||||
{
|
||||
await chart.InvokeAsync(() => chart.Instance.MouseMove(100, 80));
|
||||
Assert.Equal((invocation + 1) * 2, ctx.JSInterop.Invocations.Count(x => x.Identifier == "Radzen.openChartTooltip"));
|
||||
await chart.InvokeAsync(() => chart.Instance.MouseMove(0, 0));
|
||||
Assert.Equal(invocation + 1, ctx.JSInterop.Invocations.Count(x => x.Identifier == "Radzen.closeTooltip"));
|
||||
}
|
||||
output.WriteLine($"Time took: {stopwatch.Elapsed}");
|
||||
}
|
||||
}
|
||||
@@ -160,5 +160,83 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Contains(@$"rz-state-active", component.Markup);
|
||||
Assert.Contains(@$"rzi-times", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBox_Renders_ReadonlyParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBox<bool>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.ReadOnly, true));
|
||||
|
||||
Assert.Contains(@$"readonly", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBox_DoesNotRaise_ChangedEvent_ReadonlyParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBox<bool>>();
|
||||
|
||||
var raised = false;
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters
|
||||
.Add<bool>(p => p.ReadOnly, true)
|
||||
.Add(p => p.Change, args => { raised = true; })
|
||||
);
|
||||
|
||||
component.Find("div.rz-chkbox-box").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBox_DoesNotRaise_ValueChangedEvent_ReadonlyParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBox<bool>>();
|
||||
|
||||
var raised = false;
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters
|
||||
.Add<bool>(p => p.ReadOnly, true)
|
||||
.Add(p => p.ValueChanged, args => { raised = true; })
|
||||
);
|
||||
|
||||
component.Find("div.rz-chkbox-box").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBox_ValueNotChanged_ReadonlyParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBox<bool>>();
|
||||
|
||||
var value = true;
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters
|
||||
.Add<bool>(p => p.ReadOnly, true)
|
||||
.Add<bool>(p => p.Value, value)
|
||||
);
|
||||
|
||||
component.Find("div.rz-chkbox-box").Click();
|
||||
|
||||
Assert.Contains(@$"rz-state-active", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters
|
||||
.Add<bool>(p => p.ReadOnly, !true)
|
||||
.Add<bool>(p => p.Value, value)
|
||||
);
|
||||
|
||||
component.Find("div.rz-chkbox-box").Click();
|
||||
|
||||
Assert.DoesNotContain(@$"rz-state-active", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Components.Rendering;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
@@ -20,39 +20,29 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Main
|
||||
Assert.Contains(@$"rz-datatable-scrollable-wrapper", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable-scrollable-view", component.Markup);
|
||||
Assert.Contains(@$"rz-data-grid", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable-scrollable", component.Markup);
|
||||
|
||||
// Header
|
||||
Assert.Contains(@$"rz-datatable-scrollable-header", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable-scrollable-header-box", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable-thead", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable-scrollable-colgroup", component.Markup);
|
||||
// Data
|
||||
Assert.Contains(@$"rz-data-grid-data", component.Markup);
|
||||
|
||||
//Body
|
||||
Assert.Contains(@$"rz-datatable-scrollable-body", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable-scrollable-table-wrapper", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable-data", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable-hoverable-rows", component.Markup);
|
||||
|
||||
// Footer
|
||||
Assert.DoesNotContain(@$"rz-datatable-scrollable-footer", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-datatable-scrollable-footer-box", component.Markup);
|
||||
|
||||
//Columns
|
||||
Assert.DoesNotContain(@$"rz-sortable-column", component.Markup);
|
||||
// Table
|
||||
Assert.Contains(@$"rz-grid-table", component.Markup);
|
||||
Assert.Contains(@$"rz-grid-table-fixed", component.Markup);
|
||||
Assert.Contains(@$"rz-grid-table-striped", component.Markup);
|
||||
}
|
||||
|
||||
// Columns tests
|
||||
@@ -63,12 +53,12 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
@@ -88,12 +78,12 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Title", "MyId");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
@@ -103,6 +93,54 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Equal("MyId", title.TextContent.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataGrid_Renders_TitleAttribute()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add(p => p.ShowColumnTitleAsTooltip, true);
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Title", "MyId");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
var title = component.Find(".rz-column-title");
|
||||
Assert.Equal("MyId", title.TextContent.Trim());
|
||||
Assert.Equal("MyId", title.GetAttribute("title"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataGrid_DoesNotRender_TitleAttribute()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add(p => p.ShowColumnTitleAsTooltip, false);
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Title", "MyId");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
var title = component.Find(".rz-column-title");
|
||||
Assert.Equal("MyId", title.TextContent.Trim());
|
||||
Assert.Null(title.GetAttribute("title"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataGrid_Renders_AllowSortingParameter()
|
||||
{
|
||||
@@ -110,12 +148,12 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
@@ -140,12 +178,12 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.AddAttribute(3, "Sortable", false);
|
||||
@@ -164,12 +202,12 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
@@ -177,14 +215,14 @@ namespace Radzen.Blazor.Tests
|
||||
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-cell-filter", component.Markup);
|
||||
Assert.Contains(@$"rz-grid-filter-icon", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowFiltering, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain(@$"rz-cell-filter", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-grid-filter-icon", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -194,12 +232,12 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.AddAttribute(3, "Filterable", false);
|
||||
@@ -218,12 +256,12 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
@@ -249,12 +287,12 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<int>>(p => p.Data, new[] { 1, 2, 3 });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<int>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<int>));
|
||||
|
||||
builder.AddAttribute(1, "HeaderTemplate", (RenderFragment)delegate (RenderTreeBuilder b)
|
||||
{
|
||||
@@ -275,12 +313,12 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<int>>(p => p.Data, new[] { 1, 2, 3 });
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<int>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<int>));
|
||||
|
||||
builder.AddAttribute(1, "FooterTemplate", (RenderFragment)delegate (RenderTreeBuilder b)
|
||||
{
|
||||
@@ -291,8 +329,8 @@ namespace Radzen.Blazor.Tests
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-datatable-scrollable-footer", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable-scrollable-footer-box", component.Markup);
|
||||
Assert.Contains(@$"rz-datatable-tfoot", component.Markup);
|
||||
Assert.Contains(@$"rz-column-footer", component.Markup);
|
||||
Assert.Contains(@$"Footer", component.Markup);
|
||||
}
|
||||
|
||||
@@ -304,12 +342,12 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
@@ -324,6 +362,82 @@ namespace Radzen.Blazor.Tests
|
||||
component.Find(".rz-sortable-column").FirstElementChild.Click();
|
||||
|
||||
Assert.Contains(@$"rzi-sort-desc", component.Markup);
|
||||
|
||||
component.Find(".rz-sortable-column").FirstElementChild.Click();
|
||||
|
||||
Assert.DoesNotContain(@$"rzi-sort-desc", component.Markup);
|
||||
Assert.DoesNotContain(@$"rzi-sort-asc", component.Markup);
|
||||
}
|
||||
|
||||
|
||||
// clear Sorting tests
|
||||
[Fact]
|
||||
public void DataGrid_Renders_ClearSorting()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowSorting, true);
|
||||
});
|
||||
|
||||
component.Find(".rz-sortable-column").FirstElementChild.Click();
|
||||
Assert.Contains(@$"rzi-sort-asc", component.Markup);
|
||||
|
||||
component.Instance.Sorts.Clear();
|
||||
|
||||
Assert.DoesNotContain(@$"rzi-sort-desc", component.Markup);
|
||||
Assert.DoesNotContain(@$"rzi-sort-asc", component.Markup);
|
||||
}
|
||||
|
||||
// sorting with load data event test.
|
||||
[Fact]
|
||||
public void DataGrid_Renders_LoadDataWithOrdering()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
List<LoadDataArgs> loadDataArgs = [];
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowSorting, true);
|
||||
parameterBuilder.Add(p => p.LoadData, args => loadDataArgs.Add(args));
|
||||
});
|
||||
|
||||
component.Find(".rz-sortable-column").FirstElementChild.Click();
|
||||
Assert.Contains(@$"rzi-sort-asc", component.Markup);
|
||||
|
||||
component.Instance.Sorts.Clear();
|
||||
component.Render();
|
||||
|
||||
Assert.DoesNotContain(@$"rzi-sort-desc", component.Markup);
|
||||
Assert.DoesNotContain(@$"rzi-sort-asc", component.Markup);
|
||||
|
||||
Assert.Equal(4, loadDataArgs.Count);
|
||||
Assert.Equal("", loadDataArgs[0].OrderBy);
|
||||
Assert.Equal("Id asc", loadDataArgs[1].OrderBy);
|
||||
Assert.Equal("Id asc", loadDataArgs[2].OrderBy);
|
||||
Assert.Equal("", loadDataArgs[3].OrderBy);
|
||||
}
|
||||
|
||||
// Paging tests
|
||||
@@ -334,11 +448,20 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AllowPaging, true));
|
||||
|
||||
Assert.Contains(@$"rz-paginator-bottom", component.Markup);
|
||||
Assert.Contains(@$"rz-pager", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -348,16 +471,21 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.Top);
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
parameterBuilder.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.Top);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-paginator", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-paginator-bottom", component.Markup);
|
||||
Assert.Contains(@$"rz-pager", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -367,16 +495,59 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
parameterBuilder.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.TopAndBottom);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-pager", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataGrid_Renders_PagerDensityDefault()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.TopAndBottom);
|
||||
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.Top);
|
||||
parameters.Add<Density>(p => p.Density, Density.Default);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-paginator", component.Markup);
|
||||
Assert.Contains(@$"rz-paginator-bottom", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-density-compact", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataGrid_Renders_PagerDensityCompact()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.Top);
|
||||
parameters.Add<Density>(p => p.Density, Density.Compact);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-density-compact", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -386,7 +557,7 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
|
||||
component.Render();
|
||||
|
||||
Assert.Contains("No records to display.", component.Markup);
|
||||
@@ -400,7 +571,7 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.EmptyText, emptyText);
|
||||
@@ -416,7 +587,7 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<RenderFragment>(p => p.EmptyTemplate, builder =>
|
||||
@@ -437,18 +608,28 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
var raised = false;
|
||||
LoadDataArgs newArgs = null;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-next").Click();
|
||||
component.Find(".rz-pager-next").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(newArgs.Skip == 10);
|
||||
@@ -462,18 +643,28 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
var raised = false;
|
||||
LoadDataArgs newArgs = null;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-last").Click();
|
||||
component.Find(".rz-pager-last").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(newArgs.Skip == 90);
|
||||
@@ -487,19 +678,29 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
var raised = false;
|
||||
LoadDataArgs newArgs = null;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-next").Click();
|
||||
component.Find(".rz-paginator-prev").Click();
|
||||
component.Find(".rz-pager-next").Click();
|
||||
component.Find(".rz-pager-prev").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(newArgs.Skip == 0);
|
||||
@@ -513,19 +714,29 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
var raised = false;
|
||||
LoadDataArgs newArgs = null;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-next").Click();
|
||||
component.Find(".rz-paginator-first").Click();
|
||||
component.Find(".rz-pager-next").Click();
|
||||
component.Find(".rz-pager-first").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(newArgs.Skip == 0);
|
||||
@@ -539,17 +750,27 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
var raised = false;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-first").Click();
|
||||
component.Find(".rz-pager-first").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -561,17 +782,27 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
var raised = false;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-prev").Click();
|
||||
component.Find(".rz-pager-prev").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -583,23 +814,29 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
var raised = false;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-last").Click();
|
||||
component.Find(".rz-pager-last").Click();
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-last").Click();
|
||||
component.Find(".rz-pager-last").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -611,23 +848,29 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
var raised = false;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-last").Click();
|
||||
component.Find(".rz-pager-last").Click();
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-next").Click();
|
||||
component.Find(".rz-pager-next").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -639,19 +882,33 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
|
||||
{
|
||||
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
|
||||
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
|
||||
builder.AddAttribute(1, "Property", "Id");
|
||||
builder.AddAttribute(2, "Title", "Id");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
parameterBuilder.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
var raised = false;
|
||||
LoadDataArgs newArgs = null;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<int>(p => p.PageSize, 20);
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-next").Click();
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<int>(p => p.PageSize, 20);
|
||||
});
|
||||
|
||||
component.Find(".rz-pager-next").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(newArgs.Skip == 20);
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AllowPaging, true));
|
||||
|
||||
Assert.Contains(@$"rz-paginator-bottom", component.Markup);
|
||||
Assert.Contains(@$"rz-pager-bottom", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -44,8 +44,8 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.Top);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-paginator", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-paginator-bottom", component.Markup);
|
||||
Assert.Contains(@$"rz-pager", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-pager-bottom", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -60,8 +60,42 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.TopAndBottom);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-paginator", component.Markup);
|
||||
Assert.Contains(@$"rz-paginator-bottom", component.Markup);
|
||||
Assert.Contains(@$"rz-pager", component.Markup);
|
||||
Assert.Contains(@$"rz-pager-bottom", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataList_Renders_PagerDensityDefault()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDataList<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.Top);
|
||||
parameters.Add<Density>(p => p.Density, Density.Default);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain(@$"rz-density-compact", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataList_Renders_PagerDensityCompact()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDataList<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.Top);
|
||||
parameters.Add<Density>(p => p.Density, Density.Compact);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-density-compact", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -93,7 +127,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-next").Click();
|
||||
component.Find(".rz-pager-next").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(newArgs.Skip == 10);
|
||||
@@ -115,7 +149,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-last").Click();
|
||||
component.Find(".rz-pager-last").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(newArgs.Skip == 90);
|
||||
@@ -137,8 +171,8 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-next").Click();
|
||||
component.Find(".rz-paginator-prev").Click();
|
||||
component.Find(".rz-pager-next").Click();
|
||||
component.Find(".rz-pager-prev").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(newArgs.Skip == 0);
|
||||
@@ -160,8 +194,8 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-next").Click();
|
||||
component.Find(".rz-paginator-first").Click();
|
||||
component.Find(".rz-pager-next").Click();
|
||||
component.Find(".rz-pager-first").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(newArgs.Skip == 0);
|
||||
@@ -182,7 +216,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-first").Click();
|
||||
component.Find(".rz-pager-first").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -201,7 +235,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-prev").Click();
|
||||
component.Find(".rz-pager-prev").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -219,13 +253,13 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-last").Click();
|
||||
component.Find(".rz-pager-last").Click();
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-last").Click();
|
||||
component.Find(".rz-pager-last").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -243,13 +277,13 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<bool>(p => p.AllowPaging, true);
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-last").Click();
|
||||
component.Find(".rz-pager-last").Click();
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-next").Click();
|
||||
component.Find(".rz-pager-next").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -270,7 +304,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-paginator-next").Click();
|
||||
component.Find(".rz-pager-next").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(newArgs.Skip == 20);
|
||||
|
||||
@@ -18,10 +18,10 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
Assert.Contains(@$"rz-datepicker", component.Markup);
|
||||
Assert.Contains(@$"rz-calendar", component.Markup);
|
||||
Assert.Contains(@$"rz-datepicker-group", component.Markup);
|
||||
Assert.Contains(@$"rz-datepicker-header", component.Markup);
|
||||
Assert.Contains(@$"rz-datepicker-calendar", component.Markup);
|
||||
Assert.Contains(@$"rz-calendar-header", component.Markup);
|
||||
Assert.Contains(@$"rz-calendar-view", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -49,7 +49,8 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.ShowTime, true);
|
||||
parameters.Add<bool>(p => p.ShowSeconds, true);
|
||||
});
|
||||
@@ -69,7 +70,8 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.ShowTime, true);
|
||||
parameters.Add<bool>(p => p.ShowTimeOkButton, true);
|
||||
});
|
||||
@@ -91,7 +93,8 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var format = "d";
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.DateFormat, format);
|
||||
parameters.Add<object>(p => p.Value, DateTime.Now);
|
||||
});
|
||||
@@ -108,7 +111,8 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.ShowTime, true);
|
||||
parameters.Add(p => p.HourFormat, "12");
|
||||
});
|
||||
@@ -127,12 +131,13 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<bool>(p => p.ShowTime, true);
|
||||
parameters.Add<bool>(p => p.TimeOnly, true);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain(@$"rz-datepicker-header", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-calendar-header", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -144,12 +149,13 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<object>(p => p.Value, DateTime.Now);
|
||||
parameters.Add<bool>(p => p.AllowClear, true);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"<i class=""rz-dropdown-clear-icon rzi rzi-times""", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rz-dropdown-clear-icon rzi rzi-times""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -168,6 +174,34 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Contains(@$"tabindex=""{value}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Renders_EmptyCssClass_WhenValueIsEmpty()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Value, null));
|
||||
|
||||
Assert.Contains(@$"rz-state-empty", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_DoesNotRender_EmptyCssClass_WhenValueIsNotEmpty()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Value, DateTime.Now));
|
||||
|
||||
Assert.DoesNotContain(@$"rz-state-empty", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Renders_DisabledParameter()
|
||||
{
|
||||
@@ -212,7 +246,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Style, value));
|
||||
|
||||
Assert.Contains(@$"style=""display: inline-block;{value}""", component.Markup);
|
||||
Assert.Contains(@$"style=""{value}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -241,11 +275,12 @@ namespace Radzen.Blazor.Tests
|
||||
var raised = false;
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-datepicker-next-icon").Click();
|
||||
component.Find(".rz-calendar-next-icon").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -262,11 +297,12 @@ namespace Radzen.Blazor.Tests
|
||||
var raised = false;
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ValueChanged, args => { raised = true; newValue = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-datepicker-next-icon").Click();
|
||||
component.Find(".rz-calendar-next-icon").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -283,11 +319,12 @@ namespace Radzen.Blazor.Tests
|
||||
var raised = false;
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-datepicker-prev-icon").Click();
|
||||
component.Find(".rz-calendar-prev-icon").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -304,11 +341,12 @@ namespace Radzen.Blazor.Tests
|
||||
var raised = false;
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ValueChanged, args => { raised = true; newValue = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-datepicker-prev-icon").Click();
|
||||
component.Find(".rz-calendar-prev-icon").Click();
|
||||
|
||||
Assert.False(raised);
|
||||
}
|
||||
@@ -327,7 +365,8 @@ namespace Radzen.Blazor.Tests
|
||||
var raised = false;
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ValueChanged, args => { raised = true; newValue = args; })
|
||||
.Add(p => p.DateRender, args => { args.Disabled = dates.Contains(args.Date); });
|
||||
});
|
||||
@@ -360,7 +399,8 @@ namespace Radzen.Blazor.Tests
|
||||
var raised = false;
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ValueChanged, args => { raised = true; newValue = args; })
|
||||
.Add(p => p.DateRender, args => { args.Disabled = dates.Contains(args.Date); });
|
||||
});
|
||||
@@ -379,6 +419,72 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Null(newValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Parses_Input_Using_DateFormat()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime?>>();
|
||||
|
||||
var raised = false;
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.DateFormat, "ddMM");
|
||||
parameters.Add(p => p.ValueChanged, args => { raised = true; newValue = args; });
|
||||
});
|
||||
|
||||
var inputElement = component.Find(".rz-inputtext");
|
||||
|
||||
string input = "3012";
|
||||
ctx.JSInterop.Setup<string>("Radzen.getInputValue", invocation => true).SetResult(input);
|
||||
inputElement.Change(input);
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.Equal(new DateTime(DateTime.Now.Year, 12, 30), newValue);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Parses_Input_Using_ParseInput()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime?>>();
|
||||
|
||||
Func<string, DateTime?> customParseInput = (input) =>
|
||||
{
|
||||
if (DateTime.TryParseExact(input, "ddMM", null, DateTimeStyles.None, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
var raised = false;
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ParseInput, customParseInput);
|
||||
parameters.Add(p => p.ValueChanged, args => { raised = true; newValue = args; });
|
||||
});
|
||||
|
||||
var inputElement = component.Find(".rz-inputtext");
|
||||
|
||||
string input = "3012";
|
||||
ctx.JSInterop.Setup<string>("Radzen.getInputValue", invocation => true).SetResult(input);
|
||||
inputElement.Change(input);
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.Equal(new DateTime(DateTime.Now.Year, 12, 30), newValue);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Respects_DateTimeMaxValue()
|
||||
{
|
||||
@@ -393,26 +499,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Contains(DateTime.MaxValue.ToString(component.Instance.DateFormat), component.Markup);
|
||||
|
||||
var exception = Record.Exception(() => component.Find(".rz-datepicker-next-icon")
|
||||
.Click());
|
||||
Assert.Null(exception);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Respects_DateTimeMinValue()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, DateTime.MinValue);
|
||||
});
|
||||
|
||||
Assert.Contains(DateTime.MinValue.ToString(component.Instance.DateFormat), component.Markup);
|
||||
|
||||
var exception = Record.Exception(() => component.Find(".rz-datepicker-prev-icon")
|
||||
var exception = Record.Exception(() => component.Find(".rz-calendar-next-icon")
|
||||
.Click());
|
||||
Assert.Null(exception);
|
||||
}
|
||||
@@ -441,7 +528,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-datepicker-next-icon").Click();
|
||||
component.Find(".rz-calendar-next-icon").Click();
|
||||
component.FindAll(".rz-button-text").First(x => x.TextContent == "Ok").Click();
|
||||
|
||||
Assert.True(raised);
|
||||
@@ -505,5 +592,145 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Equal(kind, (component.Instance.Value as DateTime?)?.Kind);
|
||||
Assert.Equal(valueUtc.UtcDateTime.ToString(CultureInfo.InvariantCulture), (component.Instance.Value as DateTime?)?.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Displays_Calender_Icon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
Assert.Contains(@$"rzi-calendar", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Displays_Schedule_Icon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.TimeOnly, true);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rzi-time", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Supports_DateOnly()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
DateOnly? dateOnly = new DateOnly(2024, 1, 31);
|
||||
DateOnly? valueChangedValue = null!;
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateOnly?>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, dateOnly);
|
||||
parameters.Add(p => p.ValueChanged, args => { valueChangedValue = args; });
|
||||
});
|
||||
|
||||
Assert.False(component.Instance.ShowTime);
|
||||
var input = component.Find("input");
|
||||
input.GetAttribute("value").MarkupMatches(dateOnly.ToString());
|
||||
|
||||
// update to new value
|
||||
var inputElement = component.Find(".rz-inputtext");
|
||||
DateOnly? enteredValue = new DateOnly(2024, 2, 28);
|
||||
ctx.JSInterop.Setup<string>("Radzen.getInputValue", invocation => true).SetResult(enteredValue.Value.ToShortDateString());
|
||||
inputElement.Change(enteredValue);
|
||||
|
||||
input.GetAttribute("value").MarkupMatches(enteredValue.ToString());
|
||||
Assert.Equal(enteredValue, component.Instance.Value);
|
||||
Assert.Equal(enteredValue, valueChangedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Supports_TimeOnly()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
TimeOnly? timeOnly = new TimeOnly(23, 59, 59);
|
||||
TimeOnly? valueChangedValue = null!;
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<TimeOnly>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, timeOnly);
|
||||
parameters.Add(p => p.ValueChanged, args => { valueChangedValue = args; });
|
||||
});
|
||||
|
||||
Assert.True(component.Instance.TimeOnly);
|
||||
Assert.True(component.Instance.ShowTime);
|
||||
var input = component.Find("input");
|
||||
input.GetAttribute("value").MarkupMatches(timeOnly.ToString());
|
||||
|
||||
// update to new value
|
||||
var inputElement = component.Find(".rz-inputtext");
|
||||
TimeOnly? enteredValue = new TimeOnly(1, 4, 5);
|
||||
ctx.JSInterop.Setup<string>("Radzen.getInputValue", invocation => true).SetResult(enteredValue.Value.ToLongTimeString());
|
||||
inputElement.Change(enteredValue);
|
||||
|
||||
input.GetAttribute("value").MarkupMatches(enteredValue.ToString());
|
||||
Assert.Equal(enteredValue, component.Instance.Value);
|
||||
Assert.Equal(enteredValue, valueChangedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_ShowCalendarWeek_WeekNumberAddedInAdditionalColumn()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>(parameter =>
|
||||
{
|
||||
parameter.Add(p => p.ShowCalendarWeek, true);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-calendar-week-number", component.Markup);
|
||||
Assert.Equal(8, component.FindAll(".rz-calendar-view th").Count());
|
||||
// check header and week number column
|
||||
Assert.Single(component.FindAll("th.rz-datepicker-week-number"));
|
||||
Assert.Equal(6, component.FindAll("td.rz-calendar-week-number").Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_ShowCalendarWeekFalse_NoAdditionalColumn()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>(parameter =>
|
||||
{
|
||||
parameter.Add(p => p.ShowCalendarWeek, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain(@$"rz-calendar-week-number", component.Markup);
|
||||
Assert.Equal(7, component.FindAll(".rz-calendar-view th").Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_ShowCalendarWeekWithCustomTitle_TitleCorrectlyRendered()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>(parameter =>
|
||||
{
|
||||
parameter.Add(p => p.ShowCalendarWeek, true);
|
||||
parameter.Add(p => p.CalendarWeekTitle, "Wk");
|
||||
});
|
||||
|
||||
var weekNumberHeader = component.Find(".rz-calendar-view th.rz-datepicker-week-number");
|
||||
Assert.Contains("Wk", weekNumberHeader.InnerHtml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
58
Radzen.Blazor.Tests/Dollars.cs
Normal file
58
Radzen.Blazor.Tests/Dollars.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Radzen.Blazor.Tests;
|
||||
|
||||
[TypeConverter(typeof(DollarsTypeConverter))]
|
||||
public readonly record struct Dollars(decimal Amount) : IComparable<decimal>
|
||||
{
|
||||
public int CompareTo(decimal other)
|
||||
{
|
||||
return Amount.CompareTo(other);
|
||||
}
|
||||
|
||||
public string ToString(string format, CultureInfo culture = null) => Amount.ToString(format, culture ?? CultureInfo.CreateSpecificCulture("en-US"));
|
||||
public override string ToString() => Amount.ToString("F2", CultureInfo.CreateSpecificCulture("en-US"));
|
||||
}
|
||||
|
||||
public class DollarsTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(decimal) ||
|
||||
sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(decimal))
|
||||
return true;
|
||||
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is decimal d)
|
||||
return new Dollars(d);
|
||||
|
||||
if (value is string s)
|
||||
return decimal.TryParse(s, culture, out var val) ? new Dollars(val) : null;
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(decimal) && value is Dollars d)
|
||||
return d.Amount;
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Dom;
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
@@ -10,18 +15,20 @@ namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public int Id { get; set; }
|
||||
public bool Disabled { get; set; } = false;
|
||||
}
|
||||
|
||||
private static IRenderedComponent<RadzenDropDown<T>> DropDown<T>(TestContext ctx, Action<ComponentParameterCollectionBuilder<RadzenDropDown<T>>> configure = null)
|
||||
{
|
||||
var data = new [] {
|
||||
var data = new[] {
|
||||
new DataItem { Text = "Item 1", Id = 1 },
|
||||
new DataItem { Text = "Item 2", Id = 2 },
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDown<T>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, nameof(DataItem.Text));
|
||||
|
||||
@@ -38,6 +45,23 @@ namespace Radzen.Blazor.Tests
|
||||
return component;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Dropdown_SelectItem_Method_Should_Not_Throw()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = DropDown<int>(ctx);
|
||||
|
||||
var items = component.FindAll(".rz-dropdown-item");
|
||||
|
||||
Assert.Equal(2, items.Count);
|
||||
|
||||
//this throws
|
||||
await component.InvokeAsync(async () => await component.Instance.SelectItem(1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_RendersItems()
|
||||
{
|
||||
@@ -79,7 +103,8 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = DropDown<string>(ctx, parameters => {
|
||||
var component = DropDown<string>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Text));
|
||||
});
|
||||
|
||||
@@ -94,6 +119,35 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Contains("rz-state-highlight", items[0].ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_Respects_ItemEqualityComparer()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
List<DataItem> boundCollection = [new() { Text = "Item 2" }];
|
||||
|
||||
var component = DropDown<string>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ItemComparer, new DataItemComparer());
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Value, boundCollection);
|
||||
});
|
||||
|
||||
var selectedItems = component.FindAll(".rz-state-highlight");
|
||||
Assert.Equal(1, selectedItems.Count);
|
||||
Assert.Equal("Item 2", selectedItems[0].TextContent.Trim());
|
||||
|
||||
// select Item 1 in list
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
items[0].Click();
|
||||
component.Render();
|
||||
|
||||
selectedItems = component.FindAll(".rz-state-highlight");
|
||||
Assert.Equal(2, selectedItems.Count);
|
||||
Assert.Equal("Item 1", selectedItems[0].TextContent.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_AppliesSelectionStyleWhenMultipleSelectionIsEnabled()
|
||||
{
|
||||
@@ -101,7 +155,8 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = DropDown<string>(ctx, parameters => {
|
||||
var component = DropDown<string>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Text));
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
});
|
||||
@@ -122,5 +177,181 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Equal(2, selectedItems.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_AppliesValueTemplateOnMultipleSelection()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var valueTemplateFragment = (RenderFragment<dynamic>)(_context =>
|
||||
builder =>
|
||||
{
|
||||
builder.AddContent(0, $"value: {_context.Text}");
|
||||
});
|
||||
|
||||
var component = DropDown<string>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true)
|
||||
.Add(p => p.ValueTemplate, valueTemplateFragment);
|
||||
});
|
||||
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
|
||||
items[0].Click();
|
||||
items[1].Click();
|
||||
|
||||
component.Render();
|
||||
|
||||
var selectedItems = component.Find(".rz-inputtext");
|
||||
|
||||
Assert.Contains("value: Item 1,value: Item 2", selectedItems.Text());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_AppliesValueTemplateWhenTepmlateDefined()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var valueTemplateFragment = (RenderFragment<dynamic>)(_context =>
|
||||
builder =>
|
||||
{
|
||||
builder.AddContent(0, $"value: {_context.Text}");
|
||||
});
|
||||
|
||||
var templateFragment = (RenderFragment<dynamic>)(_context =>
|
||||
builder =>
|
||||
{
|
||||
builder.AddContent(0, $"template: {_context.Text}");
|
||||
});
|
||||
|
||||
var component = DropDown<string>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true)
|
||||
.Add(p => p.ValueTemplate, valueTemplateFragment)
|
||||
.Add(p => p.Template, templateFragment);
|
||||
});
|
||||
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
|
||||
items[0].Click();
|
||||
items[1].Click();
|
||||
|
||||
component.Render();
|
||||
|
||||
var selectedItems = component.Find(".rz-inputtext");
|
||||
var itemsText = component.FindAll(".rz-multiselect-item-content");
|
||||
|
||||
Assert.Collection(itemsText, item => Assert.Contains("template: Item 1", item.Text()), item => Assert.Contains("template: Item 2", item.Text()));
|
||||
Assert.Contains("value: Item 1,value: Item 2", selectedItems.Text());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_AppliesValueTemplateOnMultipleSelectionChips()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var valueTemplateFragment = (RenderFragment<dynamic>)(_context =>
|
||||
builder =>
|
||||
{
|
||||
builder.AddContent(0, $"value: {_context.Text}");
|
||||
});
|
||||
|
||||
var component = DropDown<string>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true)
|
||||
.Add(p => p.ValueTemplate, valueTemplateFragment)
|
||||
.Add(p => p.Chips, true);
|
||||
});
|
||||
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
|
||||
items[0].Click();
|
||||
items[1].Click();
|
||||
|
||||
component.Render();
|
||||
|
||||
var selectedItems = component.FindAll(".rz-chip-text");
|
||||
|
||||
Assert.Collection(selectedItems, item => Assert.Contains("value: Item 1", item.Text()), item => Assert.Contains("value: Item 2", item.Text()));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, true, false, true, "false")]
|
||||
[InlineData(true, false, true, false, "true")]
|
||||
[InlineData(true, false, false, false, "false")]
|
||||
[InlineData(true, false, false, true, "true")]
|
||||
[InlineData(false, false, false, true, "false")]
|
||||
public void DropDown_AllSelectedFalseIfListIsAllDisabled(bool item1Selected, bool item1Disabled, bool item2Selected, bool item2Disabled, string expectedAriaCheckedValue)
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var data = new[] {
|
||||
new DataItem { Text = "Item 1", Id = 1, Disabled = item1Disabled },
|
||||
new DataItem { Text = "Item 2", Id = 2, Disabled = item2Disabled },
|
||||
};
|
||||
|
||||
List<int> selectedValues = [];
|
||||
if (item1Selected)
|
||||
{
|
||||
selectedValues.Add(data[0].Id);
|
||||
}
|
||||
if (item2Selected)
|
||||
{
|
||||
selectedValues.Add(data[1].Id);
|
||||
}
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDown<DataItem>>(parameters => parameters
|
||||
.Add(p => p.Data, data)
|
||||
.Add(p => p.Value, selectedValues)
|
||||
.Add(p => p.Multiple, true)
|
||||
.Add(p => p.AllowSelectAll, true)
|
||||
.Add(p => p.TextProperty, nameof(DataItem.Text))
|
||||
.Add(p => p.DisabledProperty, nameof(DataItem.Disabled))
|
||||
.Add(p => p.ValueProperty, nameof(DataItem.Id)));
|
||||
|
||||
Assert.NotNull(component);
|
||||
var highlightedItems = component.FindAll(".rz-state-highlight");
|
||||
Assert.Equal(selectedValues.Count, highlightedItems.Count);
|
||||
|
||||
|
||||
var selectAllCheckBox = component.Find(".rz-multiselect-header input[type='checkbox']");
|
||||
|
||||
Assert.Equal(expectedAriaCheckedValue, selectAllCheckBox.GetAttribute("aria-checked"));
|
||||
}
|
||||
|
||||
class DataItemComparer : IEqualityComparer<DataItem>, IEqualityComparer<object>
|
||||
{
|
||||
public bool Equals(DataItem x, DataItem y)
|
||||
{
|
||||
if (ReferenceEquals(x, y)) return true;
|
||||
if (x is null) return false;
|
||||
if (y is null) return false;
|
||||
if (x.GetType() != y.GetType()) return false;
|
||||
return x.Text == y.Text;
|
||||
}
|
||||
|
||||
public int GetHashCode(DataItem obj)
|
||||
{
|
||||
return obj.Text.GetHashCode();
|
||||
}
|
||||
|
||||
public new bool Equals(object x, object y)
|
||||
{
|
||||
return Equals((DataItem)x, (DataItem)y);
|
||||
}
|
||||
|
||||
public int GetHashCode(object obj)
|
||||
{
|
||||
return GetHashCode((DataItem)obj);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
423
Radzen.Blazor.Tests/ExpressionParserTests.cs
Normal file
423
Radzen.Blazor.Tests/ExpressionParserTests.cs
Normal file
@@ -0,0 +1,423 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public enum CarType
|
||||
{
|
||||
Sedan,
|
||||
Coupe
|
||||
}
|
||||
|
||||
public class ExpressionParserTests
|
||||
{
|
||||
class Person
|
||||
{
|
||||
public short? Age { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool? Famous { get; set; }
|
||||
public DateTime BirthDate { get; set; }
|
||||
}
|
||||
|
||||
public class Car
|
||||
{
|
||||
public CarType Type { get; set; }
|
||||
}
|
||||
|
||||
public enum Status
|
||||
{
|
||||
Office,
|
||||
Remote,
|
||||
}
|
||||
class OrderDetail
|
||||
{
|
||||
public Product Product { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public Order Order { get; set; }
|
||||
|
||||
public List<WorkStatus> WorkStatuses { get; set; }
|
||||
public List<Status> Statuses { get; set; }
|
||||
}
|
||||
|
||||
class Category
|
||||
{
|
||||
public string CategoryName { get; set; }
|
||||
|
||||
}
|
||||
|
||||
class Order
|
||||
{
|
||||
public DateTime OrderDate { get; set; }
|
||||
}
|
||||
|
||||
class Product
|
||||
{
|
||||
public string ProductName { get; set; }
|
||||
|
||||
public Category Category { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_ParseBindaryExpression()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<Person>("p => p.Name == \"foo\"");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new Person() { Name = "foo" }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_ParseConditionalExpression()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => (it.Product.ProductName == null ? \"\" : it.Product.ProductName).Contains(\"Queso\") && it.Quantity == 50");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new OrderDetail() { Product = new Product { ProductName = "Queso" }, Quantity = 50 }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_ParseNestedLogicalOperations()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => (it.Product.ProductName == null ? \"\" : it.Product.ProductName).Contains(\"Queso\") && (it.Quantity == 50 || it.Quantity == 12)");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new OrderDetail() { Product = new Product { ProductName = "Queso" }, Quantity = 12 }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportDateTimeParsing()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => it.Order.OrderDate >= DateTime.Parse(\"2025-02-11\")");
|
||||
|
||||
var func = expression.Compile();
|
||||
Assert.True(func(new OrderDetail { Order = new Order { OrderDate = new DateTime(2025, 2, 11) } }));
|
||||
}
|
||||
|
||||
class ItemWithGenericProperty<T>
|
||||
{
|
||||
public T Value { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportDateOnlyParsing()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<DateOnly>>("it => it.Value >= DateOnly.Parse(\"2025-02-11\")");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new ItemWithGenericProperty<DateOnly> { Value = new DateOnly(2025, 2, 11) }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportTimeOnlyParsing()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<TimeOnly>>("it => it.Value >= TimeOnly.Parse(\"12:00:00\")");
|
||||
var func = expression.Compile();
|
||||
Assert.True(func(new ItemWithGenericProperty<TimeOnly> { Value = new TimeOnly(12, 0, 0) }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportGuidParsing()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<Guid>>("it => it.Value == Guid.Parse(\"f0e7e7d8-4f4d-4b5f-8b3e-3f1d1b4f5f5f\")");
|
||||
var func = expression.Compile();
|
||||
Assert.True(func(new ItemWithGenericProperty<Guid> { Value = Guid.Parse("f0e7e7d8-4f4d-4b5f-8b3e-3f1d1b4f5f5f") }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportDateTimeOffsetParsing()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<DateTimeOffset>>("it => it.Value == DateTimeOffset.Parse(\"2025-02-11\")");
|
||||
var func = expression.Compile();
|
||||
Assert.True(func(new ItemWithGenericProperty<DateTimeOffset> { Value = DateTimeOffset.Parse("2025-02-11") }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportEnumWithCasts()
|
||||
{
|
||||
var typeLocator = (string type) => AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName == type);
|
||||
|
||||
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<CarType[]>>("it => it.Value.Any(i => (new []{0}).Contains(i))", typeLocator);
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new ItemWithGenericProperty<CarType[]> { Value = [CarType.Sedan] }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportNullableCollection()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<Person>("it => new bool?[]{ false }.Contains(it.Famous)");
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new Person { Famous = false }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportEnumCollections()
|
||||
{
|
||||
var typeLocator = (string type) => AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName == type);
|
||||
|
||||
var expression = ExpressionParser.ParsePredicate<Car>($"it => it.Type == (Radzen.Blazor.Tests.CarType)1", typeLocator);
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new Car { Type = CarType.Coupe }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportCollectionLiterals()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => (new []{\"Tofu\"}).Contains(it.Product.ProductName)");
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new OrderDetail { Product = new Product { ProductName = "Tofu" } }));
|
||||
}
|
||||
|
||||
class WorkStatus
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportNestedLambdas()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => it.WorkStatuses.Any(i => (new []{\"Office\"}).Contains(i.Name))");
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new OrderDetail { WorkStatuses = [new() { Name = "Office" }] }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportNestedLambdasWithNot()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => it.WorkStatuses.Any(i => !(new []{\"Office\"}).Contains(i.Name))");
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.False(func(new OrderDetail { WorkStatuses = [new() { Name = "Office" }] }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportNestedLambdasWithEnums()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => it.Statuses.Any(i => (new []{1}).Contains(i))");
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new OrderDetail { Statuses = new List<Status>() { (Status)1 } }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportNestedLambdasWithComplexMethod()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => new [] { (Status)1 }.Intersect(it.Statuses).Any()", type => typeof(Status));
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new OrderDetail { Statuses = new List<Status>() { (Status)1 } }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportToLower()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<Person>("it => (it.Name == null ? \"\" : it.Name).ToLower().Contains(\"na\".ToLower())");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new Person { Name = "Nana" }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportNullableProperties()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<Person>("it => it.Age == 50");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new Person { Age = 50 }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportNullablePropertiesWithArray()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<Person>("it => (new []{}).Contains(it.Famous)");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.False(func(new Person { Famous = null }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportDateTimeWithArray()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<Person>("it => (new []{DateTime.Parse(\"5/5/2000 12:00:00 AM\")}).Contains(it.BirthDate)");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new Person { BirthDate = DateTime.Parse("5/5/2000 12:00:00 AM") }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportNumericConversion()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<double>>("it => it.Value == 50");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new ItemWithGenericProperty<double> { Value = 50.0 }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportNullCoalescence()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<double?>>("it => (it.Value ?? 0) == 0");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new ItemWithGenericProperty<double?> { Value = null }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportNullConditionAndCoalescence()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<Person>("it => (((it == null) ? null : it.Name) ?? \"\").Contains(\"Da\")");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new Person { Name = "Dali" }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_CreateProjection()
|
||||
{
|
||||
var expression = ExpressionParser.ParseLambda<OrderDetail>("it => new { ProductName = it.Product.ProductName}");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
var result = func.DynamicInvoke(new OrderDetail { Product = new Product { ProductName = "Queso" } });
|
||||
|
||||
var property = result.GetType().GetProperty("ProductName");
|
||||
|
||||
Assert.Equal(typeof(string), property.PropertyType);
|
||||
|
||||
Assert.Equal("Queso", property.GetValue(result));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_CreateProjectionFromNestedAccess()
|
||||
{
|
||||
var expression = ExpressionParser.ParseLambda<OrderDetail>("it => new { it.Product.Category.CategoryName }");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
var orderDetail = new OrderDetail { Product = new Product { Category = new Category { CategoryName = "Beverages" } } };
|
||||
|
||||
var x = new { orderDetail?.Product?.Category?.CategoryName };
|
||||
|
||||
var result = func.DynamicInvoke(orderDetail);
|
||||
|
||||
var property = result.GetType().GetProperty("CategoryName");
|
||||
|
||||
Assert.Equal(typeof(string), property.PropertyType);
|
||||
|
||||
Assert.Equal("Beverages", property.GetValue(result));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_CreateProjectionFromNestedConditionalAccess()
|
||||
{
|
||||
var expression = ExpressionParser.ParseLambda<OrderDetail>("it => new { it.Product?.Category?.CategoryName }");
|
||||
|
||||
var func = expression.Compile();
|
||||
|
||||
var orderDetail = new OrderDetail { Product = new Product { Category = new Category { CategoryName = "Beverages" } } };
|
||||
|
||||
var x = new { orderDetail?.Product?.Category?.CategoryName };
|
||||
|
||||
var result = func.DynamicInvoke(orderDetail);
|
||||
|
||||
var property = result.GetType().GetProperty("CategoryName");
|
||||
|
||||
Assert.Equal(typeof(string), property.PropertyType);
|
||||
|
||||
Assert.Equal("Beverages", property.GetValue(result));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_CreateProjectionFromConditionalAccess()
|
||||
{
|
||||
var expression = ExpressionParser.ParseLambda<OrderDetail>("it => new { ProductName = it.Product?.ProductName}");
|
||||
var func = expression.Compile();
|
||||
var result = func.DynamicInvoke(new OrderDetail { Product = null });
|
||||
|
||||
var property = result.GetType().GetProperty("ProductName");
|
||||
|
||||
Assert.Equal(typeof(string), property.PropertyType);
|
||||
Assert.Null(property.GetValue(result));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_CreateProjectionFromNestedConditionalAccessAndAssignment()
|
||||
{
|
||||
var expression = ExpressionParser.ParseLambda<OrderDetail>("it => new { CategoryName = it.Product?.Category?.CategoryName}");
|
||||
var func = expression.Compile();
|
||||
var result = func.DynamicInvoke(new OrderDetail { Product = null });
|
||||
|
||||
var property = result.GetType().GetProperty("CategoryName");
|
||||
|
||||
Assert.Equal(typeof(string), property.PropertyType);
|
||||
Assert.Null(property.GetValue(result));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SelectByString()
|
||||
{
|
||||
var list = new List<OrderDetail> { new OrderDetail { Product = new Product { ProductName = "Chai" } } }.AsQueryable();
|
||||
|
||||
var result = DynamicExtensions.Select(list, "Product.ProductName as ProductName");
|
||||
|
||||
Assert.Equal("Chai", result.ElementType.GetProperty("ProductName").GetValue(result.FirstOrDefault()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SelectByWithUntypedIQueryableString()
|
||||
{
|
||||
IQueryable list = new List<OrderDetail> { new OrderDetail { Product = new Product { ProductName = "Chai" } } }.AsQueryable();
|
||||
|
||||
var result = DynamicExtensions.Select(list, "Product.ProductName as ProductName");
|
||||
|
||||
Assert.Equal("Chai", result.ElementType.GetProperty("ProductName").GetValue(result.FirstOrDefault()));
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportDictionaryIndexAccess()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<Dictionary<string, object>>("it => (int)it[\"foo\"] == 1");
|
||||
var func = expression.Compile();
|
||||
Assert.True(func(new Dictionary<string, object> { ["foo"] = 1 }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportDictionaryIndexAccessWithNullableCast()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<Dictionary<string, object>>("it => (Int32?)it[\"foo\"] == null");
|
||||
var func = expression.Compile();
|
||||
Assert.True(func(new Dictionary<string, object> { ["foo"] = null }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_SupportArrayIndexAccess()
|
||||
{
|
||||
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<int[]>>("it => it.Value[0] == 1");
|
||||
var func = expression.Compile();
|
||||
|
||||
Assert.True(func(new ItemWithGenericProperty<int[]> { Value = [1] }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, value));
|
||||
|
||||
Assert.Contains(@$"<i class=""rzi"">{value}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rzi"">{value}</i>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -104,11 +104,11 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AllowCollapse, true));
|
||||
|
||||
Assert.Contains(@"<span class=""rz-fieldset-toggler rzi rzi-w rzi-minus""></span>", component.Markup);
|
||||
Assert.Contains(@"<span class=""notranslate rz-fieldset-toggler rzi rzi-w rzi-minus""></span>", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.Collapsed, true));
|
||||
|
||||
Assert.Contains(@"<span class=""rz-fieldset-toggler rzi rzi-w rzi-plus""></span>", component.Markup);
|
||||
Assert.Contains(@"<span class=""notranslate rz-fieldset-toggler rzi rzi-w rzi-plus""></span>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Radzen.Blazor.Tests
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(@$">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""rzi""", component.Markup);
|
||||
Assert.Contains(@$"class=""notranslate rzi""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -58,6 +58,18 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Contains(@$"rzi-primary", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Icon_Renders_IconColor()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenIcon>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(icon => icon.IconColor, Colors.Primary));
|
||||
|
||||
Assert.Contains(@$"color:", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Icon_NotRenders_IconStyleClass_WhenNull()
|
||||
{
|
||||
@@ -69,5 +81,17 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.DoesNotContain(@$"rzi-primary", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Icon_NotRenders_IconColor_WhenNull()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenIcon>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(icon => icon.IconColor, null));
|
||||
|
||||
Assert.DoesNotContain(@$"color:", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(@$"<i class=""rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rzi"">{icon}</i>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -78,6 +78,20 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Contains(@$"target=""{target}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Link_Renders_DisabledParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenLink>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true));
|
||||
|
||||
Assert.Contains("class=\"rz-link rz-link-disabled active\"", component.Markup);
|
||||
|
||||
Assert.DoesNotContain("href=", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Icon_Renders_UnmatchedParameter()
|
||||
{
|
||||
|
||||
@@ -119,13 +119,54 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenMask>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""on""", component.Markup);
|
||||
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("autocomplete", "custom"));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""custom""", component.Markup);
|
||||
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
|
||||
|
||||
component.Instance.DefaultAutoCompleteAttribute = "autocomplete-custom";
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""autocomplete-custom""", component.Markup);
|
||||
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mask_Renders_TypedAutoCompleteParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenMask>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""on""", component.Markup);
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.AdditionalName));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.AdditionalName.GetAutoCompleteValue()}""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Email));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.Email.GetAutoCompleteValue()}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
214
Radzen.Blazor.Tests/NotificationServiceTests.cs
Normal file
214
Radzen.Blazor.Tests/NotificationServiceTests.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class NotificationServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void NotificationService_IsMessageIsNull_ExceptionExpected()
|
||||
{
|
||||
NotificationService notificationService = new NotificationService();
|
||||
NotificationMessage notificationMessage = null;
|
||||
|
||||
var exception = Record.Exception(() => notificationService.Notify(notificationMessage));
|
||||
|
||||
Assert.IsType<ArgumentNullException>(exception);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationService_CheckAreTwoMessages_Equals()
|
||||
{
|
||||
var messageOne = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
};
|
||||
|
||||
var messageTwo = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
};
|
||||
|
||||
Assert.True(messageOne.Equals(messageTwo));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationService_CheckAreTwoMessages_NotEquals()
|
||||
{
|
||||
var messageOne = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
};
|
||||
|
||||
var messageTwo = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary Two",
|
||||
Detail = "Info Detail Two",
|
||||
Duration = 6000
|
||||
};
|
||||
|
||||
Assert.False(messageOne.Equals(messageTwo));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationService_CheckAreTwoMessages_EqualsByReference()
|
||||
{
|
||||
var messageOne = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
};
|
||||
|
||||
var messageTwo = messageOne;
|
||||
|
||||
Assert.True(messageOne.Equals(messageTwo));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationService_CheckAreTwoMessages_EqualsByOperator()
|
||||
{
|
||||
var messageOne = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
};
|
||||
|
||||
var messageTwo = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
};
|
||||
|
||||
Assert.True(messageOne == messageTwo);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationService_CheckAreTwoMessages_NotEqualsByOperator()
|
||||
{
|
||||
var messageOne = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
};
|
||||
|
||||
var messageTwo = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary Two",
|
||||
Detail = "Info Detail Two",
|
||||
Duration = 6000
|
||||
};
|
||||
|
||||
Assert.True(messageOne != messageTwo);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationService_CheckAreTwoMessages_EqualsByHashCode()
|
||||
{
|
||||
var messageOne = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
};
|
||||
|
||||
var messageTwo = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
};
|
||||
|
||||
var messageOneHashCode = messageOne.GetHashCode();
|
||||
var messageTwoHashCode = messageTwo.GetHashCode();
|
||||
|
||||
Assert.Equal(messageOneHashCode, messageTwoHashCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationService_CheckAreTwoMessages_NotEqualsByHashCode()
|
||||
{
|
||||
var messageOne = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
};
|
||||
|
||||
var messageTwo = new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary Two",
|
||||
Detail = "Info Detail Tow",
|
||||
Duration = 5000
|
||||
};
|
||||
|
||||
var messageOneHashCode = messageOne.GetHashCode();
|
||||
var messageTwoHashCode = messageTwo.GetHashCode();
|
||||
|
||||
Assert.NotEqual(messageOneHashCode, messageTwoHashCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationService_MessagesCount_AfterAddingMessages()
|
||||
{
|
||||
NotificationService notificationService = new NotificationService();
|
||||
|
||||
//Messages are the same so only one should be added
|
||||
notificationService.Notify(NotificationSeverity.Info, "Info Summary", "Info Detail", 4000);
|
||||
notificationService.Notify(new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
});
|
||||
|
||||
int expectedMessagesNumber = 1;
|
||||
|
||||
Assert.Equal(expectedMessagesNumber, notificationService.Messages.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationService_MessagesCount_AfterAddingTwoDifferentMessages()
|
||||
{
|
||||
NotificationService notificationService = new NotificationService();
|
||||
|
||||
//Messages are the same so only one should be added
|
||||
notificationService.Notify(NotificationSeverity.Info, "Info Summary 2", "Info Detail 2", 6000);
|
||||
notificationService.Notify(new NotificationMessage()
|
||||
{
|
||||
Severity = NotificationSeverity.Info,
|
||||
Summary = "Info Summary",
|
||||
Detail = "Info Detail",
|
||||
Duration = 4000
|
||||
});
|
||||
|
||||
int expectedMessagesNumber = 2;
|
||||
|
||||
Assert.Equal(expectedMessagesNumber, notificationService.Messages.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
176
Radzen.Blazor.Tests/NumericRangeValidatorTests.cs
Normal file
176
Radzen.Blazor.Tests/NumericRangeValidatorTests.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class NumericRangeValidatorTests
|
||||
{
|
||||
class FormComponentTestDouble : IRadzenFormComponent
|
||||
{
|
||||
public bool IsBound => false;
|
||||
|
||||
public bool HasValue => true;
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public FieldIdentifier FieldIdentifier => throw new System.NotImplementedException();
|
||||
|
||||
public object GetValue()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public ValueTask FocusAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
public bool Visible { get; set; }
|
||||
public IFormFieldContext FormFieldContext => null;
|
||||
|
||||
public object Value { get; set; }
|
||||
}
|
||||
|
||||
class RadzenNumericRangeValidatorTestDouble : RadzenNumericRangeValidator
|
||||
{
|
||||
public bool Validate(object value)
|
||||
{
|
||||
return base.Validate(new FormComponentTestDouble { Value = value });
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_Exception_If_Min_And_Max_Are_Null()
|
||||
{
|
||||
var validator = new RadzenNumericRangeValidatorTestDouble();
|
||||
|
||||
Assert.Throws<System.ArgumentException>(() => validator.Validate(1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_False_If_Value_Is_Null()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0).Add(p => p.Max, 10));
|
||||
});
|
||||
|
||||
Assert.False(component.Instance.Validate(null));
|
||||
}
|
||||
[Fact]
|
||||
public void Returns_True_If_Value_Is_Null_And_AllowNull_Is_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0).Add(p => p.Max, 10).Add(p => p.AllowNull, true));
|
||||
});
|
||||
|
||||
Assert.True(component.Instance.Validate(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_False_If_Value_Overflows()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0).Add(p => p.Max, 10));
|
||||
});
|
||||
|
||||
Assert.False(component.Instance.Validate(long.MaxValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_True_If_Value_Is_Greater_Than_Min()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0));
|
||||
|
||||
Assert.True(component.Instance.Validate(1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_True_If_Value_Is_Equal_To_Min()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0));
|
||||
|
||||
Assert.True(component.Instance.Validate(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_True_If_Value_Is_Less_Than_Max()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Max, 10));
|
||||
|
||||
Assert.True(component.Instance.Validate(9));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_True_If_Value_Is_Equal_To_Max()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Max, 10));
|
||||
|
||||
Assert.True(component.Instance.Validate(10));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_True_If_Value_Is_Between_Min_And_Max()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0).Add(p => p.Max, 10));
|
||||
|
||||
Assert.True(component.Instance.Validate(5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_True_If_Value_Is_Between_Min_And_Max_And_They_Are_Nullable()
|
||||
{
|
||||
int? min = 0;
|
||||
int? max = 10;
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, min).Add(p => p.Max, max));
|
||||
Assert.True(component.Instance.Validate(5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_True_When_Value_Is_Of_DifferentType()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0m).Add(p => p.Max, 10m));
|
||||
|
||||
Assert.True(component.Instance.Validate(5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_False_If_Cannot_Conert_Value()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0m).Add(p => p.Max, 10m));
|
||||
|
||||
Assert.False(component.Instance.Validate(DateTime.Now));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using Bunit;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
@@ -14,9 +16,9 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains(@$"rz-spinner", component.Markup);
|
||||
Assert.Contains(@$"rz-spinner-up", component.Markup);
|
||||
Assert.Contains(@$"rz-spinner-down", component.Markup);
|
||||
Assert.Contains(@$"rz-numeric", component.Markup);
|
||||
Assert.Contains(@$"rz-numeric-up", component.Markup);
|
||||
Assert.Contains(@$"rz-numeric-down", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -52,11 +54,41 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<decimal?>(p => p.Min, minValue);
|
||||
});
|
||||
|
||||
component.Find(".rz-spinner-down").Click();
|
||||
component.Find(".rz-numeric-down").Click();
|
||||
|
||||
Assert.False(raised, $"Numeric value should Change event if value is less than min value.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Respect_Nullable_With_MinParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<double?>>();
|
||||
|
||||
var raised = false;
|
||||
var value = 3.5;
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
|
||||
parameters.Add<decimal?>(p => p.Min, 1);
|
||||
});
|
||||
|
||||
component.Find("input").Change(value);
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(object.Equals(value, newValue));
|
||||
|
||||
component.Find("input").Change("");
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(object.Equals(null, newValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Respect_MaxParameter()
|
||||
{
|
||||
@@ -76,7 +108,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<decimal?>(p => p.Max, maxValue);
|
||||
});
|
||||
|
||||
component.Find(".rz-spinner-up").Click();
|
||||
component.Find(".rz-numeric-up").Click();
|
||||
|
||||
Assert.False(raised, $"Numeric value should Change event if value is less than min value.");
|
||||
}
|
||||
@@ -184,13 +216,54 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<double>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""on""", component.Markup);
|
||||
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("autocomplete", "custom"));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""custom""", component.Markup);
|
||||
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
|
||||
|
||||
component.Instance.DefaultAutoCompleteAttribute = "autocomplete-custom";
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""autocomplete-custom""", component.Markup);
|
||||
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Renders_TypedAutoCompleteParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<double>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""on""", component.Markup);
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.BdayMonth));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.BdayMonth.GetAutoCompleteValue()}""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.BdayYear));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.BdayYear.GetAutoCompleteValue()}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -256,7 +329,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-spinner-up").Click();
|
||||
component.Find(".rz-numeric-up").Click();
|
||||
|
||||
Assert.True(raised, "Numeric Change should be raised on step up");
|
||||
Assert.True(object.Equals(expectedValue, newValue), $"Numeric value should be incremented on step up. Expected value: {expectedValue}, value: {newValue}");
|
||||
@@ -265,7 +338,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ValueChanged, args => { raised = true; }));
|
||||
|
||||
component.Find(".rz-spinner-up").Click();
|
||||
component.Find(".rz-numeric-up").Click();
|
||||
|
||||
Assert.True(raised, "Numeric ValueChanged should be raised on step up");
|
||||
}
|
||||
@@ -291,7 +364,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
|
||||
});
|
||||
|
||||
component.Find(".rz-spinner-down").Click();
|
||||
component.Find(".rz-numeric-down").Click();
|
||||
|
||||
Assert.True(raised, "Numeric Change should be raised on step up");
|
||||
Assert.True(object.Equals(expectedValue, newValue), $"Numeric value should be incremented on step up. Expected value: {expectedValue}, value: {newValue}");
|
||||
@@ -300,7 +373,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ValueChanged, args => { raised = true; }));
|
||||
|
||||
component.Find(".rz-spinner-down").Click();
|
||||
component.Find(".rz-numeric-down").Click();
|
||||
|
||||
Assert.True(raised, "Numeric ValueChanged should be raised on step up");
|
||||
}
|
||||
@@ -314,9 +387,9 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains(@$"rz-spinner-button-icon", component.Markup);
|
||||
Assert.Contains(@$"rz-spinner-up", component.Markup);
|
||||
Assert.Contains(@$"rz-spinner-down", component.Markup);
|
||||
Assert.Contains(@$"rz-numeric-button-icon", component.Markup);
|
||||
Assert.Contains(@$"rz-numeric-up", component.Markup);
|
||||
Assert.Contains(@$"rz-numeric-down", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -328,9 +401,9 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.DoesNotContain(@$"rz-spinner-button-icon", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-spinner-up", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-spinner-down", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-numeric-button-icon", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-numeric-up", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-numeric-down", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -350,5 +423,219 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest.ToString(format)}\"", component.Markup);
|
||||
}
|
||||
|
||||
public static TheoryData<decimal, decimal> NumericFormatterPreservesLeadingZerosData =>
|
||||
new()
|
||||
{
|
||||
{ 10.000m, 100.000m },
|
||||
{ 100.000m, 10.000m }
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(NumericFormatterPreservesLeadingZerosData))]
|
||||
public void Numeric_Formatter_PreservesLeadingZeros(decimal oldValue, decimal newValue)
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
string format = "0.000";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<decimal>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<decimal>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<decimal>.Value), oldValue)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{oldValue.ToString(format)}\"", component.Markup);
|
||||
|
||||
component.Find("input").Change(newValue);
|
||||
|
||||
Assert.Contains($" value=\"{newValue.ToString(format)}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Uses_ConvertValue()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var value = new Dollars(11m);
|
||||
Dollars? ConvertFunc(string s) => decimal.TryParse(s, System.Globalization.CultureInfo.InvariantCulture, out var val) ? new Dollars(val) : null;
|
||||
var component = ctx.RenderComponent<RadzenNumeric<Dollars?>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars?>.ConvertValue), (Func<string, Dollars?>)ConvertFunc),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars?>.Value), value),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{value.ToString()}\"", component.Markup);
|
||||
|
||||
var newValue = new Dollars(13.53m);
|
||||
component.Find("input").Change("13.53");
|
||||
|
||||
Assert.Contains($" value=\"{newValue.ToString()}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_TypeConverter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = new Dollars(100.234m);
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<Dollars>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Value), valueToTest)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest.ToString(format, System.Globalization.CultureInfo.CurrentCulture)}\"", component.Markup);
|
||||
}
|
||||
[Fact]
|
||||
public void Numeric_Supports_TypeConverterWithCulture()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = new Dollars(100.234m);
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<Dollars>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Value), valueToTest),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest.ToString(format, System.Globalization.CultureInfo.InvariantCulture)}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_EmptyString()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = "";
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<string>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"0.00\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_ValueString()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = "12.50";
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<string>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_ValueStringEsCLCulture()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = "12,50";
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<string>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.GetCultureInfo("es-CL"))
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_ValueStringEnUSCulture()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = "12.50";
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<string>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.GetCultureInfo("en-US"))
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_IComparable()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<Dollars>>();
|
||||
|
||||
var maxValue = 2;
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, new Dollars(1m));
|
||||
parameters.Add(p => p.Max, maxValue);
|
||||
});
|
||||
});
|
||||
|
||||
component.Find("input").Change(13.53);
|
||||
|
||||
var maxDollars = new Dollars(maxValue);
|
||||
Assert.Contains($" value=\"{maxDollars}\"", component.Markup);
|
||||
Assert.Equal(component.Instance.Value, maxDollars);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_IFormattable()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = new Temperature(60.23m);
|
||||
const string format = "F";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<Temperature>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Temperature>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Temperature>.Value), valueToTest)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
var input = component.Find("input").GetAttribute("value");
|
||||
input.MarkupMatches(valueToTest.ToString(format));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,14 +23,14 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains(@$"rz-paginator", component.Markup);
|
||||
Assert.Contains(@$"rz-pager", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add<int>(p => p.PageSize, 101);
|
||||
parameters.Add<int>(p => p.Count, 100);
|
||||
});
|
||||
Assert.DoesNotContain(@$"rz-paginator", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-pager", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -49,7 +49,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains(@$"rz-paginator", component.Markup);
|
||||
Assert.Contains(@$"rz-pager", component.Markup);
|
||||
Assert.Contains(@$"rz-dropdown-trigger", component.Markup);
|
||||
}
|
||||
|
||||
@@ -67,14 +67,93 @@ namespace Radzen.Blazor.Tests
|
||||
await component.Instance.GoToPage(2);
|
||||
component.Render();
|
||||
|
||||
Assert.Contains(@$"rz-paginator-summary", component.Markup);
|
||||
Assert.Contains(@$"rz-pager-summary", component.Markup);
|
||||
Assert.Contains(@$"Page 3 of 10 (100 items)", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
parameters.Add<bool>(p => p.ShowPagingSummary, false);
|
||||
});
|
||||
Assert.DoesNotContain(@$"rz-paginator-summary", component.Markup);
|
||||
Assert.DoesNotContain(@$"rz-pager-summary", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenPager_Renders_PagerDensityDefault()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPager>(parameters =>
|
||||
{
|
||||
parameters.Add<int>(p => p.PageSize, 20);
|
||||
parameters.Add<int>(p => p.Count, 100);
|
||||
parameters.Add<Density>(p => p.Density, Density.Default);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain(@$"rz-density-compact", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenPager_Renders_PagerDensityCompact()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPager>(parameters =>
|
||||
{
|
||||
parameters.Add<int>(p => p.PageSize, 20);
|
||||
parameters.Add<int>(p => p.Count, 100);
|
||||
parameters.Add<Density>(p => p.Density, Density.Compact);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"rz-density-compact", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RadzenPager_First_And_Prev_Buttons_Are_Disabled_When_On_The_First_Page()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPager>(parameters => {
|
||||
parameters.Add<int>(p => p.PageSize, 10);
|
||||
parameters.Add<int>(p => p.Count, 100);
|
||||
parameters.Add<bool>(p => p.ShowPagingSummary, true);
|
||||
});
|
||||
|
||||
await component.Instance.GoToPage(0);
|
||||
component.Render();
|
||||
|
||||
var firstPageButton = component.Find("a.rz-pager-first");
|
||||
Assert.True(firstPageButton.HasAttribute("disabled"));
|
||||
|
||||
var prevPageButton = component.Find("a.rz-pager-prev");
|
||||
Assert.True(prevPageButton.HasAttribute("disabled"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RadzenPager_Last_And_Next_Buttons_Are_Disabled_When_On_The_Last_Page()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPager>(parameters => {
|
||||
parameters.Add<int>(p => p.PageSize, 10);
|
||||
parameters.Add<int>(p => p.Count, 100);
|
||||
parameters.Add<bool>(p => p.ShowPagingSummary, true);
|
||||
});
|
||||
|
||||
await component.Instance.GoToPage(9);
|
||||
component.Render();
|
||||
|
||||
var lastPageButton = component.Find("a.rz-pager-last");
|
||||
Assert.True(lastPageButton.HasAttribute("disabled"));
|
||||
|
||||
var nextPageButton = component.Find("a.rz-pager-next");
|
||||
Assert.True(nextPageButton.HasAttribute("disabled"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, value));
|
||||
|
||||
Assert.Contains(@$"<i class=""rzi"">{value}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rzi"">{value}</i>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -119,11 +119,11 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AllowCollapse, true));
|
||||
|
||||
Assert.Contains(@"<span class=""rzi rzi-minus""></span>", component.Markup);
|
||||
Assert.Contains(@"<span class=""notranslate rzi rzi-minus""></span>", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.Collapsed, true));
|
||||
|
||||
Assert.Contains(@"<span class=""rzi rzi-plus""></span>", component.Markup);
|
||||
Assert.Contains(@"<span class=""notranslate rzi rzi-plus""></span>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -119,13 +119,45 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPassword>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""new-password""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""on""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("autocomplete", "custom"));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""custom""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_Renders_TypedAutoCompleteParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPassword>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""new-password""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.CurrentPassword));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.CurrentPassword.GetAutoCompleteValue()}""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.NewPassword));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.NewPassword.GetAutoCompleteValue()}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Style, value));
|
||||
|
||||
Assert.Contains(@$"style=""{value}""", component.Markup);
|
||||
Assert.Contains(@$"style=""--rz-progressbar-value: 0%;{value}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -118,7 +118,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<double>(p => p.Max, max);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"style=""width: {Math.Min(value / max * 100, 100).ToInvariantString()}%;""", component.Markup);
|
||||
Assert.Contains(@$"style=""--rz-progressbar-value: {Math.Min(value / max * 100, 100).ToInvariantString()}%;""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -129,18 +129,18 @@ namespace Radzen.Blazor.Tests
|
||||
var component = ctx.RenderComponent<RadzenProgressBar>();
|
||||
|
||||
component.SetParametersAndRender(parameters=>parameters.Add(p=>p.ProgressBarStyle, ProgressBarStyle.Success));
|
||||
Assert.Contains(@$"rz-progressbar-determinate-success", component.Markup);
|
||||
Assert.Contains(@$"rz-progressbar-success", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ProgressBarStyle, ProgressBarStyle.Info));
|
||||
Assert.Contains(@$"rz-progressbar-determinate-info", component.Markup);
|
||||
Assert.Contains(@$"rz-progressbar-info", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ProgressBarStyle, ProgressBarStyle.Success));
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Mode, ProgressBarMode.Indeterminate));
|
||||
Assert.Contains(@$"rz-progressbar-indeterminate-success", component.Markup);
|
||||
Assert.Contains(@$"rz-progressbar-success", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ProgressBarStyle, ProgressBarStyle.Info));
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Mode, ProgressBarMode.Indeterminate));
|
||||
Assert.Contains(@$"rz-progressbar-indeterminate-info", component.Markup);
|
||||
Assert.Contains(@$"rz-progressbar-info", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using AngleSharp.Css;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7</TargetFramework>
|
||||
<TargetFramework>net9</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||
<PackageReference Include="bunit.web" Version="1.2.49" />
|
||||
<PackageReference Include="bunit.web" Version="1.36.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Radzen.Blazor.Tests
|
||||
component.SetParametersAndRender(parameters => parameters.Add<int>(p => p.Value, value));
|
||||
|
||||
Assert.Contains(@$"style=""width: {Math.Round((value / max * 100)).ToInvariantString()}%;""", component.Markup);
|
||||
Assert.Contains(@$"style=""left: {Math.Round((value / max * 100)).ToInvariantString()}%;""", component.Markup);
|
||||
Assert.Contains(@$"style=""inset-inline-start: {Math.Round((value / max * 100)).ToInvariantString()}%;""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -55,9 +55,9 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<IEnumerable<int>>(p => p.Value, new int[] { 4, 30 });
|
||||
});
|
||||
|
||||
Assert.Contains(@$"left: 4%", component.Markup);
|
||||
Assert.Contains(@$"left: 30%", component.Markup);
|
||||
Assert.Contains(@$"left: 4%; width: 26%;", component.Markup);
|
||||
Assert.Contains(@$"inset-inline-start: 4%", component.Markup);
|
||||
Assert.Contains(@$"inset-inline-start: 30%", component.Markup);
|
||||
Assert.Contains(@$"inset-inline-start: 4%; width: 26%;", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
@@ -44,7 +45,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -59,10 +60,10 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
parameters.Add(p => p.Text, text);
|
||||
parameters.Add(p => p.Icon, icon);
|
||||
parameters.Add(p => p.Icon, icon);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Image, image));
|
||||
|
||||
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" />", component.Markup);
|
||||
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""image"" />", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -93,12 +94,29 @@ namespace Radzen.Blazor.Tests
|
||||
component.SetParametersAndRender(parameters => {
|
||||
parameters.Add(p => p.Text, text);
|
||||
parameters.Add(p => p.Image, image);
|
||||
parameters.Add(p => p.ImageAlternateText, text);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" />", component.Markup);
|
||||
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""{text}"" />", component.Markup);
|
||||
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SplitButton_Renders_ButtonContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
RenderFragment buttonContent = (builder) => builder.AddMarkupContent(0, "<strong>Custom button content</strong>");
|
||||
|
||||
var text = "Test";
|
||||
var component = ctx.RenderComponent<RadzenSplitButton>(parameters => parameters
|
||||
.Add(p => p.ButtonContent, buttonContent)
|
||||
.Add(p => p.Text, text));
|
||||
|
||||
Assert.Contains(@$"<strong>Custom button content</strong>", component.Markup);
|
||||
Assert.DoesNotContain(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SplitButton_Renders_DisabledParameter()
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Style, value));
|
||||
|
||||
Assert.Contains(@$"style=""outline: 0 none;{value}""", component.Markup);
|
||||
Assert.Contains(@$"style=""{value}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -134,5 +134,18 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.True(raised);
|
||||
Assert.True(object.Equals(value, !(bool)newValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Switch_Renders_ReadOnlyParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSwitch>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.ReadOnly, true));
|
||||
|
||||
Assert.Contains(@$"rz-readonly", component.Markup);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
35
Radzen.Blazor.Tests/Temperature.cs
Normal file
35
Radzen.Blazor.Tests/Temperature.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Radzen.Blazor.Tests;
|
||||
|
||||
public record struct Temperature(decimal DegreesCelsius)
|
||||
: IFormattable
|
||||
{
|
||||
public decimal Celsius => DegreesCelsius;
|
||||
public decimal Fahrenheit => DegreesCelsius * 9 / 5 + 32;
|
||||
public decimal Kelvin => DegreesCelsius + 273.15m;
|
||||
|
||||
public override string ToString() => ToString("G");
|
||||
public string ToString(string format) => ToString(format, CultureInfo.CurrentCulture);
|
||||
|
||||
public string ToString(string format, IFormatProvider provider)
|
||||
{
|
||||
provider ??= CultureInfo.CurrentCulture;
|
||||
if (string.IsNullOrEmpty(format))
|
||||
format = "G";
|
||||
|
||||
switch (format.ToUpperInvariant())
|
||||
{
|
||||
case "G":
|
||||
case "C":
|
||||
return $"{Celsius.ToString("F2", provider)} °C";
|
||||
case "F":
|
||||
return $"{Fahrenheit.ToString("F2", provider)} °F";
|
||||
case "K":
|
||||
return $"{Kelvin.ToString("F2", provider)} K";
|
||||
default:
|
||||
throw new FormatException($"The {format} format string is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,13 +119,54 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenTextBox>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""on""", component.Markup);
|
||||
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("autocomplete", "custom"));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""custom""", component.Markup);
|
||||
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
|
||||
|
||||
component.Instance.DefaultAutoCompleteAttribute = "autocomplete-custom";
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""autocomplete-custom""", component.Markup);
|
||||
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TextBox_Renders_TypedAutoCompleteParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenTextBox>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""on""", component.Markup);
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.AdditionalName));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.AdditionalName.GetAutoCompleteValue()}""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.FamilyName));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.FamilyName.GetAutoCompleteValue()}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
138
Radzen.Blazor/AutoCompleteType.cs
Normal file
138
Radzen.Blazor/AutoCompleteType.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// The <c>AutomCompleteType</c> is a string-associated enum of
|
||||
/// browser-supported autocomplete attribute values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class lists the autocomplete attirbute options allowing
|
||||
/// developers to provide the browser with guidance on how to pre-populate
|
||||
/// the form fields. It is a class rather than a simpler enum to associate
|
||||
/// each option with the string the browser expects. For more information
|
||||
/// please review the list of options (https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete)
|
||||
/// and the spec (https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill).
|
||||
/// </remarks>
|
||||
public enum AutoCompleteType
|
||||
{
|
||||
/// <summary>Autocomplete is disabled. </summary>
|
||||
Off,
|
||||
/// <summary>Autocomplete is enabled. The browser chooses what values to suggest. </summary>
|
||||
On,
|
||||
/// <summary>The field expects the value to be a person's full name.</summary>
|
||||
Name,
|
||||
/// <summary>The prefix or title, such as "Mrs.", "Mr.", "Miss", "Ms.", "Dr." etc.</summary>
|
||||
HonorificPrefix,
|
||||
/// <summary>The given (or "first") name.</summary>
|
||||
GivenName,
|
||||
/// <summary>The middle name.</summary>
|
||||
AdditionalName,
|
||||
/// <summary>The family (or "last") name.</summary>
|
||||
FamilyName,
|
||||
/// <summary>The suffix, such as "Jr.", "B.Sc.", "PhD.", "MBASW", etc.</summary>
|
||||
HonorificSuffix,
|
||||
/// <summary>The nickname or handle.</summary>
|
||||
Nickname,
|
||||
/// <summary>The email address.</summary>
|
||||
Email,
|
||||
/// <summary>The username or account name.</summary>
|
||||
Username,
|
||||
/// <summary>A new password. When creating a new account or changing passwords.</summary>
|
||||
NewPassword,
|
||||
/// <summary>A current password. When filling in an existing password.</summary>
|
||||
CurrentPassword,
|
||||
/// <summary>A one-time code used for verifying user identity.</summary>
|
||||
OneTimeCode,
|
||||
/// <summary>A job title, or the title a person has within an organization, such as "Senior Technical Writer", "President", or "Assistant Troop Leader".</summary>
|
||||
OrganizationTitle,
|
||||
/// <summary>A company, business, or organization name.</summary>
|
||||
Organization,
|
||||
/// <summary>A street address. Use multiple address lines when more space is needed.</summary>
|
||||
StreetAddress,
|
||||
/// <summary>The line 1 of a street address. For example, "1234 Main Street".</summary>
|
||||
AddressLine1,
|
||||
/// <summary>The line 2 of a street address. For example, "Apartment 123".</summary>
|
||||
AddressLine2,
|
||||
/// <summary>The line 3 of a street address. For example, "c/o Jane Doe".</summary>
|
||||
AddressLine3,
|
||||
/// <summary>The city or locality.</summary>
|
||||
AddressLevel1,
|
||||
/// <summary>The state, province, prefecture, or region.</summary>
|
||||
AddressLevel2,
|
||||
/// <summary>The zip code or postal code.</summary>
|
||||
AddressLevel3,
|
||||
/// <summary>The country name.</summary>
|
||||
AddressLevel4,
|
||||
/// <summary>The country code.</summary>
|
||||
Country,
|
||||
/// <summary>The country name.</summary>
|
||||
CountryName,
|
||||
/// <summary>The postal code.</summary>
|
||||
PostalCode,
|
||||
/// <summary>The full name as printed on or associated with a payment instrument such as a credit card.</summary>
|
||||
CcName,
|
||||
/// <summary>The given (or "first") name as printed on or associated with a payment instrument such as a credit card.</summary>
|
||||
CcGivenName,
|
||||
/// <summary>The middle name as printed on or associated with a payment instrument such as a credit card.</summary>
|
||||
CcAdditionalName,
|
||||
/// <summary>The family (or "last") name as printed on or associated with a payment instrument such as a credit card.</summary>
|
||||
CcFamilyName,
|
||||
/// <summary>A credit card number or other number identifying a payment method, such as an account number.</summary>
|
||||
CcNumber,
|
||||
/// <summary>A payment method expiration date, typically in the form "MM/YY" or "MM/YYYY".</summary>
|
||||
CcExp,
|
||||
/// <summary>A payment method expiration month, typically in numeric form (MM).</summary>
|
||||
CcExpMonth,
|
||||
/// <summary>A payment method expiration year, typically in numeric form (YYYY).</summary>
|
||||
CcExpYear,
|
||||
/// <summary>The security code for your payment method, such as the CVV code.</summary>
|
||||
CcCsc,
|
||||
/// <summary>The type of payment instrument, such as "Visa", "Master Card", "Checking", or "Savings".</summary>
|
||||
CcType,
|
||||
/// <summary>The currency in which the transaction was completed. Use the ISO 4217 currency codes, such as "USD" for the US dollar.</summary>
|
||||
TransactionCurrency,
|
||||
/// <summary>The amount, in the currency specified by the transaction currency attribute, of the transaction completed.</summary>
|
||||
TransactionAmount,
|
||||
/// <summary>The language in which the transaction was completed. Use the relevant BCP 47 language tag.</summary>
|
||||
Language,
|
||||
/// <summary>A birth date, as a full date.</summary>
|
||||
Bday,
|
||||
/// <summary>The day of the month of a birth date.</summary>
|
||||
BdayDay,
|
||||
/// <summary>The month of the year of a birth date.</summary>
|
||||
BdayMonth,
|
||||
/// <summary>The year of a birth date.</summary>
|
||||
BdayYear,
|
||||
/// <summary>A gender identity (such as "Female", "Fa'afafine", "Hijra", "Male", "Nonbinary"), as freeform text without newlines.</summary>
|
||||
Sex,
|
||||
/// <summary>A full telephone number, including the country code. </summary>
|
||||
Tel,
|
||||
/// <summary>A country code, such as "1" for the United States, Canada, and other areas in North America and parts of the Caribbean.</summary>
|
||||
TelCountryCode,
|
||||
/// <summary>The entire phone number without the country code component, including a country-internal prefix.</summary>
|
||||
TelNational,
|
||||
/// <summary>The area code, with any country-internal prefix applied if appropriate.</summary>
|
||||
TelAreaCode,
|
||||
/// <summary>The phone number without the country or area code.</summary>
|
||||
TelLocal,
|
||||
/// <summary>The extension number, if applicable.</summary>
|
||||
TelExtension,
|
||||
/// <summary>A URL for an instant messaging protocol endpoint, such as "xmpp:username@example.net".</summary>
|
||||
Impp,
|
||||
/// <summary>A URL, such as a home page or company website address as appropriate given the context of the other fields in the form.</summary>
|
||||
Url,
|
||||
/// <summary>The URL of an image representing the person, company, or contact information given in the other fields in the form.</summary>
|
||||
Photo,
|
||||
/// <summary>State.</summary>
|
||||
State,
|
||||
/// <summary>Province.</summary>
|
||||
Province,
|
||||
/// <summary>Zip code.</summary>
|
||||
ZipCode,
|
||||
/// <summary>Firs name.</summary>
|
||||
FirstName,
|
||||
/// <summary>Middle name.</summary>
|
||||
MiddleName,
|
||||
/// <summary>Last name.</summary>
|
||||
LastName,
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,17 @@ namespace Radzen.Blazor
|
||||
/// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool Visible { get; set; } = true;
|
||||
/// <summary>
|
||||
/// Specifies the label rotation angle in degrees. Set to <c>null</c> by default which means no rotation is applied. Has higher precedence than <see cref="LabelAutoRotation"/>.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public double? LabelRotation { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the automatic label rotation angle in degrees. If set RadzenChart will automatically rotate the labels to fit the available space by the specified value. Has lower precedence than <see cref="LabelRotation"/>.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public double? LabelAutoRotation { get; set; } = null;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool ShouldRefreshChart(ParameterView parameters)
|
||||
@@ -108,6 +119,8 @@ namespace Radzen.Blazor
|
||||
return DidParameterChange(parameters, nameof(Min), Min) ||
|
||||
DidParameterChange(parameters, nameof(Max), Max) ||
|
||||
DidParameterChange(parameters, nameof(Visible), Visible) ||
|
||||
DidParameterChange(parameters, nameof(LabelRotation), LabelRotation) ||
|
||||
DidParameterChange(parameters, nameof(LabelAutoRotation), LabelAutoRotation) ||
|
||||
DidParameterChange(parameters, nameof(Step), Step);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections;
|
||||
using System.Net.Mime;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
@@ -16,22 +15,34 @@ namespace Radzen.Blazor
|
||||
/// <typeparam name="TItem">The type of the series data.</typeparam>
|
||||
public abstract class CartesianSeries<TItem> : RadzenChartComponentBase, IChartSeries, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Cache for the value returned by <see cref="Category"/> when that value is only dependent on
|
||||
/// <see cref="CategoryProperty"/>.
|
||||
/// </summary>
|
||||
Func<TItem, double> categoryPropertyCache;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a getter function that returns a value from the specified category scale for the specified data item.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale.</param>
|
||||
internal Func<TItem, double> Category(ScaleBase scale)
|
||||
{
|
||||
if (categoryPropertyCache != null)
|
||||
{
|
||||
return categoryPropertyCache;
|
||||
}
|
||||
|
||||
if (IsNumeric(CategoryProperty))
|
||||
{
|
||||
return PropertyAccess.Getter<TItem, double>(CategoryProperty);
|
||||
categoryPropertyCache = PropertyAccess.Getter<TItem, double>(CategoryProperty);
|
||||
return categoryPropertyCache;
|
||||
}
|
||||
|
||||
if (IsDate(CategoryProperty))
|
||||
{
|
||||
var category = PropertyAccess.Getter<TItem, DateTime>(CategoryProperty);
|
||||
|
||||
return (item) => category(item).Ticks;
|
||||
categoryPropertyCache = (item) => category(item).Ticks;
|
||||
return categoryPropertyCache;
|
||||
}
|
||||
|
||||
if (scale is OrdinalScale ordinal)
|
||||
@@ -80,6 +91,12 @@ namespace Radzen.Blazor
|
||||
throw new ArgumentException($"Property {propertyName} does not exist");
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
if(PropertyAccess.IsDateOnly(property))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return PropertyAccess.IsDate(property);
|
||||
}
|
||||
|
||||
@@ -351,6 +368,7 @@ namespace Radzen.Blazor
|
||||
var shouldRefresh = parameters.DidParameterChange(nameof(Data), Data);
|
||||
var visibleChanged = parameters.DidParameterChange(nameof(Visible), Visible);
|
||||
var hiddenChanged = parameters.DidParameterChange(nameof(Hidden), Hidden);
|
||||
var categoryChanged = parameters.DidParameterChange(nameof(CategoryProperty), CategoryProperty);
|
||||
|
||||
await base.SetParametersAsync(parameters);
|
||||
|
||||
@@ -366,6 +384,11 @@ namespace Radzen.Blazor
|
||||
shouldRefresh = true;
|
||||
}
|
||||
|
||||
if (categoryChanged || shouldRefresh)
|
||||
{
|
||||
categoryPropertyCache = null;
|
||||
}
|
||||
|
||||
if (Data != null && Data.Count() != Items.Count)
|
||||
{
|
||||
shouldRefresh = true;
|
||||
@@ -454,28 +477,74 @@ namespace Radzen.Blazor
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual RenderFragment RenderTooltip(object data, double marginLeft, double marginTop)
|
||||
public virtual RenderFragment RenderTooltip(object data)
|
||||
{
|
||||
var item = (TItem)data;
|
||||
|
||||
return builder =>
|
||||
{
|
||||
if (Chart.Tooltip.Shared)
|
||||
{
|
||||
var category = PropertyAccess.GetValue(item, CategoryProperty);
|
||||
builder.OpenComponent<ChartSharedTooltip>(0);
|
||||
builder.AddAttribute(1, nameof(ChartSharedTooltip.Class), TooltipClass(item));
|
||||
builder.AddAttribute(2, nameof(ChartSharedTooltip.Title), TooltipTitle(item));
|
||||
builder.AddAttribute(3, nameof(ChartSharedTooltip.ChildContent), RenderSharedTooltipItems(category));
|
||||
builder.CloseComponent();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.OpenComponent<ChartTooltip>(0);
|
||||
builder.AddAttribute(1, nameof(ChartTooltip.ChildContent), TooltipTemplate?.Invoke(item));
|
||||
builder.AddAttribute(2, nameof(ChartTooltip.Title), TooltipTitle(item));
|
||||
builder.AddAttribute(3, nameof(ChartTooltip.Label), TooltipLabel(item));
|
||||
builder.AddAttribute(4, nameof(ChartTooltip.Value), TooltipValue(item));
|
||||
builder.AddAttribute(5, nameof(ChartTooltip.Class), TooltipClass(item));
|
||||
builder.AddAttribute(6, nameof(ChartTooltip.Style), TooltipStyle(item));
|
||||
builder.CloseComponent();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private RenderFragment RenderSharedTooltipItems(object category)
|
||||
{
|
||||
return builder =>
|
||||
{
|
||||
var visibleSeries = Chart.Series.Where(s => s.Visible).ToList();
|
||||
|
||||
foreach (var series in visibleSeries)
|
||||
{
|
||||
builder.AddContent(1, series.RenderSharedTooltipItem(category));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual RenderFragment RenderSharedTooltipItem(object category)
|
||||
{
|
||||
return builder =>
|
||||
{
|
||||
var item = Items.FirstOrDefault(i => object.Equals(PropertyAccess.GetValue(i, CategoryProperty), category));
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
builder.OpenComponent<ChartSharedTooltipItem>(0);
|
||||
builder.AddAttribute(1, nameof(ChartSharedTooltipItem.Value), TooltipValue(item));
|
||||
builder.AddAttribute(2, nameof(ChartSharedTooltipItem.ChildContent), TooltipTemplate?.Invoke(item));
|
||||
builder.AddAttribute(3, nameof(ChartSharedTooltipItem.LegendItem), RenderLegendItem(false));
|
||||
builder.CloseComponent();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Point GetTooltipPosition(object data)
|
||||
{
|
||||
var item = (TItem)data;
|
||||
var x = TooltipX(item);
|
||||
var y = TooltipY(item);
|
||||
|
||||
return builder =>
|
||||
{
|
||||
builder.OpenComponent<ChartTooltip>(0);
|
||||
builder.AddAttribute(1, nameof(ChartTooltip.X), x + marginLeft);
|
||||
builder.AddAttribute(2, nameof(ChartTooltip.Y), y + marginTop);
|
||||
|
||||
builder.AddAttribute(3, nameof(ChartTooltip.ChildContent), TooltipTemplate == null ? null : TooltipTemplate(item));
|
||||
|
||||
builder.AddAttribute(4, nameof(ChartTooltip.Title), TooltipTitle(item));
|
||||
builder.AddAttribute(5, nameof(ChartTooltip.Label), TooltipLabel(item));
|
||||
builder.AddAttribute(6, nameof(ChartTooltip.Value), TooltipValue(item));
|
||||
builder.AddAttribute(7, nameof(ChartTooltip.Class), TooltipClass(item));
|
||||
builder.AddAttribute(8, nameof(ChartTooltip.Style), TooltipStyle(item));
|
||||
builder.CloseComponent();
|
||||
};
|
||||
return new Point { X = x, Y = y };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -498,6 +567,14 @@ namespace Radzen.Blazor
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual RenderFragment RenderLegendItem()
|
||||
{
|
||||
return RenderLegendItem(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the legend item for this series.
|
||||
/// </summary>
|
||||
protected virtual RenderFragment RenderLegendItem(bool clickable)
|
||||
{
|
||||
var style = new List<string>();
|
||||
|
||||
@@ -516,6 +593,7 @@ namespace Radzen.Blazor
|
||||
builder.AddAttribute(5, nameof(LegendItem.MarkerSize), MarkerSize);
|
||||
builder.AddAttribute(6, nameof(LegendItem.Text), GetTitle());
|
||||
builder.AddAttribute(7, nameof(LegendItem.Click), EventCallback.Factory.Create(this, OnLegendItemClick));
|
||||
builder.AddAttribute(8, nameof(LegendItem.Clickable), clickable);
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
@@ -545,13 +623,13 @@ namespace Radzen.Blazor
|
||||
/// <inheritdoc />
|
||||
public double GetMean()
|
||||
{
|
||||
return Data.Select(e => Value(e)).Average();
|
||||
return Data.Select(e => Value(e)).DefaultIfEmpty(double.NaN).Average();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMode()
|
||||
{
|
||||
return Data.GroupBy(e => Value(e)).Select(g => new { Value = g.Key, Count = g.Count() }).OrderByDescending(e => e.Count).FirstOrDefault().Value;
|
||||
return Data.Any() ? Data.GroupBy(e => Value(e)).Select(g => new { Value = g.Key, Count = g.Count() }).OrderByDescending(e => e.Count).FirstOrDefault().Value : double.NaN;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -559,35 +637,38 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
public (double a, double b) GetTrend()
|
||||
{
|
||||
double a, b;
|
||||
double a = double.NaN, b = double.NaN;
|
||||
|
||||
Func<TItem, double> X;
|
||||
Func<TItem, double> Y;
|
||||
if (Chart.ShouldInvertAxes())
|
||||
if (Data.Any())
|
||||
{
|
||||
X = e => Chart.CategoryScale.Scale(Value(e));
|
||||
Y = e => Chart.ValueScale.Scale(Category(Chart.ValueScale)(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
X = e => Chart.CategoryScale.Scale(Category(Chart.CategoryScale)(e));
|
||||
Y = e => Chart.ValueScale.Scale(Value(e));
|
||||
}
|
||||
Func<TItem, double> X;
|
||||
Func<TItem, double> Y;
|
||||
if (Chart.ShouldInvertAxes())
|
||||
{
|
||||
X = e => Chart.CategoryScale.Scale(Value(e));
|
||||
Y = e => Chart.ValueScale.Scale(Category(Chart.ValueScale)(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
X = e => Chart.CategoryScale.Scale(Category(Chart.CategoryScale)(e));
|
||||
Y = e => Chart.ValueScale.Scale(Value(e));
|
||||
}
|
||||
|
||||
var avgX = Data.Select(e => X(e)).Average();
|
||||
var avgY = Data.Select(e => Y(e)).Average();
|
||||
var sumXY = Data.Sum(e => (X(e) - avgX) * (Y(e) - avgY));
|
||||
if (Chart.ShouldInvertAxes())
|
||||
{
|
||||
var sumYSq = Data.Sum(e => (Y(e) - avgY) * (Y(e) - avgY));
|
||||
b = sumXY / sumYSq;
|
||||
a = avgX - b * avgY;
|
||||
}
|
||||
else
|
||||
{
|
||||
var sumXSq = Data.Sum(e => (X(e) - avgX) * (X(e) - avgX));
|
||||
b = sumXY / sumXSq;
|
||||
a = avgY - b * avgX;
|
||||
var avgX = Data.Select(e => X(e)).Average();
|
||||
var avgY = Data.Select(e => Y(e)).Average();
|
||||
var sumXY = Data.Sum(e => (X(e) - avgX) * (Y(e) - avgY));
|
||||
if (Chart.ShouldInvertAxes())
|
||||
{
|
||||
var sumYSq = Data.Sum(e => (Y(e) - avgY) * (Y(e) - avgY));
|
||||
b = sumXY / sumYSq;
|
||||
a = avgX - b * avgY;
|
||||
}
|
||||
else
|
||||
{
|
||||
var sumXSq = Data.Sum(e => (X(e) - avgX) * (X(e) - avgX));
|
||||
b = sumXY / sumXSq;
|
||||
a = avgY - b * avgX;
|
||||
}
|
||||
}
|
||||
|
||||
return (a, b);
|
||||
@@ -669,29 +750,32 @@ namespace Radzen.Blazor
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual object DataAt(double x, double y)
|
||||
public virtual (object, Point) DataAt(double x, double y)
|
||||
{
|
||||
if (Items.Any())
|
||||
{
|
||||
return Items.Select(item =>
|
||||
var retObject = Items.Select(item =>
|
||||
{
|
||||
var distance = Math.Abs(TooltipX(item) - x);
|
||||
return new { Item = item, Distance = distance };
|
||||
}).Aggregate((a, b) => a.Distance < b.Distance ? a : b).Item;
|
||||
|
||||
return (retObject,
|
||||
new Point() { X = TooltipX(retObject), Y = TooltipY(retObject)});
|
||||
}
|
||||
|
||||
return null;
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<ChartDataLabel> GetDataLabels(double offsetX, double offsetY)
|
||||
{
|
||||
var list = new List<ChartDataLabel>();
|
||||
|
||||
|
||||
foreach (var d in Data)
|
||||
{
|
||||
list.Add(new ChartDataLabel
|
||||
{
|
||||
list.Add(new ChartDataLabel
|
||||
{
|
||||
Position = new Point { X = TooltipX(d) + offsetX, Y = TooltipY(d) + offsetY },
|
||||
TextAnchor = "middle",
|
||||
Text = Chart.ValueAxis.Format(Chart.ValueScale, Value(d))
|
||||
@@ -707,16 +791,25 @@ namespace Radzen.Blazor
|
||||
/// <param name="index">The index.</param>
|
||||
/// <param name="colors">The colors.</param>
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
protected string PickColor(int index, IEnumerable<string> colors, string defaultValue = null)
|
||||
/// <param name="colorRange">The color range value.</param>
|
||||
/// <param name="value">The value of the item.</param>
|
||||
protected string PickColor(int index, IEnumerable<string> colors, string defaultValue = null, IList<SeriesColorRange> colorRange = null, double value = 0.0)
|
||||
{
|
||||
if (colors == null || !colors.Any())
|
||||
if (colorRange != null)
|
||||
{
|
||||
return defaultValue;
|
||||
var result = colorRange.Where(r => r.Min <= value && r.Max >= value).FirstOrDefault<SeriesColorRange>();
|
||||
return result != null ? result.Color : defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (colors == null || !colors.Any())
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return colors.ElementAt(index % colors.Count());
|
||||
return colors.ElementAt(index % colors.Count());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,11 +65,8 @@ namespace Radzen
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs"/> instance containing the event data.</param>
|
||||
private void UriHelper_OnLocationChanged(object sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
|
||||
{
|
||||
if (this.OnNavigate != null)
|
||||
{
|
||||
this.OnNavigate();
|
||||
}
|
||||
{
|
||||
this.OnNavigate?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -199,5 +196,30 @@ namespace Radzen
|
||||
/// </summary>
|
||||
/// <value>The value.</value>
|
||||
public object Value { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the icon.
|
||||
/// </summary>
|
||||
/// <value>The icon.</value>
|
||||
public string Icon { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the icon color.
|
||||
/// </summary>
|
||||
/// <value>The icon color.</value>
|
||||
public string IconColor { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the image.
|
||||
/// </summary>
|
||||
/// <value>The image.</value>
|
||||
public string Image { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the image style.
|
||||
/// </summary>
|
||||
/// <value>The image style.</value>
|
||||
public string ImageStyle { get; set; }
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disabled.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is disabled; otherwise, <c>false</c>.</value>
|
||||
public bool Disabled { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
110
Radzen.Blazor/CookieThemeService.cs
Normal file
110
Radzen.Blazor/CookieThemeService.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Radzen
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for the <see cref="CookieThemeService" />.
|
||||
/// </summary>
|
||||
public class CookieThemeServiceOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the cookie name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = "Theme";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cookie duration.
|
||||
/// </summary>
|
||||
public TimeSpan Duration { get; set; } = TimeSpan.FromDays(365);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persist the current theme in a cookie. Requires <see cref="ThemeService" /> to be registered in the DI container.
|
||||
/// </summary>
|
||||
public class CookieThemeService
|
||||
{
|
||||
private readonly CookieThemeServiceOptions options;
|
||||
private readonly IJSRuntime jsRuntime;
|
||||
private readonly ThemeService themeService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CookieThemeService" /> class.
|
||||
/// </summary>
|
||||
public CookieThemeService(IJSRuntime jsRuntime, ThemeService themeService, IOptions<CookieThemeServiceOptions> options)
|
||||
{
|
||||
this.jsRuntime = jsRuntime;
|
||||
this.themeService = themeService;
|
||||
this.options = options.Value;
|
||||
|
||||
themeService.ThemeChanged += OnThemeChanged;
|
||||
|
||||
_ = InitializeAsync();
|
||||
}
|
||||
|
||||
private async Task InitializeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var cookies = await jsRuntime.InvokeAsync<string>("eval", "document.cookie");
|
||||
|
||||
var themeCookie = cookies?.Split("; ").Select(x =>
|
||||
{
|
||||
var parts = x.Split("=");
|
||||
|
||||
return (Key: parts[0], Value: parts[1]);
|
||||
})
|
||||
.FirstOrDefault(x => x.Key == options.Name);
|
||||
|
||||
var theme = themeCookie?.Value;
|
||||
|
||||
if (!string.IsNullOrEmpty(theme) && themeService.Theme != theme)
|
||||
{
|
||||
themeService.SetTheme(theme);
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void OnThemeChanged()
|
||||
{
|
||||
var expiration = DateTime.Now.Add(options.Duration);
|
||||
|
||||
_ = jsRuntime.InvokeVoidAsync("eval", $"document.cookie = \"{options.Name}={themeService.Theme}; expires={expiration:R}; path=/\"");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods to register the <see cref="CookieThemeService" />.
|
||||
/// </summary>
|
||||
public static class CookieThemeServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the <see cref="CookieThemeService" /> to the service collection.
|
||||
/// </summary>
|
||||
public static IServiceCollection AddRadzenCookieThemeService(this IServiceCollection services)
|
||||
{
|
||||
services.AddOptions<CookieThemeServiceOptions>();
|
||||
services.AddScoped<CookieThemeService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <see cref="CookieThemeService" /> to the service collection with the specified configuration.
|
||||
/// </summary>
|
||||
public static IServiceCollection AddRadzenCookieThemeService(this IServiceCollection services, Action<CookieThemeServiceOptions> configure)
|
||||
{
|
||||
services.Configure(configure);
|
||||
services.AddScoped<CookieThemeService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Radzen.Blazor;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Radzen
|
||||
{
|
||||
@@ -213,6 +212,32 @@ namespace Radzen
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the search text
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string SearchText
|
||||
{
|
||||
get
|
||||
{
|
||||
return searchText;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (searchText != value)
|
||||
{
|
||||
searchText = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the search text changed.
|
||||
/// </summary>
|
||||
/// <value>The search text changed.</value>
|
||||
[Parameter]
|
||||
public EventCallback<string> SearchTextChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The search text
|
||||
/// </summary>
|
||||
@@ -234,23 +259,7 @@ namespace Radzen
|
||||
{
|
||||
if (!string.IsNullOrEmpty(searchText))
|
||||
{
|
||||
var ignoreCase = FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive;
|
||||
|
||||
var query = new List<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(TextProperty))
|
||||
{
|
||||
query.Add(TextProperty);
|
||||
}
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
query.Add("ToLower()");
|
||||
}
|
||||
|
||||
query.Add($"{Enum.GetName(typeof(StringFilterOperator), FilterOperator)}(@0)");
|
||||
|
||||
_view = Query.Where(String.Join(".", query), ignoreCase ? searchText.ToLower() : searchText);
|
||||
_view = Query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -288,20 +297,35 @@ namespace Radzen
|
||||
/// <returns>A Task representing the asynchronous operation.</returns>
|
||||
public override async Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
var searchTextChanged = parameters.DidParameterChange(nameof(SearchText), SearchText);
|
||||
if (searchTextChanged)
|
||||
{
|
||||
searchText = parameters.GetValueOrDefault<string>(SearchText);
|
||||
}
|
||||
|
||||
var dataChanged = parameters.DidParameterChange(nameof(Data), Data);
|
||||
|
||||
if (dataChanged)
|
||||
{
|
||||
await OnDataChanged();
|
||||
}
|
||||
|
||||
var disabledChanged = parameters.DidParameterChange(nameof(Disabled), Disabled);
|
||||
|
||||
var result = base.SetParametersAsync(parameters);
|
||||
|
||||
if (EditContext != null && ValueExpression != null && FieldIdentifier.Model != EditContext.Model)
|
||||
{
|
||||
FieldIdentifier = FieldIdentifier.Create(ValueExpression);
|
||||
EditContext.OnValidationStateChanged -= ValidationStateChanged;
|
||||
EditContext.OnValidationStateChanged += ValidationStateChanged;
|
||||
}
|
||||
|
||||
if (disabledChanged)
|
||||
{
|
||||
FormFieldContext?.DisabledChanged(Disabled);
|
||||
}
|
||||
|
||||
await result;
|
||||
}
|
||||
|
||||
@@ -334,7 +358,7 @@ namespace Radzen
|
||||
/// Gets the value.
|
||||
/// </summary>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object GetValue()
|
||||
public virtual object GetValue()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
@@ -347,6 +371,35 @@ namespace Radzen
|
||||
/// <returns>ClassList.</returns>
|
||||
protected ClassList GetClassList(string className) => ClassList.Create(className)
|
||||
.AddDisabled(Disabled)
|
||||
.Add(FieldIdentifier, EditContext);
|
||||
.Add(FieldIdentifier, EditContext)
|
||||
.Add("rz-state-empty", !HasValue);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask FocusAsync()
|
||||
{
|
||||
await Element.FocusAsync();
|
||||
}
|
||||
|
||||
/// <summary> Provides support for RadzenFormField integration. </summary>
|
||||
[CascadingParameter]
|
||||
public IFormFieldContext FormFieldContext { get; set; }
|
||||
|
||||
/// <summary> Gets the current placeholder. Returns empty string if this component is inside a RadzenFormField.</summary>
|
||||
protected string CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="E:ContextMenu" /> event.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public override Task OnContextMenu(MouseEventArgs args)
|
||||
{
|
||||
if (!Disabled)
|
||||
{
|
||||
return base.OnContextMenu(args);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
using Microsoft.JSInterop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Radzen
|
||||
@@ -24,7 +26,7 @@ namespace Radzen
|
||||
/// <div class="row">
|
||||
/// <div class="col-md-12">
|
||||
/// <RadzenButton Text="Ok" Click="() => ds.Close(true)" Style="margin-bottom: 10px; width: 150px" />
|
||||
/// <RadzenButton Text="Cancel" Click="() => ds.Close(false)" ButtonStyle="ButtonStyle.Secondary" Style="margin-bottom: 10px; width: 150px"/>
|
||||
/// <RadzenButton Text="Cancel" Click="() => ds.Close(false)" ButtonStyle="ButtonStyle.Base" Style="margin-bottom: 10px; width: 150px"/>
|
||||
/// <RadzenButton Text="Refresh" Click="(() => { orderID = 10249; ds.Refresh(); })" ButtonStyle="ButtonStyle.Info" Style="margin-bottom: 10px; width: 150px"/>
|
||||
/// Order ID: @orderID
|
||||
/// </div>
|
||||
@@ -76,10 +78,15 @@ namespace Radzen
|
||||
|
||||
private void UriHelper_OnLocationChanged(object sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
|
||||
{
|
||||
if (dialogs.Count > 0)
|
||||
while (dialogs.Any())
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
if (_sideDialogTask?.Task.IsCompleted == false)
|
||||
{
|
||||
CloseSide();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -97,18 +104,47 @@ namespace Radzen
|
||||
/// </summary>
|
||||
public event Action<string, Type, Dictionary<string, object>, DialogOptions> OnOpen;
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Close event for the side dialog
|
||||
/// </summary>
|
||||
public event Action<dynamic> OnSideClose;
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Open event for the side dialog
|
||||
/// </summary>
|
||||
public event Action<Type, Dictionary<string, object>, SideDialogOptions> OnSideOpen;
|
||||
|
||||
/// <summary>
|
||||
/// Opens a dialog with the specified arguments.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the Blazor component which will be displayed in a dialog.</typeparam>
|
||||
/// <param name="title">The text displayed in the title bar of the dialog.</param>
|
||||
/// <param name="parameters">The dialog parameters. Passed as property values of <typeparamref name="T" />.</param>
|
||||
/// <param name="parameters">The dialog parameters.</param>
|
||||
/// <param name="options">The dialog options.</param>
|
||||
public void Open<T>(string title, Dictionary<string, object> parameters = null, DialogOptions options = null) where T : ComponentBase
|
||||
public virtual void Open<T>(string title, Dictionary<string, object> parameters = null, DialogOptions options = null) where T : ComponentBase
|
||||
{
|
||||
OpenDialog<T>(title, parameters, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a dialog with the specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="title">The text displayed in the title bar of the dialog.</param>
|
||||
/// <param name="componentType">The type of the component to be displayed in the dialog. Must inherit from <see cref="ComponentBase"/>.</param>
|
||||
/// <param name="parameters">The dialog parameters.</param>
|
||||
/// <param name="options">The dialog options.</param>
|
||||
public virtual void Open(string title, Type componentType, Dictionary<string, object> parameters = null, DialogOptions options = null)
|
||||
{
|
||||
if (!typeof(ComponentBase).IsAssignableFrom(componentType))
|
||||
{
|
||||
throw new ArgumentException("The component type must be a subclass of ComponentBase.", nameof(componentType));
|
||||
}
|
||||
|
||||
var method = GetType().GetMethod(nameof(OpenDialog), BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
method.MakeGenericMethod(componentType).Invoke(this, new object[] { title, parameters, options });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes <see cref="OnRefresh" />.
|
||||
/// </summary>
|
||||
@@ -121,6 +157,7 @@ namespace Radzen
|
||||
/// The tasks
|
||||
/// </summary>
|
||||
protected List<TaskCompletionSource<dynamic>> tasks = new List<TaskCompletionSource<dynamic>>();
|
||||
private TaskCompletionSource<dynamic> _sideDialogTask;
|
||||
|
||||
/// <summary>
|
||||
/// Opens a dialog with the specified arguments.
|
||||
@@ -130,7 +167,7 @@ namespace Radzen
|
||||
/// <param name="parameters">The dialog parameters. Passed as property values of <typeparamref name="T" />.</param>
|
||||
/// <param name="options">The dialog options.</param>
|
||||
/// <returns>The value passed as argument to <see cref="Close" />.</returns>
|
||||
public Task<dynamic> OpenAsync<T>(string title, Dictionary<string, object> parameters = null, DialogOptions options = null) where T : ComponentBase
|
||||
public virtual Task<dynamic> OpenAsync<T>(string title, Dictionary<string, object> parameters = null, DialogOptions options = null) where T : ComponentBase
|
||||
{
|
||||
var task = new TaskCompletionSource<dynamic>();
|
||||
tasks.Add(task);
|
||||
@@ -140,6 +177,149 @@ namespace Radzen
|
||||
return task.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a dialog with the specified arguments dynamically.
|
||||
/// </summary>
|
||||
/// <param name="title">The text displayed in the title bar of the dialog.</param>
|
||||
/// <param name="componentType">The type of the Blazor component to be displayed in a dialog. Must inherit from <see cref="ComponentBase"/>.</param>
|
||||
/// <param name="parameters">The dialog parameters, passed as property values of the specified component.</param>
|
||||
/// <param name="options">The dialog options.</param>
|
||||
/// <returns>A task that represents the result passed as an argument to <see cref="Close"/>.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if <paramref name="componentType"/> does not inherit from <see cref="ComponentBase"/>.</exception>
|
||||
public virtual Task<dynamic> OpenAsync(string title, Type componentType, Dictionary<string, object> parameters = null, DialogOptions options = null)
|
||||
{
|
||||
if (!typeof(ComponentBase).IsAssignableFrom(componentType))
|
||||
{
|
||||
throw new ArgumentException("The component type must be a subclass of ComponentBase.", nameof(componentType));
|
||||
}
|
||||
|
||||
var task = new TaskCompletionSource<dynamic>();
|
||||
tasks.Add(task);
|
||||
|
||||
var method = GetType().GetMethod(nameof(OpenDialog), BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
method.MakeGenericMethod(componentType).Invoke(this, new object[] { title, parameters, options });
|
||||
|
||||
return task.Task;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Opens a side dialog with the specified arguments
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of Blazor component which will be displayed in the side dialog.</typeparam>
|
||||
/// <param name="title">The text displayed in the title bar of the side dialog.</param>
|
||||
/// <param name="parameters">The dialog parameters. Passed as property values of <typeparamref name="T"/></param>
|
||||
/// <param name="options">The side dialog options.</param>
|
||||
/// <returns>A task that completes when the dialog is closed or a new one opened</returns>
|
||||
public Task<dynamic> OpenSideAsync<T>(string title, Dictionary<string, object> parameters = null, SideDialogOptions options = null)
|
||||
where T : ComponentBase
|
||||
{
|
||||
CloseSide();
|
||||
_sideDialogTask = new TaskCompletionSource<dynamic>();
|
||||
if (options == null)
|
||||
{
|
||||
options = new SideDialogOptions();
|
||||
}
|
||||
|
||||
options.Title = title;
|
||||
OnSideOpen?.Invoke(typeof(T), parameters ?? new Dictionary<string, object>(), options);
|
||||
return _sideDialogTask.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a side dialog with the specified arguments dynamically.
|
||||
/// </summary>
|
||||
/// <param name="title">The text displayed in the title bar of the side dialog.</param>
|
||||
/// <param name="componentType">The type of the Blazor component to be displayed in the side dialog. Must inherit from <see cref="ComponentBase"/>.</param>
|
||||
/// <param name="parameters">The dialog parameters, passed as property values of the specified component.</param>
|
||||
/// <param name="options">The side dialog options.</param>
|
||||
/// <returns>A task that represents the result passed as an argument to <see cref="CloseSide"/>.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if <paramref name="componentType"/> does not inherit from <see cref="ComponentBase"/>.</exception>
|
||||
public Task<dynamic> OpenSideAsync(string title, Type componentType, Dictionary<string, object> parameters = null, SideDialogOptions options = null)
|
||||
{
|
||||
if (!typeof(ComponentBase).IsAssignableFrom(componentType))
|
||||
{
|
||||
throw new ArgumentException("The component type must be a subclass of ComponentBase.", nameof(componentType));
|
||||
}
|
||||
|
||||
CloseSide();
|
||||
_sideDialogTask = new TaskCompletionSource<dynamic>();
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
options = new SideDialogOptions();
|
||||
}
|
||||
|
||||
options.Title = title;
|
||||
OnSideOpen?.Invoke(componentType, parameters ?? new Dictionary<string, object>(), options);
|
||||
|
||||
return _sideDialogTask.Task;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Opens a side dialog with the specified arguments
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of Blazor component which will be displayed in the side dialog.</typeparam>
|
||||
/// <param name="title">The text displayed in the title bar of the side dialog.</param>
|
||||
/// <param name="parameters">The dialog parameters. Passed as property values of <typeparamref name="T"/></param>
|
||||
/// <param name="options">The side dialog options.</param>
|
||||
public void OpenSide<T>(string title, Dictionary<string, object> parameters = null, SideDialogOptions options = null)
|
||||
where T : ComponentBase
|
||||
{
|
||||
CloseSide();
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
options = new SideDialogOptions();
|
||||
}
|
||||
|
||||
options.Title = title;
|
||||
OnSideOpen?.Invoke(typeof(T), parameters ?? new Dictionary<string, object>(), options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a side dialog with the specified arguments dynamically.
|
||||
/// </summary>
|
||||
/// <param name="title">The text displayed in the title bar of the side dialog.</param>
|
||||
/// <param name="componentType">The type of the Blazor component to be displayed in the side dialog. Must inherit from <see cref="ComponentBase"/>.</param>
|
||||
/// <param name="parameters">The dialog parameters, passed as property values of the specified component.</param>
|
||||
/// <param name="options">The side dialog options.</param>
|
||||
/// <exception cref="ArgumentException">Thrown if <paramref name="componentType"/> does not inherit from <see cref="ComponentBase"/>.</exception>
|
||||
public void OpenSide(string title, Type componentType, Dictionary<string, object> parameters = null, SideDialogOptions options = null)
|
||||
{
|
||||
if (!typeof(ComponentBase).IsAssignableFrom(componentType))
|
||||
{
|
||||
throw new ArgumentException("The component type must be a subclass of ComponentBase.", nameof(componentType));
|
||||
}
|
||||
|
||||
CloseSide();
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
options = new SideDialogOptions();
|
||||
}
|
||||
|
||||
options.Title = title;
|
||||
OnSideOpen?.Invoke(componentType, parameters ?? new Dictionary<string, object>(), options);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Closes the side dialog
|
||||
/// </summary>
|
||||
/// <param name="result">The result of the Dialog</param>
|
||||
public void CloseSide(dynamic result = null)
|
||||
{
|
||||
if (_sideDialogTask?.Task.IsCompleted == false)
|
||||
{
|
||||
_sideDialogTask.TrySetResult(result);
|
||||
}
|
||||
|
||||
OnSideClose?.Invoke(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a dialog with the specified content.
|
||||
/// </summary>
|
||||
@@ -147,7 +327,7 @@ namespace Radzen
|
||||
/// <param name="childContent">The content displayed in the dialog.</param>
|
||||
/// <param name="options">The dialog options.</param>
|
||||
/// <returns>The value passed as argument to <see cref="Close" />.</returns>
|
||||
public Task<dynamic> OpenAsync(string title, RenderFragment<DialogService> childContent, DialogOptions options = null)
|
||||
public virtual Task<dynamic> OpenAsync(string title, RenderFragment<DialogService> childContent, DialogOptions options = null)
|
||||
{
|
||||
var task = new TaskCompletionSource<dynamic>();
|
||||
tasks.Add(task);
|
||||
@@ -161,13 +341,36 @@ namespace Radzen
|
||||
return task.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a dialog with the specified content.
|
||||
/// </summary>
|
||||
/// <param name="titleContent">The content displayed in the title bar of the dialog.</param>
|
||||
/// <param name="childContent">The content displayed in the dialog.</param>
|
||||
/// <param name="options">The dialog options.</param>
|
||||
/// <returns>The value passed as argument to <see cref="Close" />.</returns>
|
||||
public virtual Task<dynamic> OpenAsync(RenderFragment<DialogService> titleContent, RenderFragment<DialogService> childContent, DialogOptions options = null)
|
||||
{
|
||||
var task = new TaskCompletionSource<dynamic>();
|
||||
tasks.Add(task);
|
||||
|
||||
options = options ?? new DialogOptions();
|
||||
|
||||
options.ChildContent = childContent;
|
||||
|
||||
options.TitleContent = titleContent;
|
||||
|
||||
OpenDialog<object>(null, null, options);
|
||||
|
||||
return task.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a dialog with the specified content.
|
||||
/// </summary>
|
||||
/// <param name="title">The text displayed in the title bar of the dialog.</param>
|
||||
/// <param name="childContent">The content displayed in the dialog.</param>
|
||||
/// <param name="options">The dialog options.</param>
|
||||
public void Open(string title, RenderFragment<DialogService> childContent, DialogOptions options = null)
|
||||
public virtual void Open(string title, RenderFragment<DialogService> childContent, DialogOptions options = null)
|
||||
{
|
||||
options = options ?? new DialogOptions();
|
||||
|
||||
@@ -196,11 +399,17 @@ namespace Radzen
|
||||
Resizable = options != null ? options.Resizable : false,
|
||||
Draggable = options != null ? options.Draggable : false,
|
||||
ChildContent = options?.ChildContent,
|
||||
TitleContent = options?.TitleContent,
|
||||
Style = options != null ? options.Style : "",
|
||||
AutoFocusFirstElement = options != null ? options.AutoFocusFirstElement : true,
|
||||
CloseDialogOnOverlayClick = options != null ? options.CloseDialogOnOverlayClick : false,
|
||||
CloseDialogOnEsc = options != null ? options.CloseDialogOnEsc : true,
|
||||
CssClass = options != null ? options.CssClass : "",
|
||||
WrapperCssClass = options != null ? options.WrapperCssClass : "",
|
||||
CloseTabIndex = options != null ? options.CloseTabIndex : 0,
|
||||
ContentCssClass = options != null ? options.ContentCssClass : "",
|
||||
Resize = options?.Resize,
|
||||
Drag = options?.Drag
|
||||
});
|
||||
}
|
||||
|
||||
@@ -243,7 +452,7 @@ namespace Radzen
|
||||
/// <param name="title">The text displayed in the title bar of the dialog.</param>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns><c>true</c> if the user clicked the OK button, <c>false</c> otherwise.</returns>
|
||||
public async Task<bool?> Confirm(string message = "Confirm?", string title = "Confirm", ConfirmOptions options = null)
|
||||
public virtual async Task<bool?> Confirm(string message = "Confirm?", string title = "Confirm", ConfirmOptions options = null)
|
||||
{
|
||||
var dialogOptions = new DialogOptions()
|
||||
{
|
||||
@@ -262,6 +471,8 @@ namespace Radzen
|
||||
CloseDialogOnOverlayClick = options != null ? options.CloseDialogOnOverlayClick : false,
|
||||
CloseDialogOnEsc = options != null ? options.CloseDialogOnEsc : true,
|
||||
CssClass = options != null ? $"rz-dialog-confirm {options.CssClass}" : "rz-dialog-confirm",
|
||||
WrapperCssClass = options != null ? $"rz-dialog-wrapper {options.WrapperCssClass}" : "rz-dialog-wrapper",
|
||||
CloseTabIndex = options != null ? options.CloseTabIndex : 0,
|
||||
};
|
||||
|
||||
return await OpenAsync(title, ds =>
|
||||
@@ -271,7 +482,7 @@ namespace Radzen
|
||||
var i = 0;
|
||||
b.OpenElement(i++, "p");
|
||||
b.AddAttribute(i++, "class", "rz-dialog-confirm-message");
|
||||
b.AddContent(i++, message);
|
||||
b.AddContent(i++, (MarkupString)message);
|
||||
b.CloseElement();
|
||||
|
||||
b.OpenElement(i++, "div");
|
||||
@@ -284,7 +495,7 @@ namespace Radzen
|
||||
|
||||
b.OpenComponent<Blazor.RadzenButton>(i++);
|
||||
b.AddAttribute(i++, "Text", options != null ? options.CancelButtonText : "Cancel");
|
||||
b.AddAttribute(i++, "ButtonStyle", ButtonStyle.Secondary);
|
||||
b.AddAttribute(i++, "ButtonStyle", ButtonStyle.Base);
|
||||
b.AddAttribute(i++, "Click", EventCallback.Factory.Create<Microsoft.AspNetCore.Components.Web.MouseEventArgs>(this, () => ds.Close(false)));
|
||||
b.CloseComponent();
|
||||
|
||||
@@ -301,7 +512,7 @@ namespace Radzen
|
||||
/// <param name="title">The text displayed in the title bar of the dialog.</param>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns><c>true</c> if the user clicked the OK button, <c>false</c> otherwise.</returns>
|
||||
public async Task<bool?> Alert(string message = "", string title = "Message", AlertOptions options = null)
|
||||
public virtual async Task<bool?> Alert(string message = "", string title = "Message", AlertOptions options = null)
|
||||
{
|
||||
var dialogOptions = new DialogOptions()
|
||||
{
|
||||
@@ -320,6 +531,9 @@ namespace Radzen
|
||||
CloseDialogOnOverlayClick = options != null ? options.CloseDialogOnOverlayClick : false,
|
||||
CloseDialogOnEsc = options != null ? options.CloseDialogOnEsc : true,
|
||||
CssClass = options != null ? $"rz-dialog-alert {options.CssClass}" : "rz-dialog-alert",
|
||||
WrapperCssClass = options != null ? $"rz-dialog-wrapper {options.WrapperCssClass}" : "rz-dialog-wrapper",
|
||||
ContentCssClass = options != null ? $"rz-dialog-content {options.ContentCssClass}" : "rz-dialog-content",
|
||||
CloseTabIndex = options != null ? options.CloseTabIndex : 0,
|
||||
};
|
||||
|
||||
return await OpenAsync(title, ds =>
|
||||
@@ -329,7 +543,7 @@ namespace Radzen
|
||||
var i = 0;
|
||||
b.OpenElement(i++, "p");
|
||||
b.AddAttribute(i++, "class", "rz-dialog-alert-message");
|
||||
b.AddContent(i++, message);
|
||||
b.AddContent(i++, (MarkupString)message);
|
||||
b.CloseElement();
|
||||
|
||||
b.OpenElement(i++, "div");
|
||||
@@ -348,9 +562,9 @@ namespace Radzen
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class DialogOptions.
|
||||
/// Base Class for dialog options
|
||||
/// </summary>
|
||||
public class DialogOptions
|
||||
public abstract class DialogOptionsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show the title bar. Set to <c>true</c> by default.
|
||||
@@ -363,17 +577,127 @@ namespace Radzen
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if the close button is shown; otherwise, <c>false</c>.</value>
|
||||
public bool ShowClose { get; set; } = true;
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the dialog.
|
||||
/// </summary>
|
||||
/// <value>The width.</value>
|
||||
public string Width { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the height of the dialog.
|
||||
/// </summary>
|
||||
/// <value>The height.</value>
|
||||
public string Height { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the CSS style of the dialog
|
||||
/// </summary>
|
||||
/// <value>The style.</value>
|
||||
public string Style { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the dialog should be closed by clicking the overlay.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if closeable; otherwise, <c>false</c>.</value>
|
||||
public bool CloseDialogOnOverlayClick { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets dialog box custom class
|
||||
/// </summary>
|
||||
public string CssClass { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the CSS classes added to the dialog's wrapper element.
|
||||
/// </summary>
|
||||
public string WrapperCssClass { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the CSS classes added to the dialog's content element.
|
||||
/// </summary>
|
||||
public string ContentCssClass { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value the dialog escape tabindex. Set to <c>0</c> by default.
|
||||
/// </summary>
|
||||
public int CloseTabIndex { get; set; } = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class SideDialogOptions
|
||||
/// </summary>
|
||||
public class SideDialogOptions : DialogOptionsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The title displayed on the dialog.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Position on which the dialog will be positioned
|
||||
/// </summary>
|
||||
public DialogPosition Position { get; set; } = DialogPosition.Right;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to show a mask on the background or not
|
||||
/// </summary>
|
||||
public bool ShowMask { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to focus the first focusable HTML element. Set to <c>true</c> by default.
|
||||
/// </summary>
|
||||
public bool AutoFocusFirstElement { get; set; } = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DialogPosition enum
|
||||
/// </summary>
|
||||
public enum DialogPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// Dialog will be positioned on the right side
|
||||
/// </summary>
|
||||
Right,
|
||||
/// <summary>
|
||||
/// Dialog will be positioned on the left side
|
||||
/// </summary>
|
||||
Left,
|
||||
/// <summary>
|
||||
/// Dialog will be positioned on the top of the page
|
||||
/// </summary>
|
||||
Top,
|
||||
/// <summary>
|
||||
/// Dialog will be positioned at the bottom of the page
|
||||
/// </summary>
|
||||
Bottom
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class DialogOptions.
|
||||
/// </summary>
|
||||
public class DialogOptions : DialogOptionsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the dialog is resizable. Set to <c>false</c> by default.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if resizable; otherwise, <c>false</c>.</value>
|
||||
public bool Resizable { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the change.
|
||||
/// </summary>
|
||||
/// <value>The change.</value>
|
||||
public Action<Size> Resize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the dialog is draggable. Set to <c>false</c> by default.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if draggable; otherwise, <c>false</c>.</value>
|
||||
public bool Draggable { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the change.
|
||||
/// </summary>
|
||||
/// <value>The change.</value>
|
||||
public Action<Point> Drag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the X coordinate of the dialog. Maps to the <c>left</c> CSS attribute.
|
||||
/// </summary>
|
||||
@@ -390,46 +714,24 @@ namespace Radzen
|
||||
/// <value>The bottom.</value>
|
||||
public string Bottom { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the dialog.
|
||||
/// </summary>
|
||||
/// <value>The width.</value>
|
||||
public string Width { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the height of the dialog.
|
||||
/// </summary>
|
||||
/// <value>The height.</value>
|
||||
public string Height { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the CSS style of the dialog
|
||||
/// </summary>
|
||||
/// <value>The style.</value>
|
||||
public string Style { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the child content.
|
||||
/// </summary>
|
||||
/// <value>The child content.</value>
|
||||
public RenderFragment<DialogService> ChildContent { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the title content.
|
||||
/// </summary>
|
||||
/// <value>The title content.</value>
|
||||
public RenderFragment<DialogService> TitleContent { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to focus the first focusable HTML element. Set to <c>true</c> by default.
|
||||
/// </summary>
|
||||
public bool AutoFocusFirstElement { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the dialog should be closed by clicking the overlay.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if closeable; otherwise, <c>false</c>.</value>
|
||||
public bool CloseDialogOnOverlayClick { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the dialog should be closed on ESC key press.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if closeable; otherwise, <c>false</c>.</value>
|
||||
public bool CloseDialogOnEsc { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets dialog box custom class
|
||||
/// </summary>
|
||||
public string CssClass { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
using Microsoft.JSInterop;
|
||||
using Radzen.Blazor;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Radzen
|
||||
{
|
||||
@@ -16,7 +16,12 @@ namespace Radzen
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class DropDownBase<T> : DataBoundFormComponent<T>
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Gets or sets a value that determines how many additional items will be rendered before and after the visible region. This help to reduce the frequency of rendering during scrolling. However, higher values mean that more elements will be present in the page.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int VirtualizationOverscanCount { get; set; }
|
||||
|
||||
internal Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<object> virtualize;
|
||||
|
||||
/// <summary>
|
||||
@@ -39,14 +44,14 @@ namespace Radzen
|
||||
var totalItemsCount = LoadData.HasDelegate ? Count : view.Count();
|
||||
var top = request.Count;
|
||||
|
||||
if(top <= 0)
|
||||
if (top <= 0)
|
||||
{
|
||||
top = PageSize;
|
||||
}
|
||||
|
||||
if (LoadData.HasDelegate)
|
||||
{
|
||||
await LoadData.InvokeAsync(new Radzen.LoadDataArgs() { Skip = request.StartIndex, Top = request.Count, Filter = await JSRuntime.InvokeAsync<string>("Radzen.getInputValue", search) });
|
||||
await LoadData.InvokeAsync(new Radzen.LoadDataArgs() { Skip = request.StartIndex, Top = top, Filter = searchText });
|
||||
}
|
||||
|
||||
virtualItems = (LoadData.HasDelegate ? Data : view.Skip(request.StartIndex).Take(top)).Cast<object>().ToList();
|
||||
@@ -71,18 +76,19 @@ namespace Radzen
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int PageSize { get; set; } = 5;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether virtualization is allowed.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if virtualization is allowed; otherwise, <c>false</c>.</returns>
|
||||
internal bool IsVirtualizationAllowed()
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
return AllowVirtualization;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal int GetVirtualizationOverscanCount()
|
||||
{
|
||||
return VirtualizationOverscanCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -93,7 +99,6 @@ namespace Radzen
|
||||
{
|
||||
return new RenderFragment(builder =>
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
if (AllowVirtualization)
|
||||
{
|
||||
builder.OpenComponent(0, typeof(Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<object>));
|
||||
@@ -106,6 +111,11 @@ namespace Radzen
|
||||
});
|
||||
}));
|
||||
|
||||
if (VirtualizationOverscanCount != default(int))
|
||||
{
|
||||
builder.AddAttribute(3, "OverscanCount", VirtualizationOverscanCount);
|
||||
}
|
||||
|
||||
builder.AddComponentReferenceCapture(7, c => { virtualize = (Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<object>)c; });
|
||||
|
||||
builder.CloseComponent();
|
||||
@@ -117,12 +127,6 @@ namespace Radzen
|
||||
RenderItem(builder, item);
|
||||
}
|
||||
}
|
||||
#else
|
||||
foreach (var item in LoadData.HasDelegate ? Data : View)
|
||||
{
|
||||
RenderItem(builder, item);
|
||||
}
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
@@ -173,6 +177,13 @@ namespace Radzen
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header template.
|
||||
/// </summary>
|
||||
/// <value>The header template.</value>
|
||||
[Parameter]
|
||||
public RenderFragment HeaderTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether filtering is allowed. Set to <c>false</c> by default.
|
||||
/// </summary>
|
||||
@@ -180,6 +191,13 @@ namespace Radzen
|
||||
[Parameter]
|
||||
public virtual bool AllowFiltering { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether filtering is allowed as you type. Set to <c>true</c> by default.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if filtering is allowed; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public virtual bool FilterAsYouType { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the user can clear the value. Set to <c>false</c> by default.
|
||||
/// </summary>
|
||||
@@ -222,17 +240,38 @@ namespace Radzen
|
||||
[Parameter]
|
||||
public string DisabledProperty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the remove chip button title.
|
||||
/// </summary>
|
||||
/// <value>The remove chip button title.</value>
|
||||
[Parameter]
|
||||
public string RemoveChipTitle { get; set; } = "Remove";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the search aria label text.
|
||||
/// </summary>
|
||||
/// <value>The search aria label text.</value>
|
||||
[Parameter]
|
||||
public string SearchAriaLabel { get; set; } = "Search";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the empty value aria label text.
|
||||
/// </summary>
|
||||
/// <value>The empty value aria label text.</value>
|
||||
[Parameter]
|
||||
public string EmptyAriaLabel { get; set; } = "Empty";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected item changed.
|
||||
/// </summary>
|
||||
/// <value>The selected item changed.</value>
|
||||
[Parameter]
|
||||
public Action<object> SelectedItemChanged { get; set; }
|
||||
public EventCallback<object> SelectedItemChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The selected items
|
||||
/// </summary>
|
||||
protected IList<object> selectedItems = new List<object>();
|
||||
protected ISet<object> selectedItems = new HashSet<object>();
|
||||
/// <summary>
|
||||
/// The selected item
|
||||
/// </summary>
|
||||
@@ -241,17 +280,17 @@ namespace Radzen
|
||||
/// <summary>
|
||||
/// Selects all.
|
||||
/// </summary>
|
||||
protected async System.Threading.Tasks.Task SelectAll()
|
||||
protected virtual async System.Threading.Tasks.Task SelectAll()
|
||||
{
|
||||
if (Disabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedItems.Count != View.Cast<object>().Count())
|
||||
if (selectedItems.Count != View.Cast<object>().ToList().Where(i => disabledPropertyGetter == null || disabledPropertyGetter(i) as bool? != true).Count())
|
||||
{
|
||||
selectedItems.Clear();
|
||||
selectedItems = View.Cast<object>().ToList();
|
||||
selectedItems = View.Cast<object>().ToList().Where(i => disabledPropertyGetter == null || disabledPropertyGetter(i) as bool? != true).ToHashSet(ItemComparer);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -278,6 +317,15 @@ namespace Radzen
|
||||
}
|
||||
await ValueChanged.InvokeAsync((T)(object)list);
|
||||
}
|
||||
else if (typeof(T).IsGenericType && typeof(ICollection<>).MakeGenericType(typeof(T).GetGenericArguments()[0]).IsAssignableFrom(typeof(T)))
|
||||
{
|
||||
var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(T).GetGenericArguments()[0]));
|
||||
foreach (var i in (IEnumerable)internalValue)
|
||||
{
|
||||
list.Add(i);
|
||||
}
|
||||
await ValueChanged.InvokeAsync((T)(object)list);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ValueChanged.InvokeAsync((T)internalValue);
|
||||
@@ -286,16 +334,23 @@ namespace Radzen
|
||||
await Change.InvokeAsync(internalValue);
|
||||
|
||||
StateHasChanged();
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", GetId());
|
||||
}
|
||||
|
||||
internal bool IsAllSelected()
|
||||
{
|
||||
List<object> notDisabledItemsInList = View.Cast<object>().ToList()
|
||||
.Where(i => disabledPropertyGetter == null || disabledPropertyGetter(i) as bool? != true)
|
||||
.ToList();
|
||||
|
||||
if (LoadData.HasDelegate && !string.IsNullOrEmpty(ValueProperty))
|
||||
{
|
||||
return View != null && View.Cast<object>().All(i => IsItemSelectedByValue(GetItemOrValueFromProperty(i, ValueProperty)));
|
||||
return View != null && notDisabledItemsInList.Count > 0 && notDisabledItemsInList
|
||||
.All(i => IsItemSelectedByValue(GetItemOrValueFromProperty(i, ValueProperty)));
|
||||
}
|
||||
|
||||
return View != null && selectedItems.Count == View.Cast<object>().Count();
|
||||
return View != null && notDisabledItemsInList.Count > 0 && selectedItems.Count == notDisabledItemsInList.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -316,6 +371,7 @@ namespace Radzen
|
||||
return;
|
||||
|
||||
searchText = null;
|
||||
await SearchTextChanged.InvokeAsync(searchText);
|
||||
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", search, "");
|
||||
|
||||
internalValue = default(T);
|
||||
@@ -379,28 +435,50 @@ namespace Radzen
|
||||
|
||||
var type = query.ElementType;
|
||||
|
||||
if (type == typeof(object) && typeof(EnumerableQuery).IsAssignableFrom(query.GetType()) && query.Any())
|
||||
if (type == typeof(object) && typeof(EnumerableQuery).IsAssignableFrom(query.GetType()) && query.Cast<object>().Any())
|
||||
{
|
||||
type = query.FirstOrDefault().GetType();
|
||||
var firstElement = query.Cast<object>().FirstOrDefault(i => i != null);
|
||||
if (firstElement != null)
|
||||
{
|
||||
type = firstElement.GetType();
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ValueProperty))
|
||||
{
|
||||
valuePropertyGetter = PropertyAccess.Getter<object, object>(ValueProperty, type);
|
||||
valuePropertyGetter = GetGetter(ValueProperty, type);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(TextProperty))
|
||||
{
|
||||
textPropertyGetter = PropertyAccess.Getter<object, object>(TextProperty, type);
|
||||
textPropertyGetter = GetGetter(TextProperty, type);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(DisabledProperty))
|
||||
{
|
||||
disabledPropertyGetter = PropertyAccess.Getter<object, object>(DisabledProperty, type);
|
||||
disabledPropertyGetter = GetGetter(DisabledProperty, type);
|
||||
}
|
||||
|
||||
if (selectedItems.Count == 0)
|
||||
{
|
||||
selectedItems = new HashSet<object>(ItemComparer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Func<object, object> GetGetter(string propertyName, Type type)
|
||||
{
|
||||
if (propertyName?.Contains("[") == true)
|
||||
{
|
||||
var getter = typeof(PropertyAccess).GetMethod("Getter", [typeof(string), typeof(Type)]);
|
||||
var getterMethod = getter.MakeGenericMethod([type, typeof(object)]);
|
||||
|
||||
return (i) => getterMethod.Invoke(i, [propertyName, type]);
|
||||
}
|
||||
|
||||
return PropertyAccess.Getter<object, object>(propertyName, type);
|
||||
}
|
||||
|
||||
internal Func<object, object> valuePropertyGetter;
|
||||
internal Func<object, object> textPropertyGetter;
|
||||
internal Func<object, object> disabledPropertyGetter;
|
||||
@@ -415,30 +493,29 @@ namespace Radzen
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
if (property == TextProperty && textPropertyGetter != null)
|
||||
{
|
||||
return textPropertyGetter(item);
|
||||
}
|
||||
else if (property == ValueProperty && valuePropertyGetter != null)
|
||||
{
|
||||
return valuePropertyGetter(item);
|
||||
}
|
||||
else if (property == DisabledProperty && disabledPropertyGetter != null)
|
||||
{
|
||||
return disabledPropertyGetter(item);
|
||||
}
|
||||
|
||||
var enumValue = item as Enum;
|
||||
if (enumValue != null)
|
||||
{
|
||||
return Radzen.Blazor.EnumExtensions.GetDisplayDescription(enumValue);
|
||||
}
|
||||
|
||||
if (property == TextProperty)
|
||||
{
|
||||
return textPropertyGetter != null ? textPropertyGetter(item) : PropertyAccess.GetItemOrValueFromProperty(item, property);
|
||||
}
|
||||
else if (property == ValueProperty)
|
||||
{
|
||||
return valuePropertyGetter != null ? valuePropertyGetter(item) : PropertyAccess.GetItemOrValueFromProperty(item, property);
|
||||
}
|
||||
else if (property == DisabledProperty)
|
||||
{
|
||||
return disabledPropertyGetter != null ? disabledPropertyGetter(item) : PropertyAccess.GetItemOrValueFromProperty(item, property);
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnDataChanged()
|
||||
{
|
||||
@@ -449,7 +526,6 @@ namespace Radzen
|
||||
await InvokeAsync(Virtualize.RefreshDataAsync);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets the popup identifier.
|
||||
@@ -467,7 +543,7 @@ namespace Radzen
|
||||
/// Gets the search identifier.
|
||||
/// </summary>
|
||||
/// <value>The search identifier.</value>
|
||||
protected string SearchID
|
||||
public string SearchID
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -543,12 +619,15 @@ namespace Radzen
|
||||
}
|
||||
}
|
||||
|
||||
internal bool preventKeydown = false;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the key press.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="Microsoft.AspNetCore.Components.Web.KeyboardEventArgs"/> instance containing the event data.</param>
|
||||
/// <param name="isFilter">if set to <c>true</c> [is filter].</param>
|
||||
private async System.Threading.Tasks.Task HandleKeyPress(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs args, bool isFilter = false)
|
||||
/// <param name="shouldSelectOnChange">Should select item on item change with keyboard.</param>
|
||||
protected virtual async System.Threading.Tasks.Task HandleKeyPress(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs args, bool isFilter = false, bool? shouldSelectOnChange = null)
|
||||
{
|
||||
if (Disabled)
|
||||
return;
|
||||
@@ -566,9 +645,7 @@ namespace Radzen
|
||||
{
|
||||
if (IsVirtualizationAllowed())
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
items = virtualItems;
|
||||
#endif
|
||||
items = virtualItems ?? Enumerable.Empty<object>().ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -580,48 +657,72 @@ namespace Radzen
|
||||
|
||||
if (!args.AltKey && (key == "ArrowDown" || key == "ArrowLeft" || key == "ArrowUp" || key == "ArrowRight"))
|
||||
{
|
||||
preventKeydown = true;
|
||||
|
||||
try
|
||||
{
|
||||
var currentViewIndex = Multiple ? selectedIndex : items.IndexOf(selectedItem);
|
||||
selectedIndex = await JSRuntime.InvokeAsync<int>("Radzen.focusListItem", search, list, key == "ArrowDown" || key == "ArrowRight", selectedIndex);
|
||||
|
||||
var newSelectedIndex = await JSRuntime.InvokeAsync<int>("Radzen.focusListItem", search, list, key == "ArrowDown" || key == "ArrowRight", currentViewIndex);
|
||||
var popupOpened = await JSRuntime.InvokeAsync<bool>("Radzen.popupOpened", PopupID);
|
||||
|
||||
if (!Multiple)
|
||||
if (!Multiple && !popupOpened && shouldSelectOnChange != false)
|
||||
{
|
||||
if (newSelectedIndex != currentViewIndex && newSelectedIndex >= 0 && newSelectedIndex <= items.Count() - 1)
|
||||
var itemToSelect = items.ElementAtOrDefault(selectedIndex);
|
||||
if (itemToSelect != null)
|
||||
{
|
||||
selectedIndex = newSelectedIndex;
|
||||
await OnSelectItem(items.ElementAt(selectedIndex), true);
|
||||
await OnSelectItem(itemToSelect, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedIndex = await JSRuntime.InvokeAsync<int>("Radzen.focusListItem", search, list, key == "ArrowDown", currentViewIndex);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
else if (Multiple && key == "Enter")
|
||||
else if (key == "Enter" || key == "NumpadEnter")
|
||||
{
|
||||
preventKeydown = true;
|
||||
|
||||
if (selectedIndex >= 0 && selectedIndex <= items.Count() - 1)
|
||||
{
|
||||
var itemToSelect = items.ElementAtOrDefault(selectedIndex);
|
||||
|
||||
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", search, $"{searchText}".Trim());
|
||||
await OnSelectItem(items.ElementAt(selectedIndex), true);
|
||||
|
||||
if (itemToSelect != null)
|
||||
{
|
||||
await OnSelectItem(itemToSelect, true);
|
||||
}
|
||||
}
|
||||
|
||||
var popupOpened = await JSRuntime.InvokeAsync<bool>("Radzen.popupOpened", PopupID);
|
||||
|
||||
if (!popupOpened)
|
||||
{
|
||||
await OpenPopup(key, isFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Multiple)
|
||||
{
|
||||
await ClosePopup(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key == "Enter" || (args.AltKey && key == "ArrowDown"))
|
||||
else if (args.AltKey && key == "ArrowDown")
|
||||
{
|
||||
preventKeydown = true;
|
||||
|
||||
await OpenPopup(key, isFilter);
|
||||
}
|
||||
else if (key == "Escape" || key == "Tab")
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
|
||||
await ClosePopup(key);
|
||||
}
|
||||
else if (key == "Delete" && AllowClear)
|
||||
{
|
||||
preventKeydown = true;
|
||||
|
||||
if (!Multiple && selectedItem != null)
|
||||
{
|
||||
selectedIndex = -1;
|
||||
@@ -633,12 +734,59 @@ namespace Radzen
|
||||
Debounce(DebounceFilter, FilterDelay);
|
||||
}
|
||||
}
|
||||
else if (AllowFiltering && isFilter)
|
||||
else if (AllowFiltering && isFilter && FilterAsYouType)
|
||||
{
|
||||
preventKeydown = true;
|
||||
|
||||
Debounce(DebounceFilter, FilterDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
var filteredItems = (!string.IsNullOrEmpty(TextProperty) ?
|
||||
Query.Where(TextProperty, args.Key, StringFilterOperator.StartsWith, FilterCaseSensitivity.CaseInsensitive) :
|
||||
Query)
|
||||
.Cast(Query.ElementType).Cast<dynamic>().ToList();
|
||||
|
||||
|
||||
if (previousKey != args.Key)
|
||||
{
|
||||
previousKey = args.Key;
|
||||
itemIndex = -1;
|
||||
}
|
||||
|
||||
itemIndex = itemIndex + 1 >= filteredItems.Count() ? 0 : itemIndex + 1;
|
||||
var itemToSelect = filteredItems.ElementAtOrDefault(itemIndex);
|
||||
|
||||
if (itemToSelect != null)
|
||||
{
|
||||
if (!Multiple)
|
||||
{
|
||||
await SelectItem(itemToSelect);
|
||||
}
|
||||
|
||||
var result = items.Select((x, i) => new { Item = x, Index = i }).FirstOrDefault(itemWithIndex => object.Equals(itemWithIndex.Item, itemToSelect));
|
||||
if (result != null)
|
||||
{
|
||||
if (!Multiple)
|
||||
{
|
||||
selectedIndex = result.Index;
|
||||
}
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.selectListItem", list, list, result.Index);
|
||||
}
|
||||
}
|
||||
|
||||
preventKeydown = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual async Task ClosePopup(string key)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
|
||||
}
|
||||
|
||||
int itemIndex;
|
||||
string previousKey;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="E:FilterKeyPress" /> event.
|
||||
/// </summary>
|
||||
@@ -655,17 +803,14 @@ namespace Radzen
|
||||
{
|
||||
if (!LoadData.HasDelegate)
|
||||
{
|
||||
searchText = await JSRuntime.InvokeAsync<string>("Radzen.getInputValue", search);
|
||||
_view = null;
|
||||
if (IsVirtualizationAllowed())
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
if (virtualize != null)
|
||||
{
|
||||
await virtualize.RefreshDataAsync();
|
||||
}
|
||||
await InvokeAsync(() => { StateHasChanged(); });
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -676,13 +821,15 @@ namespace Radzen
|
||||
{
|
||||
if (IsVirtualizationAllowed())
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
if (virtualize != null)
|
||||
{
|
||||
await InvokeAsync(virtualize.RefreshDataAsync);
|
||||
}
|
||||
else
|
||||
{
|
||||
await LoadData.InvokeAsync(await GetLoadDataArgs());
|
||||
}
|
||||
await InvokeAsync(() => { StateHasChanged(); });
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -694,15 +841,17 @@ namespace Radzen
|
||||
selectedIndex = -1;
|
||||
|
||||
await JSRuntime.InvokeAsync<string>("Radzen.repositionPopup", Element, PopupID);
|
||||
await SearchTextChanged.InvokeAsync(SearchText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="E:KeyPress" /> event.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="Microsoft.AspNetCore.Components.Web.KeyboardEventArgs"/> instance containing the event data.</param>
|
||||
protected async System.Threading.Tasks.Task OnKeyPress(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs args)
|
||||
/// <param name="shouldSelectOnChange">Should select item on item change with keyboard.</param>
|
||||
protected virtual async System.Threading.Tasks.Task OnKeyPress(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs args, bool? shouldSelectOnChange = null)
|
||||
{
|
||||
await HandleKeyPress(args);
|
||||
await HandleKeyPress(args, false, shouldSelectOnChange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -730,18 +879,14 @@ namespace Radzen
|
||||
/// <returns>LoadDataArgs.</returns>
|
||||
internal virtual async System.Threading.Tasks.Task<LoadDataArgs> GetLoadDataArgs()
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
if (AllowVirtualization)
|
||||
{
|
||||
return new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize, Filter = await JSRuntime.InvokeAsync<string>("Radzen.getInputValue", search) };
|
||||
return await Task.FromResult(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize, Filter = searchText });
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Radzen.LoadDataArgs() { Filter = await JSRuntime.InvokeAsync<string>("Radzen.getInputValue", search) };
|
||||
return await Task.FromResult(new Radzen.LoadDataArgs() { Filter = searchText });
|
||||
}
|
||||
#else
|
||||
return new Radzen.LoadDataArgs() { Filter = await JSRuntime.InvokeAsync<string>("Radzen.getInputValue", search) };
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -776,13 +921,12 @@ namespace Radzen
|
||||
/// <returns>A Task representing the asynchronous operation.</returns>
|
||||
public override async Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
var pageSize = parameters.GetValueOrDefault<int>(nameof(PageSize));
|
||||
if(pageSize != default(int))
|
||||
if (pageSize != default(int))
|
||||
{
|
||||
PageSize = pageSize;
|
||||
}
|
||||
#endif
|
||||
|
||||
var selectedItemChanged = parameters.DidParameterChange(nameof(SelectedItem), SelectedItem);
|
||||
if (selectedItemChanged)
|
||||
{
|
||||
@@ -820,7 +964,7 @@ namespace Radzen
|
||||
|
||||
if (valueAsEnumerable != null)
|
||||
{
|
||||
if (valueAsEnumerable.OfType<object>().Count() != selectedItems.Count)
|
||||
if (!valueAsEnumerable.Cast<object>().SequenceEqual(selectedItems.Select(i => string.IsNullOrEmpty(ValueProperty) ? i : GetItemOrValueFromProperty(i, ValueProperty))))
|
||||
{
|
||||
selectedItems.Clear();
|
||||
}
|
||||
@@ -855,11 +999,11 @@ namespace Radzen
|
||||
{
|
||||
if (Multiple)
|
||||
{
|
||||
return selectedItems.IndexOf(item) != -1;
|
||||
return selectedItems.Contains(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
return object.Equals(item,selectedItem);
|
||||
return object.Equals(item, selectedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -913,42 +1057,7 @@ namespace Radzen
|
||||
{
|
||||
if (_view == null && Query != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(searchText))
|
||||
{
|
||||
var ignoreCase = FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive;
|
||||
|
||||
var query = new List<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(TextProperty))
|
||||
{
|
||||
query.Add(TextProperty);
|
||||
}
|
||||
|
||||
if (typeof(EnumerableQuery).IsAssignableFrom(Query.GetType()))
|
||||
{
|
||||
query.Add("ToString()");
|
||||
}
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
query.Add("ToLower()");
|
||||
}
|
||||
|
||||
query.Add($"{Enum.GetName(typeof(StringFilterOperator), FilterOperator)}(@0)");
|
||||
|
||||
_view = Query.Where(String.Join(".", query), ignoreCase ? searchText.ToLower() : searchText);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsVirtualizationAllowed())
|
||||
{
|
||||
_view = Query;
|
||||
}
|
||||
else
|
||||
{
|
||||
_view = (typeof(IQueryable).IsAssignableFrom(Data.GetType())) ? (Query as IEnumerable).Cast<object>().ToList().AsQueryable() : Query;
|
||||
}
|
||||
}
|
||||
_view = Query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
|
||||
}
|
||||
|
||||
return _view;
|
||||
@@ -996,7 +1105,7 @@ namespace Radzen
|
||||
/// <param name="raiseChange">if set to <c>true</c> [raise change].</param>
|
||||
public async System.Threading.Tasks.Task SelectItem(object item, bool raiseChange = true)
|
||||
{
|
||||
if (disabledPropertyGetter != null && disabledPropertyGetter(item) as bool? == true)
|
||||
if (disabledPropertyGetter != null && item != null && disabledPropertyGetter(item) as bool? == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1009,7 +1118,7 @@ namespace Radzen
|
||||
selectedItem = item;
|
||||
if (!string.IsNullOrEmpty(ValueProperty))
|
||||
{
|
||||
internalValue = GetItemOrValueFromProperty(item, ValueProperty);
|
||||
internalValue = PropertyAccess.GetItemOrValueFromProperty(item, ValueProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1018,7 +1127,7 @@ namespace Radzen
|
||||
|
||||
SetSelectedIndexFromSelectedItem();
|
||||
|
||||
SelectedItemChanged?.Invoke(selectedItem);
|
||||
await SelectedItemChanged.InvokeAsync(selectedItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1034,9 +1143,13 @@ namespace Radzen
|
||||
var query = Data.AsQueryable();
|
||||
var elementType = query.ElementType;
|
||||
|
||||
if (elementType == typeof(object) && typeof(EnumerableQuery).IsAssignableFrom(query.GetType()) && query.Any())
|
||||
if (elementType == typeof(object) && typeof(EnumerableQuery).IsAssignableFrom(query.GetType()) && query.Cast<object>().Any())
|
||||
{
|
||||
elementType = query.FirstOrDefault().GetType();
|
||||
var firstElement = query.Cast<object>().FirstOrDefault(i => i != null);
|
||||
if (firstElement != null)
|
||||
{
|
||||
elementType = firstElement.GetType();
|
||||
}
|
||||
}
|
||||
|
||||
if (elementType != null)
|
||||
@@ -1069,6 +1182,22 @@ namespace Radzen
|
||||
await ValueChanged.InvokeAsync((T)(object)list);
|
||||
}
|
||||
}
|
||||
else if (typeof(T).IsGenericType && typeof(ICollection<>).MakeGenericType(typeof(T).GetGenericArguments()[0]).IsAssignableFrom(typeof(T)))
|
||||
{
|
||||
if (object.Equals(internalValue, null))
|
||||
{
|
||||
await ValueChanged.InvokeAsync(default(T));
|
||||
}
|
||||
else
|
||||
{
|
||||
var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(T).GetGenericArguments()[0]));
|
||||
foreach (var i in (IEnumerable)internalValue)
|
||||
{
|
||||
list.Add(i);
|
||||
}
|
||||
await ValueChanged.InvokeAsync((T)(object)list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await ValueChanged.InvokeAsync(object.Equals(internalValue, null) ? default(T) : (T)internalValue);
|
||||
@@ -1082,6 +1211,12 @@ namespace Radzen
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object GetValue()
|
||||
{
|
||||
return internalValue;
|
||||
}
|
||||
|
||||
internal void UpdateSelectedItems(object item)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ValueProperty))
|
||||
@@ -1094,18 +1229,14 @@ namespace Radzen
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedItems = selectedItems.AsQueryable().Where($@"!object.Equals({ValueProperty},@0)", value).ToList();
|
||||
selectedItems = selectedItems.AsQueryable().Where(i => !object.Equals(GetItemOrValueFromProperty(i, ValueProperty), value)).ToHashSet(ItemComparer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!selectedItems.Any(i => object.Equals(i, item)))
|
||||
if (!selectedItems.Add(item))
|
||||
{
|
||||
selectedItems.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedItems = selectedItems.Where(i => !object.Equals(i, item)).ToList();
|
||||
selectedItems.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1116,19 +1247,29 @@ namespace Radzen
|
||||
/// <param name="value">The value.</param>
|
||||
protected virtual void SelectItemFromValue(object value)
|
||||
{
|
||||
if (value != null && View != null)
|
||||
var view = LoadData.HasDelegate ? Data : View;
|
||||
if (value != null && view != null)
|
||||
{
|
||||
if (!Multiple)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ValueProperty))
|
||||
{
|
||||
if (typeof(EnumerableQuery).IsAssignableFrom(View.GetType()))
|
||||
if (typeof(EnumerableQuery).IsAssignableFrom(view.GetType()))
|
||||
{
|
||||
SelectedItem = View.OfType<object>().Where(i => object.Equals(GetItemOrValueFromProperty(i, ValueProperty), value)).FirstOrDefault();
|
||||
SelectedItem = view.OfType<object>().Where(i => object.Equals(GetItemOrValueFromProperty(i, ValueProperty), value)).FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedItem = View.AsQueryable().Where($@"{ValueProperty} == @0", value).FirstOrDefault();
|
||||
SelectedItem = view.AsQueryable().Where(new FilterDescriptor[]
|
||||
{
|
||||
new FilterDescriptor()
|
||||
{
|
||||
Property = ValueProperty,
|
||||
FilterValue = value
|
||||
}
|
||||
},
|
||||
LogicalFilterOperator.And,
|
||||
FilterCaseSensitivity.Default).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1137,8 +1278,6 @@ namespace Radzen
|
||||
}
|
||||
|
||||
SetSelectedIndexFromSelectedItem();
|
||||
|
||||
SelectedItemChanged?.Invoke(selectedItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1147,20 +1286,29 @@ namespace Radzen
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ValueProperty))
|
||||
{
|
||||
foreach (object v in values.ToDynamicList())
|
||||
foreach (object v in values.Cast<dynamic>().ToList())
|
||||
{
|
||||
dynamic item;
|
||||
|
||||
if (typeof(EnumerableQuery).IsAssignableFrom(View.GetType()))
|
||||
if (typeof(EnumerableQuery).IsAssignableFrom(view.GetType()))
|
||||
{
|
||||
item = View.OfType<object>().Where(i => object.Equals(GetItemOrValueFromProperty(i, ValueProperty), v)).FirstOrDefault();
|
||||
item = view.OfType<object>().Where(i => object.Equals(GetItemOrValueFromProperty(i, ValueProperty), v)).FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
item = View.AsQueryable().Where($@"{ValueProperty} == @0", v).FirstOrDefault();
|
||||
item = view.AsQueryable().Where(new FilterDescriptor[]
|
||||
{
|
||||
new FilterDescriptor()
|
||||
{
|
||||
Property = ValueProperty,
|
||||
FilterValue = v
|
||||
}
|
||||
},
|
||||
LogicalFilterOperator.And,
|
||||
FilterCaseSensitivity.Default).FirstOrDefault();
|
||||
}
|
||||
|
||||
if (!object.Equals(item, null) && !selectedItems.AsQueryable().Where($@"object.Equals({ValueProperty},@0)", v).Any())
|
||||
if (!object.Equals(item, null) && !selectedItems.AsQueryable().Where(i => object.Equals(GetItemOrValueFromProperty(i, ValueProperty), v)).Any())
|
||||
{
|
||||
selectedItems.Add(item);
|
||||
}
|
||||
@@ -1168,7 +1316,7 @@ namespace Radzen
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedItems = ((IEnumerable)values).Cast<object>().ToList();
|
||||
selectedItems = values.Cast<object>().ToHashSet(ItemComparer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1180,6 +1328,11 @@ namespace Radzen
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For lists of objects, an IEqualityComparer to control how selected items are determined
|
||||
/// </summary>
|
||||
[Parameter] public IEqualityComparer<object> ItemComparer { get; set; }
|
||||
|
||||
internal bool IsItemSelectedByValue(object v)
|
||||
{
|
||||
switch (internalValue)
|
||||
|
||||
144
Radzen.Blazor/DynamicExtensions.cs
Normal file
144
Radzen.Blazor/DynamicExtensions.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using Radzen;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace System.Linq.Dynamic.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Class DynamicExtensions used to replace System.Linq.Dynamic.Core library.
|
||||
/// </summary>
|
||||
public static class DynamicExtensions
|
||||
{
|
||||
static readonly Func<string, Type> typeLocator = type => AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName.Replace("+", ".") == type);
|
||||
|
||||
/// <summary>
|
||||
/// Filters using the specified filter descriptors.
|
||||
/// </summary>
|
||||
public static IQueryable<T> Where<T>(
|
||||
this IQueryable<T> source,
|
||||
string predicate,
|
||||
object[] parameters = null, object[] otherParameters = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (parameters != null && !string.IsNullOrEmpty(predicate))
|
||||
{
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
object param = parameters[i];
|
||||
string value = param switch
|
||||
{
|
||||
string s when s == string.Empty => @"""""",
|
||||
null => "null",
|
||||
string s => @$"""{s.Replace("\"", "\\\"")}""",
|
||||
bool b => b.ToString().ToLower(),
|
||||
Guid g => $"Guid.Parse(\"{g}\")",
|
||||
DateTime dt => $"DateTime.Parse(\"{dt:yyyy-MM-ddTHH:mm:ss.fffZ}\")",
|
||||
DateTimeOffset dto => $"DateTime.Parse(\"{dto.UtcDateTime:yyyy-MM-ddTHH:mm:ss.fffZ}\")",
|
||||
DateOnly d => $"DateOnly.Parse(\"{d:yyy-MM-dd}\")",
|
||||
TimeOnly t => $"TimeOnly.Parse(\"{t:HH:mm:ss}\")",
|
||||
_ => param.ToString()
|
||||
};
|
||||
|
||||
predicate = predicate.Replace($"@{i}", $"{value}");
|
||||
}
|
||||
}
|
||||
|
||||
predicate = (predicate == "true" ? "" : predicate)
|
||||
.Replace("DateTime(", "DateTime.Parse(")
|
||||
.Replace("DateTimeOffset(", "DateTimeOffset.Parse(")
|
||||
.Replace("DateOnly(", "DateOnly.Parse(")
|
||||
.Replace("Guid(", "Guid.Parse(")
|
||||
.Replace(" = ", " == ");
|
||||
|
||||
return !string.IsNullOrEmpty(predicate) ?
|
||||
source.Where(ExpressionParser.ParsePredicate<T>(predicate, typeLocator)) : source;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid predicate: {predicate}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the elements of a sequence in ascending or descending order according to a key.
|
||||
/// </summary>
|
||||
public static IOrderedQueryable<T> OrderBy<T>(
|
||||
this IQueryable<T> source,
|
||||
string selector,
|
||||
object[] parameters = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return QueryableExtension.OrderBy(source, selector);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid selector: {selector}.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects each element of a sequence into a collection of property values.
|
||||
/// </summary>
|
||||
public static IQueryable Select<T>(this IQueryable<T> source, string selector, object[] parameters = null)
|
||||
{
|
||||
if (source.ElementType == typeof(object))
|
||||
{
|
||||
var elementType = source.ElementType;
|
||||
|
||||
if (source.Expression is MethodCallExpression methodCall && methodCall.Method.Name == "Cast")
|
||||
{
|
||||
elementType = methodCall.Arguments[0].Type.GetGenericArguments().FirstOrDefault() ?? typeof(object);
|
||||
}
|
||||
else if (typeof(EnumerableQuery).IsAssignableFrom(source.GetType()))
|
||||
{
|
||||
elementType = source.FirstOrDefault()?.GetType() ?? typeof(object);
|
||||
}
|
||||
|
||||
return source.Cast(elementType).Select(selector, expression => ExpressionParser.ParseLambda(expression, elementType));
|
||||
}
|
||||
|
||||
return source.Select(selector, expression => ExpressionParser.ParseLambda<T>(expression));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects each element of a sequence into a collection of property values.
|
||||
/// </summary>
|
||||
public static IQueryable Select(this IQueryable source, string selector, object[] parameters = null)
|
||||
{
|
||||
return source.Select(selector, expression => ExpressionParser.ParseLambda(expression, source.ElementType));
|
||||
}
|
||||
|
||||
private static IQueryable Select(this IQueryable source, string selector, Func<string, LambdaExpression> lambdaCreator)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(selector))
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
if (!selector.Contains("=>"))
|
||||
{
|
||||
var properties = selector
|
||||
.Replace("new (", "").Replace(")", "").Replace("new {", "").Replace("}", "").Trim()
|
||||
.Split(",", StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
selector = string.Join(", ", properties
|
||||
.Select(s => (s.Contains(" as ") ? s.Split(" as ").LastOrDefault().Trim().Replace(".", "_") : s.Trim().Replace(".", "_")) +
|
||||
" = " + $"it.{s.Split(" as ").FirstOrDefault().Replace(".", "?.").Trim()}"));
|
||||
}
|
||||
|
||||
var lambda = lambdaCreator(selector.Contains("=>") ? selector : $"it => new {{ {selector} }}");
|
||||
|
||||
return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), nameof(Queryable.Select),
|
||||
[source.ElementType, lambda.Body.Type], source.Expression, Expression.Quote(lambda)));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid selector: {selector}.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
548
Radzen.Blazor/ExpressionParser.cs
Normal file
548
Radzen.Blazor/ExpressionParser.cs
Normal file
@@ -0,0 +1,548 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
static class DynamicTypeFactory
|
||||
{
|
||||
public static Type CreateType(string typeName, string[] propertyNames, Type[] propertyTypes)
|
||||
{
|
||||
if (propertyNames.Length != propertyTypes.Length)
|
||||
{
|
||||
throw new ArgumentException("Property names and types count mismatch.");
|
||||
}
|
||||
|
||||
var assemblyName = new AssemblyName("DynamicTypesAssembly");
|
||||
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
|
||||
var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicTypesModule");
|
||||
|
||||
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed);
|
||||
|
||||
for (int i = 0; i < propertyNames.Length; i++)
|
||||
{
|
||||
var fieldBuilder = typeBuilder.DefineField("_" + propertyNames[i], propertyTypes[i], FieldAttributes.Private);
|
||||
var propertyBuilder = typeBuilder.DefineProperty(propertyNames[i], PropertyAttributes.None, propertyTypes[i], null);
|
||||
|
||||
var getterMethod = typeBuilder.DefineMethod(
|
||||
"get_" + propertyNames[i],
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
propertyTypes[i],
|
||||
Type.EmptyTypes);
|
||||
|
||||
var getterIl = getterMethod.GetILGenerator();
|
||||
getterIl.Emit(OpCodes.Ldarg_0);
|
||||
getterIl.Emit(OpCodes.Ldfld, fieldBuilder);
|
||||
getterIl.Emit(OpCodes.Ret);
|
||||
|
||||
propertyBuilder.SetGetMethod(getterMethod);
|
||||
|
||||
var setterMethod = typeBuilder.DefineMethod(
|
||||
"set_" + propertyNames[i],
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
null,
|
||||
[propertyTypes[i]]);
|
||||
|
||||
var setterIl = setterMethod.GetILGenerator();
|
||||
setterIl.Emit(OpCodes.Ldarg_0);
|
||||
setterIl.Emit(OpCodes.Ldarg_1);
|
||||
setterIl.Emit(OpCodes.Stfld, fieldBuilder);
|
||||
setterIl.Emit(OpCodes.Ret);
|
||||
|
||||
propertyBuilder.SetSetMethod(setterMethod);
|
||||
}
|
||||
|
||||
var dynamicType = typeBuilder.CreateType();
|
||||
return dynamicType;
|
||||
}
|
||||
}
|
||||
|
||||
class ExpressionSyntaxVisitor : CSharpSyntaxVisitor<Expression>
|
||||
{
|
||||
private readonly ParameterExpression parameter;
|
||||
private readonly Func<string, Type> typeLocator;
|
||||
|
||||
public ExpressionSyntaxVisitor(ParameterExpression parameter, Func<string, Type> typeLocator)
|
||||
{
|
||||
this.parameter = parameter;
|
||||
this.typeLocator = typeLocator;
|
||||
}
|
||||
|
||||
public override Expression VisitBinaryExpression(BinaryExpressionSyntax node)
|
||||
{
|
||||
var left = Visit(node.Left);
|
||||
|
||||
var right = ConvertIfNeeded(Visit(node.Right), left.Type);
|
||||
|
||||
return Expression.MakeBinary(ParseBinaryOperator(node.OperatorToken), left, right);
|
||||
}
|
||||
|
||||
|
||||
public override Expression VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
|
||||
{
|
||||
var expression = Visit(node.Expression) ?? parameter;
|
||||
return Expression.PropertyOrField(expression, node.Name.Identifier.Text);
|
||||
}
|
||||
|
||||
public override Expression VisitLiteralExpression(LiteralExpressionSyntax node)
|
||||
{
|
||||
return Expression.Constant(ParseLiteral(node));
|
||||
}
|
||||
|
||||
public override Expression VisitIdentifierName(IdentifierNameSyntax node)
|
||||
{
|
||||
if (node.Identifier.Text == parameter.Name)
|
||||
{
|
||||
return parameter;
|
||||
}
|
||||
|
||||
var type = GetType(node.Identifier.Text);
|
||||
|
||||
if (type != null)
|
||||
{
|
||||
return Expression.Constant(type);
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Unsupported identifier: " + node.Identifier.Text);
|
||||
}
|
||||
|
||||
public override Expression VisitConditionalExpression(ConditionalExpressionSyntax node)
|
||||
{
|
||||
var condition = Visit(node.Condition);
|
||||
|
||||
var whenTrue = Visit(node.WhenTrue);
|
||||
|
||||
var whenFalse = Visit(node.WhenFalse);
|
||||
|
||||
if (whenTrue.Type != whenFalse.Type)
|
||||
{
|
||||
if (whenTrue.Type == typeof(object))
|
||||
{
|
||||
whenTrue = Expression.Convert(whenTrue, whenFalse.Type);
|
||||
}
|
||||
else if (whenFalse.Type == typeof(object))
|
||||
{
|
||||
whenFalse = Expression.Convert(whenFalse, whenTrue.Type);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Conditional expression types mismatch: " + whenTrue.Type + " and " + whenFalse.Type);
|
||||
}
|
||||
}
|
||||
|
||||
return Expression.Condition(condition, whenTrue, whenFalse);
|
||||
}
|
||||
|
||||
public override Expression VisitParenthesizedExpression(ParenthesizedExpressionSyntax node)
|
||||
{
|
||||
return Visit(node.Expression);
|
||||
}
|
||||
|
||||
private Type GetType(string typeName)
|
||||
{
|
||||
var nullable = typeName.EndsWith('?');
|
||||
|
||||
if (nullable)
|
||||
{
|
||||
typeName = typeName[..^1];
|
||||
}
|
||||
|
||||
var type = typeName switch
|
||||
{
|
||||
nameof(Int32) => typeof(int),
|
||||
nameof(Int64) => typeof(long),
|
||||
nameof(Double) => typeof(double),
|
||||
nameof(Single) => typeof(float),
|
||||
nameof(Decimal) => typeof(decimal),
|
||||
nameof(String) => typeof(string),
|
||||
nameof(Boolean) => typeof(bool),
|
||||
nameof(DateTime) => typeof(DateTime),
|
||||
nameof(DateOnly) => typeof(DateOnly),
|
||||
nameof(DateTimeOffset) => typeof(DateTimeOffset),
|
||||
nameof(TimeOnly) => typeof(TimeOnly),
|
||||
nameof(Guid) => typeof(Guid),
|
||||
nameof(Char) => typeof(char),
|
||||
"int" => typeof(int),
|
||||
"long" => typeof(long),
|
||||
"double" => typeof(double),
|
||||
"float" => typeof(float),
|
||||
"decimal" => typeof(decimal),
|
||||
"string" => typeof(string),
|
||||
"char" => typeof(char),
|
||||
"bool" => typeof(bool),
|
||||
_ => typeLocator?.Invoke(typeName)
|
||||
};
|
||||
|
||||
if (nullable && type != null)
|
||||
{
|
||||
type = typeof(Nullable<>).MakeGenericType(type);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
public override Expression VisitCastExpression(CastExpressionSyntax node)
|
||||
{
|
||||
var typeName = node.Type.ToString();
|
||||
|
||||
var targetType = GetType(typeName);
|
||||
|
||||
if (targetType == null)
|
||||
{
|
||||
throw new NotSupportedException("Unsupported cast type: " + node.Type);
|
||||
}
|
||||
|
||||
var operand = Visit(node.Expression);
|
||||
|
||||
return Expression.Convert(operand, targetType);
|
||||
}
|
||||
|
||||
public override Expression VisitImplicitArrayCreationExpression(ImplicitArrayCreationExpressionSyntax node)
|
||||
{
|
||||
var expressions = node.Initializer.Expressions.Select(Visit).ToArray();
|
||||
var elementType = expressions.Length > 0 ? expressions[0].Type : typeof(object);
|
||||
return Expression.NewArrayInit(elementType, expressions);
|
||||
}
|
||||
|
||||
public override Expression VisitArrayCreationExpression(ArrayCreationExpressionSyntax node)
|
||||
{
|
||||
var elementType = GetType(node.Type.ElementType.ToString());
|
||||
|
||||
if (elementType == null)
|
||||
{
|
||||
throw new NotSupportedException("Unsupported array element type: " + node.Type.ElementType);
|
||||
}
|
||||
|
||||
var expressions = node.Initializer.Expressions.Select(e => ConvertIfNeeded(Visit(e), elementType));
|
||||
|
||||
return Expression.NewArrayInit(elementType, expressions);
|
||||
}
|
||||
|
||||
private static MethodCallExpression CallStaticMethod(Type type, string methodName, Expression[] arguments, Type[] argumentTypes)
|
||||
{
|
||||
var methodInfo = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static, argumentTypes);
|
||||
|
||||
if (methodInfo != null)
|
||||
{
|
||||
return Expression.Call(methodInfo, arguments);
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Method not found: " + methodName);
|
||||
}
|
||||
|
||||
public override Expression DefaultVisit(SyntaxNode node)
|
||||
{
|
||||
throw new NotSupportedException("Unsupported syntax: " + node.GetType().Name);
|
||||
}
|
||||
|
||||
public override Expression VisitSimpleLambdaExpression(SimpleLambdaExpressionSyntax node)
|
||||
{
|
||||
var body = Visit(node.Body);
|
||||
|
||||
return Expression.Lambda(body, parameter);
|
||||
}
|
||||
|
||||
private Expression VisitArgument(Expression instance, ArgumentSyntax argument)
|
||||
{
|
||||
if (argument.Expression is SimpleLambdaExpressionSyntax lambda)
|
||||
{
|
||||
var itemType = GetItemType(instance.Type);
|
||||
|
||||
var visitor = new ExpressionSyntaxVisitor(Expression.Parameter(itemType, lambda.Parameter.Identifier.Text), typeLocator);
|
||||
|
||||
return visitor.Visit(lambda);
|
||||
}
|
||||
|
||||
return Visit(argument.Expression);
|
||||
}
|
||||
|
||||
private static Expression ConvertIfNeeded(Expression expression, Type targetType)
|
||||
{
|
||||
if (expression is not LambdaExpression)
|
||||
{
|
||||
return expression.Type == targetType ? expression : Expression.Convert(expression, targetType);
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
public override Expression VisitInvocationExpression(InvocationExpressionSyntax node)
|
||||
{
|
||||
if (node.Expression is MemberAccessExpressionSyntax methodCall)
|
||||
{
|
||||
var instance = Visit(methodCall.Expression);
|
||||
var arguments = node.ArgumentList.Arguments.Select(a => VisitArgument(instance, a)).ToArray();
|
||||
var argumentTypes = arguments.Select(a => a.Type).ToArray();
|
||||
|
||||
if (instance is ConstantExpression constant && constant.Value is Type type)
|
||||
{
|
||||
return CallStaticMethod(type, methodCall.Name.Identifier.Text, arguments, argumentTypes);
|
||||
}
|
||||
|
||||
var instanceType = instance.Type;
|
||||
var methodInfo = instanceType.GetMethod(methodCall.Name.Identifier.Text, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, argumentTypes);
|
||||
|
||||
if (methodInfo == null)
|
||||
{
|
||||
methodInfo = typeof(Enumerable)
|
||||
.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.FirstOrDefault(m => m.Name == methodCall.Name.Identifier.Text && m.GetParameters().Length == arguments.Length + 1);
|
||||
|
||||
if (methodInfo != null)
|
||||
{
|
||||
var argumentType = GetItemType(instanceType);
|
||||
var genericMethod = methodInfo.MakeGenericMethod(argumentType);
|
||||
var parameters = genericMethod.GetParameters();
|
||||
var argumentsWithInstance = new[] { instance }.Concat(arguments).ToArray();
|
||||
|
||||
if (parameters.Length != argumentsWithInstance.Length)
|
||||
{
|
||||
throw new NotSupportedException("Unsupported method call: " + methodCall.Name.Identifier.Text);
|
||||
}
|
||||
|
||||
return Expression.Call(genericMethod, argumentsWithInstance.Select((a, index) => ConvertIfNeeded(a, parameters[index].ParameterType)));
|
||||
}
|
||||
}
|
||||
|
||||
if (methodInfo == null)
|
||||
{
|
||||
throw new NotSupportedException("Unsupported method call: " + methodCall.Name.Identifier.Text);
|
||||
}
|
||||
|
||||
return Expression.Call(instance, methodInfo, arguments);
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Unsupported invocation expression: " + node.ToString());
|
||||
}
|
||||
|
||||
private static Type GetItemType(Type enumerableOrArray)
|
||||
{
|
||||
return enumerableOrArray.IsArray ? enumerableOrArray.GetElementType() : enumerableOrArray.GetGenericArguments()[0];
|
||||
}
|
||||
|
||||
|
||||
private static object ParseLiteral(LiteralExpressionSyntax literal)
|
||||
{
|
||||
return literal.Kind() switch
|
||||
{
|
||||
SyntaxKind.StringLiteralExpression => literal.Token.ValueText,
|
||||
SyntaxKind.NumericLiteralExpression => literal.Token.Value,
|
||||
SyntaxKind.TrueLiteralExpression => true,
|
||||
SyntaxKind.FalseLiteralExpression => false,
|
||||
SyntaxKind.NullLiteralExpression => null,
|
||||
_ => throw new NotSupportedException("Unsupported literal: " + literal),
|
||||
};
|
||||
}
|
||||
|
||||
private static ExpressionType ParseBinaryOperator(SyntaxToken token)
|
||||
{
|
||||
return token.Kind() switch
|
||||
{
|
||||
SyntaxKind.EqualsEqualsToken => ExpressionType.Equal,
|
||||
SyntaxKind.LessThanToken => ExpressionType.LessThan,
|
||||
SyntaxKind.GreaterThanToken => ExpressionType.GreaterThan,
|
||||
SyntaxKind.LessThanEqualsToken => ExpressionType.LessThanOrEqual,
|
||||
SyntaxKind.GreaterThanEqualsToken => ExpressionType.GreaterThanOrEqual,
|
||||
SyntaxKind.ExclamationEqualsToken => ExpressionType.NotEqual,
|
||||
SyntaxKind.AmpersandAmpersandToken => ExpressionType.AndAlso,
|
||||
SyntaxKind.BarBarToken => ExpressionType.OrElse,
|
||||
SyntaxKind.QuestionQuestionToken => ExpressionType.Coalesce,
|
||||
_ => throw new NotSupportedException("Unsupported operator: " + token.Text),
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetPropertyNameFromInitializer(AnonymousObjectMemberDeclaratorSyntax initializer)
|
||||
{
|
||||
if (initializer.NameEquals != null)
|
||||
{
|
||||
return initializer.NameEquals.Name.Identifier.Text;
|
||||
}
|
||||
|
||||
var expression = initializer.Expression;
|
||||
|
||||
if (expression is MemberAccessExpressionSyntax memberAccess)
|
||||
{
|
||||
expression = memberAccess.Name;
|
||||
}
|
||||
|
||||
while (expression is ConditionalAccessExpressionSyntax conditionalAccess)
|
||||
{
|
||||
expression = conditionalAccess.WhenNotNull;
|
||||
}
|
||||
|
||||
if (expression is MemberBindingExpressionSyntax memberBinding)
|
||||
{
|
||||
expression = memberBinding.Name;
|
||||
}
|
||||
|
||||
if (expression is IdentifierNameSyntax identifier)
|
||||
{
|
||||
return identifier.Identifier.Text;
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Unsupported initializer: " + initializer.ToString());
|
||||
}
|
||||
|
||||
public override Expression VisitAnonymousObjectCreationExpression(AnonymousObjectCreationExpressionSyntax node)
|
||||
{
|
||||
var properties = node.Initializers.Select(init =>
|
||||
{
|
||||
var name = GetPropertyNameFromInitializer(init);
|
||||
var value = Visit(init.Expression);
|
||||
return new { Name = name, Value = value };
|
||||
}).ToList();
|
||||
|
||||
var propertyNames = properties.Select(p => p.Name).ToArray();
|
||||
var propertyTypes = properties.Select(p => p.Value.Type).ToArray();
|
||||
var dynamicType = DynamicTypeFactory.CreateType(parameter.Type.Name, propertyNames, propertyTypes);
|
||||
|
||||
var bindings = properties.Select(p => Expression.Bind(dynamicType.GetProperty(p.Name), p.Value));
|
||||
return Expression.MemberInit(Expression.New(dynamicType), bindings);
|
||||
}
|
||||
|
||||
private Expression instance;
|
||||
|
||||
public override Expression VisitConditionalAccessExpression(ConditionalAccessExpressionSyntax node)
|
||||
{
|
||||
var expression = Visit(node.Expression);
|
||||
|
||||
instance = expression;
|
||||
|
||||
var whenNotNull = Visit(node.WhenNotNull);
|
||||
|
||||
instance = null;
|
||||
|
||||
if (expression.Type.IsValueType && Nullable.GetUnderlyingType(expression.Type) == null)
|
||||
{
|
||||
throw new NotSupportedException("Conditional access is not supported on non-nullable value types: " + expression.Type);
|
||||
}
|
||||
|
||||
if (!expression.Type.IsValueType || Nullable.GetUnderlyingType(expression.Type) != null)
|
||||
{
|
||||
return Expression.Condition(Expression.NotEqual(expression, Expression.Constant(null, expression.Type)),
|
||||
whenNotNull, Expression.Default(whenNotNull.Type)
|
||||
);
|
||||
}
|
||||
|
||||
return whenNotNull;
|
||||
}
|
||||
|
||||
public override Expression VisitMemberBindingExpression(MemberBindingExpressionSyntax node)
|
||||
{
|
||||
if (instance is Expression expression)
|
||||
{
|
||||
return Expression.PropertyOrField(expression, node.Name.Identifier.Text);
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Unsupported member binding: " + node.ToString());
|
||||
}
|
||||
|
||||
public override Expression VisitElementAccessExpression(ElementAccessExpressionSyntax node)
|
||||
{
|
||||
var expression = Visit(node.Expression);
|
||||
var arguments = node.ArgumentList.Arguments.Select(arg => Visit(arg.Expression)).ToArray();
|
||||
|
||||
if (expression.Type.IsArray)
|
||||
{
|
||||
return Expression.ArrayIndex(expression, arguments);
|
||||
}
|
||||
|
||||
var indexer = expression.Type.GetProperties()
|
||||
.FirstOrDefault(p => p.GetIndexParameters().Length == arguments.Length);
|
||||
|
||||
if (indexer != null)
|
||||
{
|
||||
return Expression.MakeIndex(expression, indexer, arguments);
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Unsupported element access: " + node.ToString());
|
||||
}
|
||||
|
||||
public override Expression VisitPrefixUnaryExpression(PrefixUnaryExpressionSyntax node)
|
||||
{
|
||||
var operand = Visit(node.Operand);
|
||||
|
||||
return node.OperatorToken.Kind() switch
|
||||
{
|
||||
SyntaxKind.MinusToken => Expression.Negate(operand),
|
||||
SyntaxKind.ExclamationToken => Expression.Not(operand),
|
||||
_ => throw new NotSupportedException("Unsupported unary operator: " + node.OperatorToken.Text),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse lambda expressions from strings.
|
||||
/// </summary>
|
||||
public static class ExpressionParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a lambda expression that returns a boolean value.
|
||||
/// </summary>
|
||||
public static Expression<Func<T, bool>> ParsePredicate<T>(string expression, Func<string, Type> typeLocator = null)
|
||||
{
|
||||
return ParseLambda<T, bool>(expression, typeLocator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a lambda expression that returns a typed result.
|
||||
/// </summary>
|
||||
public static Expression<Func<T, TResult>> ParseLambda<T, TResult>(string expression, Func<string, Type> typeLocator = null)
|
||||
{
|
||||
var (parameter, body) = Parse<T>(expression, typeLocator);
|
||||
|
||||
return Expression.Lambda<Func<T, TResult>>(body, parameter);
|
||||
}
|
||||
|
||||
|
||||
private static (ParameterExpression, Expression) Parse<T>(string expression, Func<string, Type> typeLocator)
|
||||
{
|
||||
var syntaxTree = CSharpSyntaxTree.ParseText(expression);
|
||||
var root = syntaxTree.GetRoot();
|
||||
var lambdaExpression = root.DescendantNodes().OfType<SimpleLambdaExpressionSyntax>().FirstOrDefault();
|
||||
if (lambdaExpression == null)
|
||||
{
|
||||
throw new ArgumentException("Invalid lambda expression.");
|
||||
}
|
||||
var parameter = Expression.Parameter(typeof(T), lambdaExpression.Parameter.Identifier.Text);
|
||||
var visitor = new ExpressionSyntaxVisitor(parameter, typeLocator);
|
||||
var body = visitor.Visit(lambdaExpression.Body);
|
||||
return (parameter, body);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a lambda expression that returns untyped result.
|
||||
/// </summary>
|
||||
public static LambdaExpression ParseLambda<T>(string expression, Func<string, Type> typeLocator = null)
|
||||
{
|
||||
var (parameter, body) = Parse<T>(expression, typeLocator);
|
||||
|
||||
return Expression.Lambda(body, parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a lambda expression that returns untyped result.
|
||||
/// </summary>
|
||||
public static LambdaExpression ParseLambda(string expression, Type type, Func<string, Type> typeLocator = null)
|
||||
{
|
||||
var syntaxTree = CSharpSyntaxTree.ParseText(expression);
|
||||
var root = syntaxTree.GetRoot();
|
||||
var lambdaExpression = root.DescendantNodes().OfType<SimpleLambdaExpressionSyntax>().FirstOrDefault();
|
||||
|
||||
if (lambdaExpression == null)
|
||||
{
|
||||
throw new ArgumentException("Invalid lambda expression.");
|
||||
}
|
||||
|
||||
var parameter = Expression.Parameter(type, lambdaExpression.Parameter.Identifier.Text);
|
||||
var visitor = new ExpressionSyntaxVisitor(parameter, typeLocator);
|
||||
var body = visitor.Visit(lambdaExpression.Body);
|
||||
|
||||
return Expression.Lambda(body, parameter);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
@@ -14,22 +18,57 @@ namespace Radzen.Blazor
|
||||
/// <summary>
|
||||
/// Gets enum description.
|
||||
/// </summary>
|
||||
public static string GetDisplayDescription(this Enum enumValue)
|
||||
public static string GetDisplayDescription(this Enum enumValue, Func<string, string> translationFunction = null)
|
||||
{
|
||||
var enumValueAsString = enumValue.ToString();
|
||||
var val = enumValue.GetType().GetMember(enumValueAsString).FirstOrDefault();
|
||||
var enumVal = val?.GetCustomAttribute<DisplayAttribute>()?.GetDescription() ?? enumValueAsString;
|
||||
|
||||
return val?.GetCustomAttribute<DisplayAttribute>()?.GetDescription() ?? enumValueAsString;
|
||||
if (translationFunction != null)
|
||||
return translationFunction(enumVal);
|
||||
|
||||
return enumVal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts Enum to IEnumerable of Value/Text.
|
||||
/// </summary>
|
||||
public static IEnumerable<object> EnumAsKeyValuePair(Type enumType)
|
||||
public static IEnumerable<object> EnumAsKeyValuePair(Type enumType, Func<string, string> translationFunction = null)
|
||||
{
|
||||
return Enum.GetValues(enumType).Cast<Enum>().Distinct().Select(val => new { Value = Convert.ToInt32(val), Text = val.GetDisplayDescription() });
|
||||
Type underlyingType = Enum.GetUnderlyingType(enumType);
|
||||
return Enum.GetValues(enumType).Cast<Enum>().Distinct().Select(val => new { Value = Convert.ChangeType(val, underlyingType), Text = val.GetDisplayDescription(translationFunction) });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the autocomplete type enum value to the expected
|
||||
/// autocomplete attribute value.
|
||||
/// </summary>
|
||||
/// <returns>The autocomplete attribute string value.</returns>
|
||||
public static string GetAutoCompleteValue(this AutoCompleteType typeValue)
|
||||
{
|
||||
// Handle synonyms.
|
||||
switch (typeValue)
|
||||
{
|
||||
case AutoCompleteType.FirstName:
|
||||
return "given-name";
|
||||
case AutoCompleteType.LastName:
|
||||
return "family-name";
|
||||
case AutoCompleteType.MiddleName:
|
||||
return "additional-name";
|
||||
case AutoCompleteType.ZipCode:
|
||||
return "postal-code";
|
||||
case AutoCompleteType.Province:
|
||||
return "address-level1";
|
||||
case AutoCompleteType.State:
|
||||
return "address-level1";
|
||||
}
|
||||
|
||||
// Handle standard values.
|
||||
var value = typeValue.ToString();
|
||||
value = Regex.Replace(value, "([^A-Z])([A-Z])", "$1-$2");
|
||||
return Regex.Replace(value, "([A-Z]+)([A-Z][^A-Z$])", "$1-$2")
|
||||
.Trim().ToLower();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,80 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Radzen.Blazor;
|
||||
using Radzen.Blazor.Rendering;
|
||||
|
||||
namespace Radzen
|
||||
{
|
||||
/// <summary>
|
||||
/// Class FormComponentWithAutoComplete.
|
||||
/// </summary>
|
||||
public class FormComponentWithAutoComplete<T> : FormComponent<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating the type of built-in autocomplete
|
||||
/// the browser should use.
|
||||
/// <see cref="Blazor.AutoCompleteType" />
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type of built-in autocomplete.
|
||||
/// </value>
|
||||
[Parameter]
|
||||
public virtual AutoCompleteType AutoCompleteType { get; set; } = AutoCompleteType.On;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the autocomplete attribute's string value.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>off</c> if the AutoComplete parameter is false or the
|
||||
/// AutoCompleteType parameter is "off". When the AutoComplete
|
||||
/// parameter is true, the value is <c>on</c> or, if set, the value of
|
||||
/// AutoCompleteType.</value>
|
||||
public virtual string AutoCompleteAttribute
|
||||
{
|
||||
get => Attributes != null && Attributes.ContainsKey("AutoComplete") && $"{Attributes["AutoComplete"]}".ToLower() == "false" ? DefaultAutoCompleteAttribute :
|
||||
Attributes != null && Attributes.ContainsKey("AutoComplete") ? Attributes["AutoComplete"] as string ?? AutoCompleteType.GetAutoCompleteValue() : AutoCompleteType.GetAutoCompleteValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default autocomplete attribute's string value.
|
||||
/// </summary>
|
||||
public virtual string DefaultAutoCompleteAttribute { get; set; } = "off";
|
||||
|
||||
object ariaAutoComplete;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
parameters = parameters.TryGetValue("aria-autocomplete", out ariaAutoComplete) ?
|
||||
ParameterView.FromDictionary(parameters
|
||||
.ToDictionary().Where(i => i.Key != "aria-autocomplete").ToDictionary(i => i.Key, i => i.Value)
|
||||
.ToDictionary(i => i.Key, i => i.Value))
|
||||
: parameters;
|
||||
|
||||
await base.SetParametersAsync(parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default aria-autocomplete attribute's string value.
|
||||
/// </summary>
|
||||
public virtual string DefaultAriaAutoCompleteAttribute { get; set; } = "none";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the aria-autocomplete attribute's string value.
|
||||
/// </summary>
|
||||
public virtual string AriaAutoCompleteAttribute
|
||||
{
|
||||
get => AutoCompleteAttribute == DefaultAutoCompleteAttribute ? DefaultAriaAutoCompleteAttribute : ariaAutoComplete as string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class FormComponent.
|
||||
/// Implements the <see cref="Radzen.RadzenComponent" />
|
||||
@@ -162,6 +228,8 @@ namespace Radzen
|
||||
/// <returns>Task.</returns>
|
||||
public override Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
var disabledChanged = parameters.DidParameterChange(nameof(Disabled), Disabled);
|
||||
|
||||
var result = base.SetParametersAsync(parameters);
|
||||
|
||||
if (EditContext != null && ValueExpression != null && FieldIdentifier.Model != EditContext.Model)
|
||||
@@ -171,6 +239,11 @@ namespace Radzen
|
||||
EditContext.OnValidationStateChanged += ValidationStateChanged;
|
||||
}
|
||||
|
||||
if (disabledChanged)
|
||||
{
|
||||
FormFieldContext?.DisabledChanged(Disabled);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -230,6 +303,20 @@ namespace Radzen
|
||||
/// <returns>ClassList.</returns>
|
||||
protected ClassList GetClassList(string className) => ClassList.Create(className)
|
||||
.AddDisabled(Disabled)
|
||||
.Add(FieldIdentifier, EditContext);
|
||||
.Add(FieldIdentifier, EditContext)
|
||||
.Add("rz-state-empty", !HasValue);
|
||||
|
||||
/// <summary> Provides support for RadzenFormField integration. </summary>
|
||||
[CascadingParameter]
|
||||
public IFormFieldContext FormFieldContext { get; set; }
|
||||
|
||||
/// <summary> Gets the current placeholder. Returns empty string if this component is inside a RadzenFormField.</summary>
|
||||
protected string CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask FocusAsync()
|
||||
{
|
||||
await Element.FocusAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,14 +50,23 @@ namespace Radzen.Blazor
|
||||
/// <param name="valueScale">The value scale.</param>
|
||||
/// <returns>RenderFragment.</returns>
|
||||
RenderFragment RenderOverlays(ScaleBase categoryScale, ScaleBase valueScale);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the series tooltip.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="marginLeft">The left margin.</param>
|
||||
/// <param name="marginTop">The right margin.</param>
|
||||
/// <returns>RenderFragment.</returns>
|
||||
RenderFragment RenderTooltip(object data, double marginLeft, double marginTop);
|
||||
RenderFragment RenderTooltip(object data);
|
||||
/// <summary>
|
||||
/// Renders a tooltip item with the specified data to be displayed in a shared tooltip
|
||||
/// </summary>
|
||||
RenderFragment RenderSharedTooltipItem(object category);
|
||||
/// <summary>
|
||||
/// Get position of the series tooltip.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>Position.</returns>
|
||||
Point GetTooltipPosition(object data);
|
||||
/// <summary>
|
||||
/// Renders the legend item.
|
||||
/// </summary>
|
||||
@@ -96,7 +105,7 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
object DataAt(double x, double y);
|
||||
(object, Point) DataAt(double x, double y);
|
||||
/// <summary>
|
||||
/// Returns data chart position
|
||||
/// </summary>
|
||||
|
||||
@@ -28,6 +28,12 @@ namespace Radzen.Blazor
|
||||
/// <summary>
|
||||
/// Renders tooltip
|
||||
/// </summary>
|
||||
RenderFragment RenderTooltip(double mouseX, double mouseY, double marginLeft, double marginTop);
|
||||
RenderFragment RenderTooltip(double mouseX, double mouseY);
|
||||
|
||||
/// <summary>
|
||||
/// Get position of the overlay tooltip.
|
||||
/// </summary>
|
||||
/// <returns>Position.</returns>
|
||||
Point GetTooltipPosition(double mouseX, double mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
26
Radzen.Blazor/IChartStackedAreaSeries.cs
Normal file
26
Radzen.Blazor/IChartStackedAreaSeries.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for <see cref="RadzenStackedAreaSeries{TItem}" />.
|
||||
/// </summary>
|
||||
public interface IChartStackedAreaSeries
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the count.
|
||||
/// </summary>
|
||||
/// <value>The count.</value>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values for category.
|
||||
/// </summary>
|
||||
IEnumerable<double> ValuesForCategory(double category);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value at the specified index.
|
||||
/// </summary>
|
||||
double ValueAt(int index);
|
||||
}
|
||||
}
|
||||
26
Radzen.Blazor/IChartStackedBarSeries.cs
Normal file
26
Radzen.Blazor/IChartStackedBarSeries.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for <see cref="RadzenStackedBarSeries{TItem}" />.
|
||||
/// </summary>
|
||||
public interface IChartStackedBarSeries : IChartBarSeries
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the value at the specified index.
|
||||
/// </summary>
|
||||
double ValueAt(int index);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values for category.
|
||||
/// </summary>
|
||||
IEnumerable<double> ValuesForCategory(double category);
|
||||
/// <summary>
|
||||
/// Gets the items for category.
|
||||
/// </summary>
|
||||
/// <param name="category"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<object> ItemsForCategory(double category);
|
||||
}
|
||||
}
|
||||
33
Radzen.Blazor/IChartStackedColumnSeries.cs
Normal file
33
Radzen.Blazor/IChartStackedColumnSeries.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for <see cref="RadzenStackedColumnSeries{TItem}" />.
|
||||
/// </summary>
|
||||
public interface IChartStackedColumnSeries
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the count.
|
||||
/// </summary>
|
||||
/// <value>The count.</value>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values for category.
|
||||
/// </summary>
|
||||
IEnumerable<double> ValuesForCategory(double category);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items for category.
|
||||
/// </summary>
|
||||
/// <param name="category"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<object> ItemsForCategory(double category);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value at the specified index.
|
||||
/// </summary>
|
||||
double ValueAt(int index);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,11 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
public interface IScheduler
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the appointment move event callback.
|
||||
/// </summary>
|
||||
/// <value>The appointment move event callback.</value>
|
||||
EventCallback<SchedulerAppointmentMoveEventArgs> AppointmentMove { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the appointments in the specified range.
|
||||
/// </summary>
|
||||
@@ -59,6 +64,32 @@ namespace Radzen.Blazor
|
||||
/// <param name="end">The end.</param>
|
||||
Task SelectSlot(DateTime start, DateTime end);
|
||||
/// <summary>
|
||||
/// Selects the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="start">The start.</param>
|
||||
/// <param name="end">The end.</param>
|
||||
/// <param name="appointments">The appointments for this range.</param>
|
||||
Task<bool> SelectSlot(DateTime start, DateTime end, IEnumerable<AppointmentData> appointments);
|
||||
/// <summary>
|
||||
/// Selects the specified month.
|
||||
/// </summary>
|
||||
/// <param name="monthStart">The start of the month.</param>
|
||||
/// <param name="appointments">The appointments for this range.</param>
|
||||
Task SelectMonth(DateTime monthStart, IEnumerable<AppointmentData> appointments);
|
||||
/// <summary>
|
||||
/// Selects the specified day.
|
||||
/// </summary>
|
||||
/// <param name="day">The selected day.</param>
|
||||
/// <param name="appointments">The appointments for this range.</param>
|
||||
Task SelectDay(DateTime day, IEnumerable<AppointmentData> appointments);
|
||||
/// <summary>
|
||||
/// Selects the specified more link.
|
||||
/// </summary>
|
||||
/// <param name="start">The start.</param>
|
||||
/// <param name="end">The end.</param>
|
||||
/// <param name="appointments">The appointments for this range.</param>
|
||||
Task<bool> SelectMore(DateTime start, DateTime end, IEnumerable<AppointmentData> appointments);
|
||||
/// <summary>
|
||||
/// Gets the appointment HTML attributes.
|
||||
/// </summary>
|
||||
/// <param name="item">The appointment.</param>
|
||||
@@ -77,6 +108,31 @@ namespace Radzen.Blazor
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>RenderFragment.</returns>
|
||||
RenderFragment RenderAppointment(AppointmentData item);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the scheduler that the user has moved the mouse over the specified appointment.
|
||||
/// </summary>
|
||||
/// <param name="reference"></param>
|
||||
/// <param name="data"></param>
|
||||
Task MouseEnterAppointment(ElementReference reference, AppointmentData data);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the scheduler has a mouse enter appointment listener.
|
||||
/// </summary>
|
||||
bool HasMouseEnterAppointmentDelegate();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the scheduler has an AppointmentMove listener.
|
||||
/// </summary>
|
||||
bool HasAppointmentMoveDelegate();
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the scheduler that the user has moved the mouse out of the specified appointment.
|
||||
/// </summary>
|
||||
/// <param name="reference"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
Task MouseLeaveAppointment(ElementReference reference, AppointmentData data);
|
||||
/// <summary>
|
||||
/// Reloads this instance.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
@@ -49,5 +50,11 @@ namespace Radzen.Blazor
|
||||
/// Gets the end date.
|
||||
/// </summary>
|
||||
DateTime EndDate { get; }
|
||||
/// <summary>
|
||||
/// Handles appointent move event.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
Task OnAppointmentMove(SchedulerAppointmentMoveEventArgs data);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2022 Radzen Ltd
|
||||
Copyright (c) 2018-2025 Radzen Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Radzen.Blazor
|
||||
|
||||
protected virtual double CalculateTickCount(int distance)
|
||||
{
|
||||
return Math.Ceiling(Math.Abs(Output.End - Output.Start) / distance);
|
||||
return Math.Max(1, Math.Ceiling(Math.Abs(Output.End - Output.Start) / distance));
|
||||
}
|
||||
|
||||
public override (double Start, double End, double Step) Ticks(int distance)
|
||||
@@ -88,11 +88,6 @@ namespace Radzen.Blazor
|
||||
if (Step is IConvertible)
|
||||
{
|
||||
step = Convert.ToDouble(Step);
|
||||
|
||||
if (step <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Step must be greater than zero");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +97,7 @@ namespace Radzen.Blazor
|
||||
end = Math.Ceiling(end / step) * step;
|
||||
}
|
||||
|
||||
if (!Double.IsFinite(Input.Start) && !Double.IsFinite(Input.End))
|
||||
if (!double.IsFinite(Input.Start) || !double.IsFinite(Input.End))
|
||||
{
|
||||
Input.Start = start = 0;
|
||||
Input.End = end = 2;
|
||||
@@ -110,7 +105,7 @@ namespace Radzen.Blazor
|
||||
Round = false;
|
||||
}
|
||||
|
||||
if (!Double.IsFinite(start) && !Double.IsFinite(end))
|
||||
if (!double.IsFinite(start) || !double.IsFinite(end))
|
||||
{
|
||||
Input.Start = start = 0;
|
||||
Input.End = end = 2;
|
||||
@@ -118,7 +113,12 @@ namespace Radzen.Blazor
|
||||
Round = false;
|
||||
}
|
||||
|
||||
return (start, end, step);
|
||||
if (step == 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Step must be non-zero");
|
||||
}
|
||||
|
||||
return (start, end, Math.Abs(step));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,4 @@
|
||||
<assembly fullname="System.Core">
|
||||
<type fullname="System.Linq.Queryable" preserve="all" />
|
||||
</assembly>
|
||||
<assembly fullname="System.Linq.Dynamic.Core" />
|
||||
</linker>
|
||||
@@ -8,7 +8,7 @@ using System.Text;
|
||||
namespace Radzen
|
||||
{
|
||||
/// <summary>
|
||||
/// Class NotificationService. Contains various methods with options to open notifications.
|
||||
/// Class NotificationService. Contains various methods with options to open notifications.
|
||||
/// Should be added as scoped service in the application services and RadzenNotification should be added in application main layout.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
@@ -31,35 +31,43 @@ namespace Radzen
|
||||
/// <param name="message">The message.</param>
|
||||
public void Notify(NotificationMessage message)
|
||||
{
|
||||
var newMessage = new NotificationMessage()
|
||||
if (message is null)
|
||||
{
|
||||
Duration = message != null && message.Duration.HasValue ? message.Duration : 3000,
|
||||
Severity = message.Severity,
|
||||
Summary = message.Summary,
|
||||
Detail = message.Detail,
|
||||
Style = message.Style,
|
||||
Click = message.Click,
|
||||
CloseOnClick = message.CloseOnClick,
|
||||
Payload = message.Payload
|
||||
};
|
||||
throw new ArgumentNullException(nameof(message));
|
||||
}
|
||||
|
||||
if (!Messages.Contains(newMessage))
|
||||
if (!Messages.Contains(message))
|
||||
{
|
||||
Messages.Add(newMessage);
|
||||
Messages.Add(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the specified severity.
|
||||
/// </summary>
|
||||
/// <param name="severity">The severity.</param>
|
||||
/// <param name="summary">The summary.</param>
|
||||
/// <param name="detail">The detail.</param>
|
||||
/// <param name="duration">The duration.</param>
|
||||
/// <param name="click">The click event.</param>
|
||||
/// <param name="closeOnClick">If true, then the notification will be closed when clicked on.</param>
|
||||
/// <param name="payload">Used to store a custom payload that can be retreived later in the click event handler.</param>
|
||||
public void Notify(NotificationSeverity severity = NotificationSeverity.Info, string summary = "", string detail = "", double duration = 3000, Action<NotificationMessage> click = null, bool closeOnClick = false, object payload = null)
|
||||
/// <summary>
|
||||
/// Notifies the specified severity.
|
||||
/// </summary>
|
||||
/// <param name="severity">The severity.</param>
|
||||
/// <param name="summary">The summary.</param>
|
||||
/// <param name="detail">The detail.</param>
|
||||
/// <param name="duration">The duration.</param>
|
||||
/// <param name="click">The click event.</param>
|
||||
public void Notify(NotificationSeverity severity, string summary,
|
||||
string detail, TimeSpan duration, Action<NotificationMessage> click = null)
|
||||
{
|
||||
Notify(severity, summary, detail, duration.TotalMilliseconds, click);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the specified severity.
|
||||
/// </summary>
|
||||
/// <param name="severity">The severity.</param>
|
||||
/// <param name="summary">The summary.</param>
|
||||
/// <param name="detail">The detail.</param>
|
||||
/// <param name="duration">The duration, default of 3 seconds.</param>
|
||||
/// <param name="click">The click event.</param>
|
||||
/// <param name="closeOnClick">If true, then the notification will be closed when clicked on.</param>
|
||||
/// <param name="payload">Used to store a custom payload that can be retreived later in the click event handler.</param>
|
||||
/// <param name="close">Action to be executed on close.</param>
|
||||
public void Notify(NotificationSeverity severity = NotificationSeverity.Info, string summary = "", string detail = "", double duration = 3000, Action<NotificationMessage> click = null, bool closeOnClick = false, object payload = null, Action<NotificationMessage> close = null)
|
||||
{
|
||||
var newMessage = new NotificationMessage()
|
||||
{
|
||||
@@ -68,6 +76,7 @@ namespace Radzen
|
||||
Summary = summary,
|
||||
Detail = detail,
|
||||
Click = click,
|
||||
Close = close,
|
||||
CloseOnClick = closeOnClick,
|
||||
Payload = payload
|
||||
};
|
||||
@@ -82,39 +91,43 @@ namespace Radzen
|
||||
/// <summary>
|
||||
/// Class NotificationMessage.
|
||||
/// </summary>
|
||||
public class NotificationMessage
|
||||
public class NotificationMessage : IEquatable<NotificationMessage>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the duration.
|
||||
/// </summary>
|
||||
/// <value>The duration.</value>
|
||||
public double? Duration { get; set; }
|
||||
public double? Duration { get; set; } = 3000;
|
||||
/// <summary>
|
||||
/// Gets or sets the severity.
|
||||
/// </summary>
|
||||
/// <value>The severity.</value>
|
||||
public NotificationSeverity Severity { get; set; }
|
||||
public NotificationSeverity Severity { get; set; } = NotificationSeverity.Info;
|
||||
/// <summary>
|
||||
/// Gets or sets the summary.
|
||||
/// </summary>
|
||||
/// <value>The summary.</value>
|
||||
public string Summary { get; set; }
|
||||
public string Summary { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the detail.
|
||||
/// </summary>
|
||||
/// <value>The detail.</value>
|
||||
public string Detail { get; set; }
|
||||
public string Detail { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the style.
|
||||
/// </summary>
|
||||
/// <value>The style.</value>
|
||||
public string Style { get; set; }
|
||||
public string Style { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the click event.
|
||||
/// </summary>
|
||||
/// <value>This event handler is called when the notification is clicked on.</value>
|
||||
public Action<NotificationMessage> Click { get; set; }
|
||||
/// <summary>
|
||||
/// Get or set the event for when the notification is closed
|
||||
/// </summary>
|
||||
public Action<NotificationMessage> Close { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets click on close action.
|
||||
/// </summary>
|
||||
/// <value>If true, then the notification will be closed when clicked on.</value>
|
||||
@@ -124,5 +137,96 @@ namespace Radzen
|
||||
/// </summary>
|
||||
/// <value>Used to store a custom payload that can be retreived later in the click event handler.</value>
|
||||
public object Payload { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if progress should be shown during duration.
|
||||
/// </summary>
|
||||
/// <value>If true, then the progress will be shown during duration.</value>
|
||||
public bool ShowProgress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the detail content.
|
||||
/// </summary>
|
||||
/// <value>The detail content.</value>
|
||||
public RenderFragment<NotificationService> DetailContent { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the summary content.
|
||||
/// </summary>
|
||||
/// <value>The summary content.</value>
|
||||
public RenderFragment<NotificationService> SummaryContent { get; set; }
|
||||
|
||||
|
||||
#region Implementation of IEquatable<NotificationMessage> and operators overloading
|
||||
|
||||
/// <summary>
|
||||
/// Check if NotificationMessage instance is equal to current instance.
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public bool Equals(NotificationMessage other)
|
||||
{
|
||||
if(other == null) return false;
|
||||
|
||||
if(object.ReferenceEquals(this, other)) return true;
|
||||
|
||||
return this.Severity == other.Severity
|
||||
&& this.Summary == other.Summary
|
||||
&& this.Detail == other.Detail
|
||||
&& this.Duration == other.Duration
|
||||
&& this.Style == other.Style
|
||||
&& this.Click == other.Click
|
||||
&& this.Close == other.Close
|
||||
&& this.CloseOnClick == other.CloseOnClick
|
||||
&& this.Payload == other.Payload
|
||||
&& this.DetailContent == other.DetailContent
|
||||
&& this.SummaryContent == other.SummaryContent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if object instance is equal to current instance.
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public override bool Equals(object obj) => Equals(obj as NotificationMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Return a hash code for the current object
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetHashCode() => (Summary, Detail, Duration, Style, Click, Close, CloseOnClick, Payload, SummaryContent, DetailContent).GetHashCode();
|
||||
|
||||
/// <summary>
|
||||
/// Overloading == operator for NotificationMessage.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="otherMessage"></param>
|
||||
/// <returns></returns>
|
||||
public static bool operator ==(NotificationMessage message, NotificationMessage otherMessage)
|
||||
{
|
||||
if (message is null)
|
||||
{
|
||||
if (otherMessage is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return message.Equals(otherMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overloading != operator for NotificationMessage.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="otherMessage"></param>
|
||||
/// <returns></returns>
|
||||
public static bool operator !=(NotificationMessage message, NotificationMessage otherMessage)
|
||||
{
|
||||
return !(message == otherMessage);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Web;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Radzen
|
||||
{
|
||||
@@ -211,7 +212,7 @@ namespace Radzen
|
||||
/// <param name="options">The options.</param>
|
||||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));
|
||||
writer.WriteStringValue(value.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,9 +231,10 @@ namespace Radzen
|
||||
/// <param name="orderby">The orderby.</param>
|
||||
/// <param name="expand">The expand.</param>
|
||||
/// <param name="select">The select.</param>
|
||||
/// <param name="apply">The apply.</param>
|
||||
/// <param name="count">if set to <c>true</c> [count].</param>
|
||||
/// <returns>Uri.</returns>
|
||||
public static Uri GetODataUri(this Uri uri, string filter = null, int? top = null, int? skip = null, string orderby = null, string expand = null, string select = null, bool? count = null)
|
||||
public static Uri GetODataUri(this Uri uri, string filter = null, int? top = null, int? skip = null, string orderby = null, string expand = null, string select = null, string apply = null, bool? count = null)
|
||||
{
|
||||
var uriBuilder = new UriBuilder(uri);
|
||||
var queryString = HttpUtility.ParseQueryString(uriBuilder.Query);
|
||||
@@ -267,6 +269,11 @@ namespace Radzen
|
||||
queryString["$select"] = $"{select}";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(apply))
|
||||
{
|
||||
queryString["$apply"] = $"{apply}";
|
||||
}
|
||||
|
||||
if (count != null)
|
||||
{
|
||||
queryString["$count"] = $"{count}".ToLower();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Radzen.Blazor;
|
||||
@@ -35,6 +35,12 @@ namespace Radzen
|
||||
[Parameter]
|
||||
public HorizontalAlign PagerHorizontalAlign { get; set; } = HorizontalAlign.Justify;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating pager density.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Density Density { get; set; } = Density.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether paging is allowed. Set to <c>false</c> by default.
|
||||
/// </summary>
|
||||
@@ -56,10 +62,24 @@ namespace Radzen
|
||||
}
|
||||
set
|
||||
{
|
||||
_PageSize = value;
|
||||
if (_PageSize != value)
|
||||
{
|
||||
_PageSize = value;
|
||||
InvokeAsync(() => OnPageSizeChanged(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal int GetPageSize()
|
||||
{
|
||||
return _PageSize;
|
||||
}
|
||||
|
||||
internal void SetPageSize(int value)
|
||||
{
|
||||
_PageSize = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the page numbers count.
|
||||
/// </summary>
|
||||
@@ -86,6 +106,13 @@ namespace Radzen
|
||||
[Parameter]
|
||||
public RenderFragment<T> Template { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the loading template.
|
||||
/// </summary>
|
||||
/// <value>The loading template.</value>
|
||||
[Parameter]
|
||||
public RenderFragment LoadingTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The data
|
||||
/// </summary>
|
||||
@@ -107,12 +134,37 @@ namespace Radzen
|
||||
if (_data != value)
|
||||
{
|
||||
_data = value;
|
||||
|
||||
if (_data != null && _data is INotifyCollectionChanged)
|
||||
{
|
||||
((INotifyCollectionChanged)_data).CollectionChanged += OnCollectionChanged;
|
||||
}
|
||||
|
||||
OnDataChanged();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when INotifyCollectionChanged CollectionChanged is raised.
|
||||
/// </summary>
|
||||
protected virtual void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
||||
if (_data != null && _data is INotifyCollectionChanged)
|
||||
{
|
||||
((INotifyCollectionChanged)_data).CollectionChanged -= OnCollectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the page size options.
|
||||
/// </summary>
|
||||
@@ -141,6 +193,78 @@ namespace Radzen
|
||||
[Parameter]
|
||||
public string PagingSummaryFormat { get; set; } = "Page {0} of {1} ({2} items)";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's first page button's title attribute.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string FirstPageTitle { get; set; } = "First page.";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's first page button's aria-label attribute.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string FirstPageAriaLabel { get; set; } = "Go to first page.";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's optional previous page button's label text.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string PrevPageLabel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's previous page button's title attribute.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string PrevPageTitle { get; set; } = "Previous page";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's previous page button's aria-label attribute.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string PrevPageAriaLabel { get; set; } = "Go to previous page.";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's last page button's title attribute.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string LastPageTitle { get; set; } = "Last page";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's last page button's aria-label attribute.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string LastPageAriaLabel { get; set; } = "Go to last page.";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's optional next page button's label text.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string NextPageLabel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's next page button's title attribute.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string NextPageTitle { get; set; } = "Next page";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's next page button's aria-label attribute.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string NextPageAriaLabel { get; set; } = "Go to next page.";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's numeric page number buttons' title attributes.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string PageTitleFormat { get; set; } = "Page {0}";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager's numeric page number buttons' aria-label attributes.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string PageAriaLabelFormat { get; set; } = "Go to page {0}.";
|
||||
|
||||
internal IQueryable<T> _view = null;
|
||||
/// <summary>
|
||||
/// Gets the paged view.
|
||||
@@ -229,18 +353,18 @@ namespace Radzen
|
||||
/// Called when [parameters set asynchronous].
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
protected override Task OnParametersSetAsync()
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (Visible && !LoadData.HasDelegate)
|
||||
{
|
||||
InvokeAsync(Reload);
|
||||
await InvokeAsync(Reload);
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculatePager();
|
||||
}
|
||||
|
||||
return base.OnParametersSetAsync();
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -316,8 +440,11 @@ namespace Radzen
|
||||
/// <param name="value">The value.</param>
|
||||
protected virtual async Task OnPageSizeChanged(int value)
|
||||
{
|
||||
pageSize = value;
|
||||
await InvokeAsync(Reload);
|
||||
if (pageSize != value && !this.firstRender)
|
||||
{
|
||||
pageSize = value;
|
||||
await InvokeAsync(Reload);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
189
Radzen.Blazor/QueryStringThemeService.cs
Normal file
189
Radzen.Blazor/QueryStringThemeService.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Radzen
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for the <see cref="QueryStringThemeService" />.
|
||||
/// </summary>
|
||||
public class QueryStringThemeServiceOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the query string parameter for the theme.
|
||||
/// </summary>
|
||||
public string ThemeParameter { get; set; } = "theme";
|
||||
/// <summary>
|
||||
/// Gets or sets the query string parameter for the wcag compatible color theme.
|
||||
/// </summary>
|
||||
public string WcagParameter { get; set; } = "wcag";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the query string parameter for the right to left theme.
|
||||
/// </summary>
|
||||
public string RightToLeftParameter { get; set; } = "rtl";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persist the current theme in the query string. Requires <see cref="ThemeService" /> to be registered in the DI container.
|
||||
/// </summary>
|
||||
public class QueryStringThemeService : IDisposable
|
||||
{
|
||||
private readonly NavigationManager navigationManager;
|
||||
private readonly ThemeService themeService;
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
private readonly IDisposable registration;
|
||||
#endif
|
||||
private readonly QueryStringThemeServiceOptions options;
|
||||
private readonly PropertyInfo hasAttachedJSRuntimeProperty;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QueryStringThemeService" /> class.
|
||||
/// </summary>
|
||||
public QueryStringThemeService(NavigationManager navigationManager, ThemeService themeService, IOptions<QueryStringThemeServiceOptions> options)
|
||||
{
|
||||
this.navigationManager = navigationManager;
|
||||
|
||||
this.themeService = themeService;
|
||||
|
||||
this.options = options.Value;
|
||||
|
||||
hasAttachedJSRuntimeProperty = navigationManager.GetType().GetProperty("HasAttachedJSRuntime");
|
||||
|
||||
var state = GetStateFromQueryString(navigationManager.Uri);
|
||||
|
||||
if (state.theme != null && RequiresChange(state))
|
||||
{
|
||||
themeService.SetTheme(new ThemeOptions
|
||||
{
|
||||
Theme = state.theme,
|
||||
Wcag = state.wcag,
|
||||
RightToLeft = state.rightToLeft,
|
||||
TriggerChange = true
|
||||
});
|
||||
}
|
||||
|
||||
themeService.ThemeChanged += OnThemeChanged;
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
try
|
||||
{
|
||||
registration = navigationManager.RegisterLocationChangingHandler(OnLocationChanging);
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
// HttpNavigationManager does not support that
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool RequiresChange((string theme, bool? wcag, bool? rightToLeft) state) =>
|
||||
(state.theme != null && !string.Equals(themeService.Theme, state.theme, StringComparison.OrdinalIgnoreCase)) ||
|
||||
themeService.Wcag != state.wcag || themeService.RightToLeft != state.rightToLeft;
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
private ValueTask OnLocationChanging(LocationChangingContext context)
|
||||
{
|
||||
var state = GetStateFromQueryString(context.TargetLocation);
|
||||
|
||||
if (RequiresChange(state))
|
||||
{
|
||||
context.PreventNavigation();
|
||||
|
||||
navigationManager.NavigateTo(GetUriWithStateQueryParameters(context.TargetLocation), replace: true);
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
#endif
|
||||
|
||||
private (string theme, bool? wcag, bool? rightToLeft) GetStateFromQueryString(string uri)
|
||||
{
|
||||
var queryString = uri.Contains('?') ? uri[(uri.IndexOf('?') + 1)..] : string.Empty;
|
||||
|
||||
var query = HttpUtility.ParseQueryString(queryString.Contains('#') ? queryString[..queryString.IndexOf('#')] : queryString);
|
||||
|
||||
bool? wcag = query.Get(options.WcagParameter) != null ? query.Get(options.WcagParameter) == "true" : null;
|
||||
bool? rtl = query.Get(options.RightToLeftParameter) != null ? query.Get(options.RightToLeftParameter) == "true" : null;
|
||||
|
||||
return (query.Get(options.ThemeParameter), wcag, rtl);
|
||||
}
|
||||
|
||||
private string GetUriWithStateQueryParameters(string uri)
|
||||
{
|
||||
var parameters = new Dictionary<string, object>
|
||||
{
|
||||
{ options.ThemeParameter, themeService.Theme.ToLowerInvariant() },
|
||||
};
|
||||
|
||||
if (themeService.Wcag.HasValue)
|
||||
{
|
||||
parameters.Add(options.WcagParameter, themeService.Wcag.Value ? "true" : "false");
|
||||
}
|
||||
|
||||
if (themeService.RightToLeft.HasValue)
|
||||
{
|
||||
parameters.Add(options.RightToLeftParameter, themeService.RightToLeft.Value ? "true" : "false");
|
||||
}
|
||||
|
||||
return navigationManager.GetUriWithQueryParameters(uri, parameters);
|
||||
}
|
||||
|
||||
private void OnThemeChanged()
|
||||
{
|
||||
|
||||
if (hasAttachedJSRuntimeProperty is null || hasAttachedJSRuntimeProperty.GetValue(navigationManager) is true)
|
||||
{
|
||||
var state = GetStateFromQueryString(navigationManager.Uri);
|
||||
|
||||
navigationManager.NavigateTo(GetUriWithStateQueryParameters(navigationManager.Uri),
|
||||
forceLoad: state.rightToLeft != themeService.RightToLeft);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
themeService.ThemeChanged -= OnThemeChanged;
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
registration?.Dispose();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods to register the <see cref="QueryStringThemeService" />.
|
||||
/// </summary>
|
||||
public static class QueryStringThemeServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the <see cref="QueryStringThemeService" /> to the service collection.
|
||||
/// </summary>
|
||||
public static IServiceCollection AddRadzenQueryStringThemeService(this IServiceCollection services)
|
||||
{
|
||||
services.AddOptions<QueryStringThemeServiceOptions>();
|
||||
services.AddScoped<QueryStringThemeService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <see cref="QueryStringThemeService" /> to the service collection with the specified condiguration.
|
||||
/// </summary>
|
||||
public static IServiceCollection AddRadzenQueryStringThemeService(this IServiceCollection services, Action<QueryStringThemeServiceOptions> configure)
|
||||
{
|
||||
services.Configure(configure);
|
||||
services.AddScoped<QueryStringThemeService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,105 +0,0 @@
|
||||
## Radzen Blazor is a set of 70+ free native Blazor UI controls packed with DataGrid, Scheduler, Charts and robust theming including Material design and Fluent UI.
|
||||
|
||||

|
||||
|
||||
## Why choose Radzen Blazor Components?
|
||||
|
||||
### :sparkles: Free
|
||||
|
||||
Radzen Blazor Components are open source and free for commercial use. You can install them from [nuget](https://www.nuget.org/packages/Radzen.Blazor) or build your own copy from source.
|
||||
|
||||
Paid support is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
|
||||
|
||||
### :computer: Native
|
||||
|
||||
The components are implemented in C# and take full advantage of the Blazor framework. They do not depend on or wrap existing JavaScript frameworks or libraries.
|
||||
|
||||
Blazor Server and Blazor WebAssembly are fully supported.
|
||||
|
||||
### :seedling: Growing
|
||||
|
||||
We add new components and features on a regular basis.
|
||||
|
||||
Short development cycle. We release as soon as new stuff is available. No more quarterly releases.
|
||||
|
||||
## Support exceeding your expectations
|
||||
|
||||
### :speech_balloon: Community Support
|
||||
Everybody is welcome to visit the [Radzen Community forum](https://forum.radzen.com/). Join the growing community and participate in the discussions!
|
||||
|
||||
### :dart: Dedicated Support
|
||||
|
||||
The Radzen team monitors the forum threads, but does not guarantee a response to every question. For guaranteed responses you may consider the dedicated support option.
|
||||
|
||||
Dedicated support for the Radzen Blazor Components is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
|
||||
|
||||
Our flagship product [Radzen Blazor Studio](https://www.radzen.com/blazor-studio/) provides tons of productivity features for Blazor developers:
|
||||
- An industry-leading WYSIWYG Blazor design time canvas
|
||||
- Scaffolding a complete CRUD applications from a database
|
||||
- Built-in security - authentication and authorization
|
||||
- Visual Studio Code and Professional support
|
||||
- Deployment to IIS and Azure
|
||||
- Dedicated support with 24 hour guaranteed response time
|
||||
|
||||
## Get started with Radzen Blazor Components
|
||||
|
||||
### 1. Install
|
||||
|
||||
Radzen Blazor Components are distributed as a [Radzen.Blazor nuget package](https://www.nuget.org/packages/Radzen.Blazor). You can add them to your project in one of the following ways
|
||||
- Install the package from command line by running `dotnet add package Radzen.Blazor`
|
||||
- Add the project from the Visual Nuget Package Manager
|
||||
- Manually edit the .csproj file and add a project reference
|
||||
|
||||
### 2. Import the namespace
|
||||
|
||||
Open the `_Imports.razor` file of your Blazor application and add this line `@using Radzen.Blazor`.
|
||||
|
||||
### 3. Include a theme
|
||||
Radzen Blazor components come with five free themes: Material, Standard, Default, Dark, Software and Humanistic.
|
||||
|
||||
To use a theme
|
||||
1. Pick a theme. The [online demos](https://blazor.radzen.com/colors) allow you to preview the available options via the theme dropdown located in the header. The Material theme is currently selected by default.
|
||||
1. Include the theme CSS file in your Blazor application. Open `Pages\_Layout.cshtml` (Blazor Server .NET 6), `Pages\_Host.cshtml` (Blazor Server .NET 7) or `wwwroot/index.html` (Blazor WebAssembly) and include a theme CSS file by adding this snippet
|
||||
```html
|
||||
<link rel="stylesheet" href="_content/Radzen.Blazor/css/material-base.css">
|
||||
```
|
||||
|
||||
To include a different theme (i.e. Standard) just change the name of the CSS file:
|
||||
```
|
||||
<link rel="stylesheet" href="_content/Radzen.Blazor/css/standard-base.css">
|
||||
```
|
||||
|
||||
### 4. Include Radzen.Blazor.js
|
||||
|
||||
Open `Pages\_Layout.cshtml` (Blazor Server .NET 6), `Pages\_Host.cshtml` (Blazor Server .NET 7) or `wwwroot/index.html` (Blazor WebAssembly) and include this snippet:
|
||||
|
||||
```html
|
||||
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
|
||||
```
|
||||
|
||||
### 5. Use a component
|
||||
Use any Radzen Blazor component by typing its tag name in a Blazor page e.g.
|
||||
```html
|
||||
<RadzenButton Text="Hi"></RadzenButton>
|
||||
```
|
||||
|
||||
#### Data-binding a property
|
||||
```razor
|
||||
<RadzenButton Text=@text />
|
||||
<RadzenTextBox @bind-Value=@text />
|
||||
@code {
|
||||
string text = "Hi";
|
||||
}
|
||||
```
|
||||
|
||||
#### Handing events
|
||||
|
||||
```razor
|
||||
<RadzenButton Click="@ButtonClicked" Text="Hi"></RadzenButton>
|
||||
@code {
|
||||
void ButtonClicked()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,44 +1,45 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
<PropertyGroup>
|
||||
<NoWarn>BL9993</NoWarn>
|
||||
<TargetFrameworks>netstandard2.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<NoWarn>BL9993;BL0007;BL0005</NoWarn>
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<RazorLangVersion>7.0</RazorLangVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<OutputType>Library</OutputType>
|
||||
<IsPackable>true</IsPackable>
|
||||
<PackageId>Radzen.Blazor</PackageId>
|
||||
<Product>Radzen.Blazor</Product>
|
||||
<Version>4.4.3</Version>
|
||||
<Version>6.2.1</Version>
|
||||
<Copyright>Radzen Ltd.</Copyright>
|
||||
<Authors>Radzen Ltd.</Authors>
|
||||
<Description>Radzen Blazor is a set of 70+ free native Blazor UI controls packed with DataGrid, Scheduler, Charts and robust theming including Material design and Fluent UI.</Description>
|
||||
<Description>Radzen Blazor is a set of 90+ free native Blazor UI controls packed with DataGrid, Scheduler, Charts and robust theming including Material design and Fluent UI.</Description>
|
||||
<PackageTags>blazor material design fluent fluentui components datagrid scheduler charts</PackageTags>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageProjectUrl>https://www.radzen.com</PackageProjectUrl>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<Title>Radzen Components for Blazor</Title>
|
||||
<RepositoryUrl>https://github.com/radzenhq/radzen-blazor</RepositoryUrl>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LibSassBuilder" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'netstandard2.1'" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'netstandard2.1'" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net5.0'" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net5.0'" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net6.0'" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net6.0'" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net7.0'" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net7.0'" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.22" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageReference Include="DartSassBuilder" Version="1.1.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net6.0'" Version="6.0.25" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net6.0'" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net7.0'" Version="7.0.14" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net7.0'" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net8.0'" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net8.0'" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net9.0'" Version="9.*-*" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net9.0'" Version="9.*-*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="LICENSE.txt" Pack="true" PackagePath="" />
|
||||
<None Include="icon.png" Pack="true" PackagePath="" />
|
||||
<None Include="README.md" Pack="true" PackagePath="\"/>
|
||||
<None Include="..\README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -48,28 +49,28 @@
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<LibSassOutputStyle>expanded</LibSassOutputStyle>
|
||||
<EnableDefaultSassItems>false</EnableDefaultSassItems>
|
||||
<DartSassOutputStyle>expanded</DartSassOutputStyle>
|
||||
<EnableDefaultSassItems>false</EnableDefaultSassItems>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Sass Include="$(MSBuildProjectDirectory)/themes/*.scss" Exclude="$(MSBuildProjectDirectory)/themes/_*.scss" Condition="'$(TargetFramework)' == 'net6.0'" />
|
||||
<Sass Include="$(MSBuildProjectDirectory)/themes/*.scss" Exclude="$(MSBuildProjectDirectory)/themes/_*.scss" Condition="'$(TargetFramework)' == 'net8.0'" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="Sass" BeforeTargets="BeforeBuild" Condition="'$(TargetFramework)' == 'net6.0'">
|
||||
<Target Name="Sass" BeforeTargets="BeforeBuild" Condition="'$(TargetFramework)' == 'net8.0'">
|
||||
<PropertyGroup>
|
||||
<_SassFileList>@(Sass->'"%(FullPath)"', ' ')</_SassFileList>
|
||||
<LibSassBuilderArgs>files $(_SassFileList) --outputstyle $(LibSassOutputStyle) --level $(LibSassOutputLevel)</LibSassBuilderArgs>
|
||||
<DartSassBuilderArgs>files $(_SassFileList) --outputstyle $(DartSassOutputStyle) --level $(DartSassOutputLevel)</DartSassBuilderArgs>
|
||||
</PropertyGroup>
|
||||
<Message Text="$(LibSassBuilderArgs)" Importance="$(LibSassMessageLevel)" />
|
||||
<Message Text="Converted SassFile list to argument" Importance="$(LibSassMessageLevel)" />
|
||||
<Message Text="$(DartSassBuilderArgs)" Importance="$(DartSassMessageLevel)" />
|
||||
<Message Text="Converted SassFile list to argument" Importance="$(DartSassMessageLevel)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="MoveCss" AfterTargets="AfterCompile" Condition="'$(TargetFramework)' == 'net6.0'" >
|
||||
<Target Name="MoveCss" AfterTargets="AfterCompile" Condition="'$(TargetFramework)' == 'net8.0'">
|
||||
<ItemGroup>
|
||||
<CssFile Include="$(MSBuildProjectDirectory)/themes/*.css" />
|
||||
</ItemGroup>
|
||||
<Move SourceFiles="@(CssFile)" DestinationFolder="$(MSBuildProjectDirectory)/wwwroot/css/" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -10,31 +10,37 @@
|
||||
}
|
||||
@if (Visible)
|
||||
{
|
||||
<div @ref="@Element" role="tablist" style=@Style @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
|
||||
<div @ref="@Element" role="tablist" style=@Style @attributes="Attributes" class="@GetCssClass()" id="@GetId()"
|
||||
tabindex="0" @onkeydown="@((args) => OnKeyPress(args))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation=preventKeyPress
|
||||
@onfocus=@(args => focusedIndex = focusedIndex == -1 ? 0: focusedIndex)>
|
||||
@for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
var item = items[i];
|
||||
if (!item.Visible)
|
||||
continue;
|
||||
|
||||
<div @ref="@item.Element" id="@item.GetItemId()" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style">
|
||||
<a @onclick="@((args) => SelectItem(item))" href="javascript:void(0)" role="tab" tabindex="0"
|
||||
<div @ref="@item.Element" id="@item.GetItemId()" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style" @onkeydown:stopPropagation>
|
||||
<a @onclick="@((args) => SelectItem(item))" aria-label="@ItemAriaLabel(i, item)" title="@ItemTitle(i, item)" @onclick:preventDefault="true" role="tab"
|
||||
id="@($"rz-accordiontab-{items.IndexOf(item)}")" aria-controls="@($"rz-accordiontab-{items.IndexOf(item)}-content")" aria-expanded="true">
|
||||
@if (IsSelected(i, item))
|
||||
{
|
||||
<span class="rz-accordion-toggle-icon rzi rzi-chevron-down"></span>
|
||||
<span class="notranslate rz-accordion-toggle-icon rzi rzi-chevron-down"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="rz-accordion-toggle-icon rzi rzi-chevron-right"></span>
|
||||
<span class="notranslate rz-accordion-toggle-icon rzi rzi-chevron-right"></span>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(item.Icon))
|
||||
{
|
||||
<i class="rzi">@((MarkupString)item.Icon)</i>
|
||||
<i class="notranslate rzi" style="@(!string.IsNullOrEmpty(item.IconColor) ? $"color:{item.IconColor}" : null)">@((MarkupString)item.Icon)</i>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(item.Text))
|
||||
@if (item.Template != null)
|
||||
{
|
||||
<span>@item.Text</span>
|
||||
@item.Template
|
||||
}
|
||||
else if(!string.IsNullOrEmpty(item.Text))
|
||||
{
|
||||
<span>@((MarkupString)item.Text)</span>
|
||||
}
|
||||
</a>
|
||||
</div>
|
||||
@@ -42,7 +48,7 @@
|
||||
{
|
||||
<div class="rz-accordion-content-wrapper" role="tabpanel"
|
||||
id="@($"rz-accordiontab-{items.IndexOf(item)}-content")" aria-hidden="false" aria-labelledby="@($"rz-accordiontab-{items.IndexOf(item)}")">
|
||||
<div class="rz-accordion-content ">
|
||||
<div class="rz-accordion-content" @onkeydown:stopPropagation>
|
||||
@item.ChildContent
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
@@ -41,7 +44,14 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
/// <value>The index of the selected item.</value>
|
||||
[Parameter]
|
||||
public int SelectedIndex { get; set; } = 0;
|
||||
public int SelectedIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value changed.
|
||||
/// </summary>
|
||||
/// <value>The value changed.</value>
|
||||
[Parameter]
|
||||
public EventCallback<int> SelectedIndexChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a callback raised when the item is expanded.
|
||||
@@ -74,18 +84,9 @@ namespace Radzen.Blazor
|
||||
{
|
||||
if (items.IndexOf(item) == -1)
|
||||
{
|
||||
if (item.Selected)
|
||||
if (item.GetSelected())
|
||||
{
|
||||
SelectedIndex = items.Count;
|
||||
if (!Multiple)
|
||||
{
|
||||
expandedIdexes.Clear();
|
||||
}
|
||||
|
||||
if (!expandedIdexes.Contains(SelectedIndex))
|
||||
{
|
||||
expandedIdexes.Add(SelectedIndex);
|
||||
}
|
||||
SelectedIndexChanged.InvokeAsync(items.Count);
|
||||
}
|
||||
|
||||
items.Add(item);
|
||||
@@ -112,7 +113,7 @@ namespace Radzen.Blazor
|
||||
/// <summary>
|
||||
/// Refreshes this instance.
|
||||
/// </summary>
|
||||
internal void Refresh()
|
||||
public void Refresh()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
@@ -125,30 +126,65 @@ namespace Radzen.Blazor
|
||||
/// <returns><c>true</c> if the specified index is selected; otherwise, <c>false</c>.</returns>
|
||||
protected bool IsSelected(int index, RadzenAccordionItem item)
|
||||
{
|
||||
return expandedIdexes.Contains(index);
|
||||
return item.GetSelected() == true;
|
||||
}
|
||||
|
||||
List<int> expandedIdexes = new List<int>();
|
||||
|
||||
internal async System.Threading.Tasks.Task SelectItem(RadzenAccordionItem item)
|
||||
/// <summary>
|
||||
/// Gets the item's title attribute value.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>The item's collapse or expand title value depending on if the item is expanded or collapsed.
|
||||
/// If the relevant title is null or whitespace this method returns "Expand" or "Collapse".</returns>
|
||||
protected string ItemTitle(int index, RadzenAccordionItem item)
|
||||
{
|
||||
if (IsSelected(index, item))
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(item.CollapseTitle) ? "Collapse" : item.CollapseTitle;
|
||||
}
|
||||
return string.IsNullOrWhiteSpace(item.ExpandTitle) ? "Expand" : item.ExpandTitle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item's aria-label attribute value.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>The item's collapse or expand aria-label value depending on if the item is expanded or collapsed.
|
||||
/// If the relevant aria-label is null or whitespace this method returns "Expand" or "Collapse".</returns>
|
||||
protected string ItemAriaLabel(int index, RadzenAccordionItem item)
|
||||
{
|
||||
if (IsSelected(index, item))
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(item.CollapseAriaLabel) ? "Collapse" : item.CollapseAriaLabel;
|
||||
}
|
||||
return string.IsNullOrWhiteSpace(item.ExpandAriaLabel) ? "Expand" : item.ExpandAriaLabel;
|
||||
}
|
||||
|
||||
internal async System.Threading.Tasks.Task SelectItem(RadzenAccordionItem item, bool? value = null)
|
||||
{
|
||||
if(item.Disabled) return;
|
||||
|
||||
await CollapseAll(item);
|
||||
|
||||
var itemIndex = items.IndexOf(item);
|
||||
if (!expandedIdexes.Contains(itemIndex))
|
||||
|
||||
var selected = item.GetSelected();
|
||||
|
||||
if (selected)
|
||||
{
|
||||
expandedIdexes.Add(itemIndex);
|
||||
await Expand.InvokeAsync(itemIndex);
|
||||
await Collapse.InvokeAsync(itemIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
expandedIdexes.Remove(itemIndex);
|
||||
await Collapse.InvokeAsync(itemIndex);
|
||||
await Expand.InvokeAsync(itemIndex);
|
||||
}
|
||||
|
||||
await item.SetSelected(value ?? !selected);
|
||||
|
||||
if (!Multiple)
|
||||
{
|
||||
SelectedIndex = itemIndex;
|
||||
await SelectedIndexChanged.InvokeAsync(itemIndex);
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
@@ -160,14 +196,60 @@ namespace Radzen.Blazor
|
||||
{
|
||||
foreach (var i in items.Where(i => i != item))
|
||||
{
|
||||
var itemIndex = items.IndexOf(i);
|
||||
if (expandedIdexes.Contains(itemIndex))
|
||||
if (i.GetSelected())
|
||||
{
|
||||
expandedIdexes.Remove(itemIndex);
|
||||
await i.SetSelected(false);
|
||||
await Collapse.InvokeAsync(items.IndexOf(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal int focusedIndex = -1;
|
||||
bool preventKeyPress = true;
|
||||
async Task OnKeyPress(KeyboardEventArgs args)
|
||||
{
|
||||
var key = args.Code != null ? args.Code : args.Key;
|
||||
|
||||
if (key == "ArrowUp" || key == "ArrowDown")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
|
||||
focusedIndex = Math.Clamp(focusedIndex + (key == "ArrowUp" ? -1 : 1), 0, items.Count - 1);
|
||||
}
|
||||
else if (key == "Space" || key == "Enter")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
|
||||
if (focusedIndex >= 0 && focusedIndex < items.Count)
|
||||
{
|
||||
await SelectItem(items.Where(i => i.Visible).ElementAt(focusedIndex));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
preventKeyPress = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsFocused(RadzenAccordionItem item)
|
||||
{
|
||||
return items.Where(i => i.Visible).ToList().IndexOf(item) == focusedIndex && focusedIndex != -1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
if (parameters.DidParameterChange(nameof(SelectedIndex), SelectedIndex))
|
||||
{
|
||||
var item = items.Where(i => i.Visible).ElementAtOrDefault(parameters.GetValueOrDefault<int>(nameof(SelectedIndex)));
|
||||
if (item != null && !item.GetSelected())
|
||||
{
|
||||
await SelectItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
await base.SetParametersAsync(parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,72 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon color.
|
||||
/// </summary>
|
||||
/// <value>The icon color.</value>
|
||||
[Parameter]
|
||||
public string IconColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="RadzenAccordionItem"/> is selected.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if selected; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool Selected { get; set; }
|
||||
public bool Selected
|
||||
{
|
||||
get
|
||||
{
|
||||
return selected != null ? selected.Value : false;
|
||||
}
|
||||
set
|
||||
{
|
||||
selected = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value changed.
|
||||
/// </summary>
|
||||
/// <value>The value changed.</value>
|
||||
[Parameter]
|
||||
public EventCallback<bool> SelectedChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="RadzenAccordionItem"/> is disabled.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if disabled; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title attribute of the expand button.
|
||||
/// </summary>
|
||||
/// <value>The title attribute value of the expand button.</value>
|
||||
[Parameter]
|
||||
public string ExpandTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title attribute of the collapse button.
|
||||
/// </summary>
|
||||
/// <value>The title attribute value of the collapse button.</value>
|
||||
[Parameter]
|
||||
public string CollapseTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the aria-label attribute of the expand button.
|
||||
/// </summary>
|
||||
/// <value>The aria-label attribute value of the expand button.</value>
|
||||
[Parameter]
|
||||
public string ExpandAriaLabel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the aria-label attribute of the collapse button.
|
||||
/// </summary>
|
||||
/// <value>The aria-label attribute value of the collapse button.</value>
|
||||
[Parameter]
|
||||
public string CollapseAriaLabel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the child content.
|
||||
/// </summary>
|
||||
@@ -36,6 +95,12 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header content.
|
||||
/// </summary>
|
||||
/// <value>The header content.</value>
|
||||
[Parameter]
|
||||
public RenderFragment Template { get; set; }
|
||||
|
||||
bool _visible = true;
|
||||
/// <summary>
|
||||
@@ -85,6 +150,19 @@ namespace Radzen.Blazor
|
||||
}
|
||||
}
|
||||
|
||||
bool? selected;
|
||||
internal bool GetSelected()
|
||||
{
|
||||
return selected ?? Selected;
|
||||
}
|
||||
|
||||
internal async Task SetSelected(bool? value)
|
||||
{
|
||||
selected = value;
|
||||
|
||||
await SelectedChanged.InvokeAsync(Selected);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set parameters as an asynchronous operation.
|
||||
/// </summary>
|
||||
@@ -92,12 +170,19 @@ namespace Radzen.Blazor
|
||||
/// <returns>A Task representing the asynchronous operation.</returns>
|
||||
public override async Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
bool shouldRefresh = false;
|
||||
if (parameters.DidParameterChange(nameof(Selected), Selected))
|
||||
{
|
||||
Accordion?.SelectItem(this);
|
||||
selected = parameters.GetValueOrDefault<bool>(nameof(Selected));
|
||||
shouldRefresh = true;
|
||||
}
|
||||
|
||||
await base.SetParametersAsync(parameters);
|
||||
|
||||
if (shouldRefresh)
|
||||
{
|
||||
Accordion.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -117,7 +202,7 @@ namespace Radzen.Blazor
|
||||
|
||||
internal string GetItemCssClass()
|
||||
{
|
||||
return GetCssClass();
|
||||
return $"{GetCssClass()} {(Accordion.IsFocused(this) ? "rz-state-focused" : "")}".Trim();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,81 +1,32 @@
|
||||
@inherits RadzenComponentWithChildren
|
||||
@if (GetVisible())
|
||||
@if (visible)
|
||||
{
|
||||
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()" aria-live="polite">
|
||||
<div class="rz-alert-item">
|
||||
@if (ShowIcon)
|
||||
{
|
||||
<RadzenIcon Icon="@getIcon()" Class="rz-alert-icon" />
|
||||
<RadzenIcon Icon="@GetIcon()" IconColor="@IconColor" class="rz-alert-icon" />
|
||||
}
|
||||
<div class="rz-alert-message">
|
||||
|
||||
@if (!string.IsNullOrEmpty(Title))
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(Title))
|
||||
{
|
||||
<div class="rz-alert-title">@Title</div>
|
||||
}
|
||||
<div class="rz-alert-content">@ChildContent</div>
|
||||
}
|
||||
<div class="rz-alert-content">
|
||||
@if (ChildContent != null)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
else
|
||||
{
|
||||
@Text
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (AllowClose)
|
||||
{
|
||||
if (Shade == Shade.Lighter)
|
||||
{
|
||||
if (AlertStyle == AlertStyle.Primary)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Primary" Shade="Shade.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Secondary)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Secondary" Shade="Shade.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Light)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Dark" Shade="Shade.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Base)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Dark" Shade="Shade.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Dark)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Light" Shade="Shade.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Success)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Success" Shade="Shade.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Danger)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Danger" Shade="Shade.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Warning)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Warning" Shade="Shade.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Info)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Info" Shade="Shade.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AlertStyle == AlertStyle.Light)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Base)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Dark" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Dark)
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Light" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<RadzenButton Click=@Close Icon="close" Variant="Variant.Text" ButtonStyle="ButtonStyle.Light" Size="@getCloseButtonSize()" />
|
||||
}
|
||||
}
|
||||
<RadzenButton Click=@OnClose Icon="close" Variant="Variant.Text" ButtonStyle="@GetCloseButtonStyle()" Shade="@GetCloseButtonShade()" Size="@GetCloseButtonSize()" />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -19,11 +19,11 @@ namespace Radzen.Blazor
|
||||
/// </example>
|
||||
public partial class RadzenAlert : RadzenComponentWithChildren
|
||||
{
|
||||
private string getAlertSize()
|
||||
private string GetAlertSize()
|
||||
{
|
||||
return Size == AlertSize.Medium ? "md" : Size == AlertSize.Large ? "lg" : Size == AlertSize.Small ? "sm" : "xs";
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether close is allowed. Set to <c>true</c> by default.
|
||||
/// </summary>
|
||||
@@ -45,6 +45,13 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text of the alert. Overriden by <see cref="RadzenComponentWithChildren.ChildContent" />.
|
||||
/// </summary>
|
||||
/// <value>The title.</value>
|
||||
[Parameter]
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon.
|
||||
/// </summary>
|
||||
@@ -52,6 +59,13 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon color.
|
||||
/// </summary>
|
||||
/// <value>The icon color.</value>
|
||||
[Parameter]
|
||||
public string IconColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the severity.
|
||||
/// </summary>
|
||||
@@ -80,72 +94,131 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public AlertSize Size { get; set; } = AlertSize.Medium;
|
||||
|
||||
ButtonSize getCloseButtonSize()
|
||||
ButtonSize GetCloseButtonSize()
|
||||
{
|
||||
return Size == AlertSize.ExtraSmall ? ButtonSize.ExtraSmall : ButtonSize.Small;
|
||||
}
|
||||
|
||||
bool? visible;
|
||||
bool GetVisible()
|
||||
Shade GetCloseButtonShade()
|
||||
{
|
||||
return visible ?? Visible;
|
||||
if (Shade == Shade.Light || Shade == Shade.Lighter)
|
||||
{
|
||||
return Shade.Darker;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Shade.Default;
|
||||
}
|
||||
}
|
||||
|
||||
ButtonStyle GetCloseButtonStyle()
|
||||
{
|
||||
if (Shade == Shade.Light || Shade == Shade.Lighter)
|
||||
{
|
||||
switch (AlertStyle)
|
||||
{
|
||||
case AlertStyle.Success:
|
||||
return ButtonStyle.Success;
|
||||
case AlertStyle.Danger:
|
||||
return ButtonStyle.Danger;
|
||||
case AlertStyle.Warning:
|
||||
return ButtonStyle.Warning;
|
||||
case AlertStyle.Info:
|
||||
return ButtonStyle.Info;
|
||||
case AlertStyle.Primary:
|
||||
return ButtonStyle.Primary;
|
||||
case AlertStyle.Secondary:
|
||||
return ButtonStyle.Secondary;
|
||||
case AlertStyle.Light:
|
||||
case AlertStyle.Base:
|
||||
return ButtonStyle.Dark;
|
||||
default:
|
||||
return ButtonStyle.Light;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (AlertStyle)
|
||||
{
|
||||
case AlertStyle.Light:
|
||||
case AlertStyle.Base:
|
||||
return ButtonStyle.Dark;
|
||||
default:
|
||||
return ButtonStyle.Light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool visible;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
visible = Visible;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string GetComponentCssClass()
|
||||
{
|
||||
return $"rz-alert rz-alert-{getAlertSize()} rz-variant-{Enum.GetName(typeof(Variant), Variant).ToLowerInvariant()} rz-{Enum.GetName(typeof(AlertStyle), AlertStyle).ToLowerInvariant()} rz-shade-{Enum.GetName(typeof(Shade), Shade).ToLowerInvariant()}";
|
||||
return $"rz-alert rz-alert-{GetAlertSize()} rz-variant-{Enum.GetName(typeof(Variant), Variant).ToLowerInvariant()} rz-{Enum.GetName(typeof(AlertStyle), AlertStyle).ToLowerInvariant()} rz-shade-{Enum.GetName(typeof(Shade), Shade).ToLowerInvariant()}";
|
||||
}
|
||||
|
||||
string getIcon()
|
||||
string GetIcon()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Icon))
|
||||
{
|
||||
return Icon;
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Primary)
|
||||
{
|
||||
return "lightbulb_outline";
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Secondary)
|
||||
{
|
||||
return "lightbulb_outline";
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Light)
|
||||
{
|
||||
return "lightbulb_outline";
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Base)
|
||||
{
|
||||
return "lightbulb_outline";
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Dark)
|
||||
{
|
||||
return "lightbulb_outline";
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Success)
|
||||
{
|
||||
return "check_circle_outline";
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Danger)
|
||||
{
|
||||
return "error_outline";
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Warning)
|
||||
{
|
||||
return "warning_amber";
|
||||
}
|
||||
else if (AlertStyle == AlertStyle.Info)
|
||||
{
|
||||
return "info_outline";
|
||||
}
|
||||
|
||||
return "";
|
||||
switch (AlertStyle)
|
||||
{
|
||||
case AlertStyle.Success:
|
||||
return "check_circle";
|
||||
case AlertStyle.Danger:
|
||||
return "error";
|
||||
case AlertStyle.Warning:
|
||||
return "warning_amber";
|
||||
case AlertStyle.Info:
|
||||
return "info";
|
||||
default:
|
||||
return "lightbulb";
|
||||
}
|
||||
}
|
||||
|
||||
void Close()
|
||||
async Task OnClose()
|
||||
{
|
||||
visible = false;
|
||||
|
||||
await VisibleChanged.InvokeAsync(false);
|
||||
await Close.InvokeAsync(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback which is invoked when the alert is shown or hidden.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<bool> VisibleChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback which is invoked when the alert is closed by the user.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback Close { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
var visibleChanged = parameters.DidParameterChange(nameof(Visible), Visible);
|
||||
|
||||
await base.SetParametersAsync(parameters);
|
||||
|
||||
if (visibleChanged)
|
||||
{
|
||||
visible = Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
Radzen.Blazor/RadzenAppearanceToggle.razor
Normal file
2
Radzen.Blazor/RadzenAppearanceToggle.razor
Normal file
@@ -0,0 +1,2 @@
|
||||
@inherits RadzenComponent
|
||||
<RadzenToggleButton Visible=@Visible @attributes=@Attributes Icon=@Icon Change=@OnChange Value=@value Variant="Variant" ButtonStyle="ButtonStyle" ToggleButtonStyle="ToggleButtonStyle" ToggleShade="ToggleShade" />
|
||||
112
Radzen.Blazor/RadzenAppearanceToggle.razor.cs
Normal file
112
Radzen.Blazor/RadzenAppearanceToggle.razor.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// Dark or light theme switch. Requires <see cref="ThemeService" /> to be registered in the DI container.
|
||||
/// </summary>
|
||||
public partial class RadzenAppearanceToggle : RadzenComponent
|
||||
{
|
||||
[Inject]
|
||||
private ThemeService ThemeService { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the switch button variant.
|
||||
/// </summary>
|
||||
/// <value>The switch button variant.</value>
|
||||
[Parameter]
|
||||
public Variant Variant { get; set; } = Variant.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the switch button style.
|
||||
/// </summary>
|
||||
/// <value>The switch button style.</value>
|
||||
[Parameter]
|
||||
public ButtonStyle ButtonStyle { get; set; } = ButtonStyle.Base;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the switch button toggled shade.
|
||||
/// </summary>
|
||||
/// <value>The switch button toggled shade.</value>
|
||||
[Parameter]
|
||||
public Shade ToggleShade { get; set; } = Shade.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the switch button toggled style.
|
||||
/// </summary>
|
||||
/// <value>The switch button toggled style.</value>
|
||||
[Parameter]
|
||||
public ButtonStyle ToggleButtonStyle { get; set; } = ButtonStyle.Base;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the light theme. Not set by default - the component uses the light version of the current theme.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string LightTheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dark theme. Not set by default - the component uses the dark version of the current theme.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string DarkTheme { get; set; }
|
||||
|
||||
private string CurrentLightTheme => LightTheme ?? ThemeService.Theme?.ToLowerInvariant() switch
|
||||
{
|
||||
"dark" => "default",
|
||||
"material-dark" => "material",
|
||||
"fluent-dark" => "fluent",
|
||||
"material3-dark" => "material3",
|
||||
"software-dark" => "software",
|
||||
"humanistic-dark" => "humanistic",
|
||||
"standard-dark" => "standard",
|
||||
_ => ThemeService.Theme,
|
||||
};
|
||||
|
||||
private string CurrentDarkTheme => DarkTheme ?? ThemeService.Theme?.ToLowerInvariant() switch
|
||||
{
|
||||
"default" => "dark",
|
||||
"material" => "material-dark",
|
||||
"fluent" => "fluent-dark",
|
||||
"material3" => "material3-dark",
|
||||
"software" => "software-dark",
|
||||
"humanistic" => "humanistic-dark",
|
||||
"standard" => "standard-dark",
|
||||
_ => ThemeService.Theme,
|
||||
};
|
||||
|
||||
private bool value;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
ThemeService.ThemeChanged += OnThemeChanged;
|
||||
|
||||
value = ThemeService.Theme != CurrentDarkTheme;
|
||||
}
|
||||
|
||||
private void OnThemeChanged()
|
||||
{
|
||||
value = ThemeService.Theme != CurrentDarkTheme;
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
void OnChange(bool value)
|
||||
{
|
||||
ThemeService.SetTheme(value ? CurrentLightTheme : CurrentDarkTheme);
|
||||
}
|
||||
|
||||
private string Icon => value ? "dark_mode" : "light_mode";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
||||
ThemeService.ThemeChanged -= OnThemeChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,19 +14,19 @@
|
||||
|
||||
var pathGenerator = GetPathGenerator();
|
||||
|
||||
var data = Items.Select(item =>
|
||||
var data = Items.Select(item =>
|
||||
{
|
||||
var x = category(item);
|
||||
var y = value(item);
|
||||
|
||||
return new Point { X = x, Y = y };
|
||||
return new Point<TItem> { X = x, Y = y, Data = item };
|
||||
});
|
||||
|
||||
var ticks = Chart.CategoryScale.Ticks(Chart.CategoryAxis.TickDistance);
|
||||
var index = Chart.Series.IndexOf(this);
|
||||
var className = $"rz-area-series rz-series-{index}";
|
||||
|
||||
return
|
||||
return
|
||||
@<g class="@className">
|
||||
@if (Items.Any())
|
||||
{
|
||||
@@ -43,7 +43,7 @@
|
||||
<path @key="@area" style="@style" d="@area" fill="@Fill" stroke="none"></path>
|
||||
<Path @key="@line" D="@line" Stroke="@Stroke" StrokeWidth="@StrokeWidth" LineType="@LineType" Style="@style" Fill="none" />
|
||||
}
|
||||
<Markers Data="@Items" Category="@category" Value="@value" MarkerType="@MarkerType" Stroke="@Markers.Stroke" Fill="@(Markers.Fill ?? Stroke)" StrokeWidth="@Markers.StrokeWidth" Size="@Markers.Size" />
|
||||
<Markers Visible="@Markers.Visible" Series="@this" Data="@data" MarkerType="@MarkerType" Stroke="@Markers.Stroke" Fill="@(Markers.Fill ?? Stroke)" StrokeWidth="@Markers.StrokeWidth" Size="@Markers.Size" />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Radzen.Blazor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies how to render lines between data points. Set to <see cref="Line"/> by default
|
||||
/// Specifies how to render lines between data points. Set to <see cref="Interpolation.Line"/> by default
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Interpolation Interpolation { get; set; } = Interpolation.Line;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@using Radzen
|
||||
@using Radzen.Blazor.Rendering
|
||||
@using System.Collections
|
||||
@using System.Linq.Dynamic.Core
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.JSInterop
|
||||
|
||||
@@ -12,23 +11,23 @@
|
||||
<div @ref="@Element" style="@($"{Style};display:inline-block;")" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
|
||||
@if (Multiline)
|
||||
{
|
||||
<textarea @ref="@search" @onkeydown="@OnFilterKeyPress" value="@Value" disabled="@Disabled"
|
||||
oninput="@OpenScript()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onchange="@OnChange"
|
||||
<textarea @ref="@search" @attributes="InputAttributes" @onkeydown="@OnFilterKeyPress" value="@Value" disabled="@Disabled"
|
||||
oninput="@OpenScript()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onchange="@OnChange" onfocus="@(OpenOnFocus ? OpenScript() : null)"
|
||||
aria-autocomplete="list" aria-haspopup="true" autocomplete="off" role="combobox"
|
||||
class="@InputClassList"
|
||||
id="@Name" aria-expanded="true" placeholder="@Placeholder" />
|
||||
class="@InputClassList" onblur="Radzen.activeElement = null"
|
||||
id="@Name" aria-expanded="true" placeholder="@CurrentPlaceholder" maxlength="@MaxLength" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<input @ref="@search" @onkeydown="@OnFilterKeyPress" value="@Value" disabled="@Disabled"
|
||||
oninput="@OpenScript()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onchange="@OnChange"
|
||||
<input @ref="@search" @attributes="InputAttributes" @onkeydown="@OnFilterKeyPress" value="@Value" disabled="@Disabled"
|
||||
oninput="@OpenScript()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onchange="@OnChange" onfocus="@(OpenOnFocus ? OpenScript() : null)"
|
||||
aria-autocomplete="list" aria-haspopup="true" autocomplete="off" role="combobox"
|
||||
class="@InputClassList"
|
||||
type="text" id="@Name" aria-expanded="true" placeholder="@Placeholder" />
|
||||
class="@InputClassList" onblur="Radzen.activeElement = null"
|
||||
type="@InputType" id="@Name" aria-expanded="true" placeholder="@CurrentPlaceholder" maxlength="@MaxLength" />
|
||||
}
|
||||
<div id="@PopupID" class="rz-autocomplete-panel" style="@PopupStyle">
|
||||
<ul @ref="@list" class="rz-autocomplete-items rz-autocomplete-list" role="listbox">
|
||||
@if (!string.IsNullOrEmpty(searchText) || !string.IsNullOrEmpty(customSearchText))
|
||||
@if (OpenOnFocus || (!string.IsNullOrEmpty(searchText) || !string.IsNullOrEmpty(customSearchText)))
|
||||
{
|
||||
@foreach (var item in LoadData.HasDelegate ? Data != null ? Data : Enumerable.Empty<object>() : (View != null ? View : Enumerable.Empty<object>()))
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Radzen;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using System.Collections;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.JSInterop;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@@ -9,6 +8,7 @@ using Microsoft.AspNetCore.Components.Web;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
@@ -22,6 +22,42 @@ namespace Radzen.Blazor
|
||||
/// </example>
|
||||
public partial class RadzenAutoComplete : DataBoundFormComponent<string>
|
||||
{
|
||||
object selectedItem = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected item.
|
||||
/// </summary>
|
||||
/// <value>The selected item.</value>
|
||||
[Parameter]
|
||||
public object SelectedItem
|
||||
{
|
||||
get
|
||||
{
|
||||
return selectedItem;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (selectedItem != value)
|
||||
{
|
||||
selectedItem = object.Equals(value, "null") ? null : value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected item changed.
|
||||
/// </summary>
|
||||
/// <value>The selected item changed.</value>
|
||||
[Parameter]
|
||||
public EventCallback<object> SelectedItemChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies additional custom attributes that will be rendered by the input.
|
||||
/// </summary>
|
||||
/// <value>The attributes.</value>
|
||||
[Parameter]
|
||||
public IReadOnlyDictionary<string, object> InputAttributes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="RadzenAutoComplete"/> is multiline.
|
||||
/// </summary>
|
||||
@@ -29,6 +65,13 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public bool Multiline { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether popup should open on focus. Set to <c>false</c> by default.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if popup should open on focus; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool OpenOnFocus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Popup height.
|
||||
/// </summary>
|
||||
@@ -57,6 +100,23 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public int FilterDelay { get; set; } = 500;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the underlying input type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This does not apply when <see cref="Multiline"/> is <c>true</c>.
|
||||
/// </remarks>
|
||||
/// <value>The input type.</value>
|
||||
[Parameter]
|
||||
public string InputType { get; set; } = "text";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the underlying max length.
|
||||
/// </summary>
|
||||
/// <value>The max length value.</value>
|
||||
[Parameter]
|
||||
public long? MaxLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets search input reference.
|
||||
/// </summary>
|
||||
@@ -91,15 +151,20 @@ namespace Radzen.Blazor
|
||||
//
|
||||
}
|
||||
}
|
||||
else if (key == "Enter")
|
||||
else if (key == "Enter" || key == "Tab")
|
||||
{
|
||||
if (selectedIndex >= 0 && selectedIndex <= items.Count() - 1)
|
||||
{
|
||||
await OnSelectItem(items.ElementAt(selectedIndex));
|
||||
selectedIndex = -1;
|
||||
}
|
||||
|
||||
if (key == "Tab")
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
|
||||
}
|
||||
}
|
||||
else if (key == "Escape" || key == "Tab")
|
||||
else if (key == "Escape")
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
|
||||
}
|
||||
@@ -115,8 +180,13 @@ namespace Radzen.Blazor
|
||||
{
|
||||
var value = await JSRuntime.InvokeAsync<string>("Radzen.getInputValue", search);
|
||||
|
||||
if (value.Length < MinLength)
|
||||
value = $"{value}";
|
||||
|
||||
if (value.Length < MinLength && !OpenOnFocus)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LoadData.HasDelegate)
|
||||
{
|
||||
@@ -153,7 +223,7 @@ namespace Radzen.Blazor
|
||||
{
|
||||
get
|
||||
{
|
||||
return Data != null && !string.IsNullOrEmpty(searchText) ? Data.AsQueryable() : null;
|
||||
return Data != null && (OpenOnFocus || !string.IsNullOrEmpty(searchText)) ? Data.AsQueryable() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,12 +237,7 @@ namespace Radzen.Blazor
|
||||
{
|
||||
if (Query != null)
|
||||
{
|
||||
string filterCaseSensitivityOperator = FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? ".ToLower()" : "";
|
||||
|
||||
string textProperty = string.IsNullOrEmpty(TextProperty) ? string.Empty : $".{TextProperty}";
|
||||
|
||||
return Query.Where($"o=>o{textProperty}{filterCaseSensitivityOperator}.{Enum.GetName(typeof(StringFilterOperator), FilterOperator)}(@0)",
|
||||
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
|
||||
return Query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -190,6 +255,8 @@ namespace Radzen.Blazor
|
||||
await ValueChanged.InvokeAsync($"{Value}");
|
||||
if (FieldIdentifier.FieldName != null) { EditContext?.NotifyFieldChanged(FieldIdentifier); }
|
||||
await Change.InvokeAsync(Value);
|
||||
|
||||
await SelectedItemChanged.InvokeAsync(null);
|
||||
}
|
||||
|
||||
async System.Threading.Tasks.Task SelectItem(object item)
|
||||
@@ -207,6 +274,8 @@ namespace Radzen.Blazor
|
||||
if (FieldIdentifier.FieldName != null) { EditContext?.NotifyFieldChanged(FieldIdentifier); }
|
||||
await Change.InvokeAsync(Value);
|
||||
|
||||
await SelectedItemChanged.InvokeAsync(item);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
@@ -265,12 +334,34 @@ namespace Radzen.Blazor
|
||||
shouldClose = !visible;
|
||||
}
|
||||
|
||||
if (parameters.DidParameterChange(nameof(SelectedItem), SelectedItem))
|
||||
{
|
||||
var item = parameters.GetValueOrDefault<object>(nameof(SelectedItem));
|
||||
if (item != null)
|
||||
{
|
||||
await SelectItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
await base.SetParametersAsync(parameters);
|
||||
|
||||
if (parameters.DidParameterChange(nameof(Value), Value))
|
||||
{
|
||||
Value = parameters.GetValueOrDefault<object>(nameof(Value));
|
||||
}
|
||||
|
||||
if (shouldClose && !firstRender)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", PopupID);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the focus on the input element.
|
||||
/// </summary>
|
||||
public override async ValueTask FocusAsync()
|
||||
{
|
||||
await search.FocusAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
</CascadingValue>
|
||||
|
||||
@code {
|
||||
public override RenderFragment Render(ScaleBase categoryScale, ScaleBase valueScale)
|
||||
{
|
||||
public override RenderFragment Render(ScaleBase categoryScale, ScaleBase valueScale)
|
||||
{
|
||||
var value = ComposeValue(categoryScale);
|
||||
var category = ComposeCategory(valueScale);
|
||||
var ticks = Chart.CategoryScale.Ticks(Chart.ValueAxis.TickDistance);
|
||||
@@ -26,15 +26,17 @@
|
||||
var height = barHeight / barSeries.Count() - padding + padding / barSeries.Count();;
|
||||
var className = $"rz-bar-series rz-series-{Chart.Series.IndexOf(this)}";
|
||||
|
||||
return
|
||||
return
|
||||
@<g class="@className">
|
||||
@foreach(var data in Items)
|
||||
{
|
||||
var y = category(data) - barHeight / 2 + index * height + index * padding;
|
||||
var x = value(data);
|
||||
var itemValue = Value(data);
|
||||
var radius = Chart.BarOptions.Radius;
|
||||
var width = Math.Abs(x0 - x);
|
||||
|
||||
if (radius > height / 2)
|
||||
if (radius > height / 2 || radius > width)
|
||||
{
|
||||
radius = 0;
|
||||
}
|
||||
@@ -47,11 +49,11 @@
|
||||
{
|
||||
path = $"M {x0.ToInvariantString()} {y.ToInvariantString()} L {(x+radius).ToInvariantString()} {y.ToInvariantString()} A {r} {r} 0 0 0 {x.ToInvariantString()} {(y+radius).ToInvariantString()} L {x.ToInvariantString()} {(y+height-radius).ToInvariantString()} A {r} {r} 0 0 0 {(x+radius).ToInvariantString()} {(y + height).ToInvariantString()} L {x0.ToInvariantString()} {(y+height).ToInvariantString()} Z";
|
||||
}
|
||||
var fill = PickColor(Items.IndexOf(data), Fills, Fill);
|
||||
var stroke = PickColor(Items.IndexOf(data), Strokes, Stroke);
|
||||
var fill = PickColor(Items.IndexOf(data), Fills, Fill, FillRange, itemValue);
|
||||
var stroke = PickColor(Items.IndexOf(data), Strokes, Stroke, StrokeRange, itemValue);
|
||||
|
||||
<Path @key="@path" D="@path" Stroke="@stroke" StrokeWidth="@StrokeWidth" Fill="@fill" LineType="@LineType" Style="@style" />
|
||||
}
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,20 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public LineType LineType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color range of the fill.
|
||||
/// </summary>
|
||||
/// <value>The color range of the fill.</value>
|
||||
[Parameter]
|
||||
public IList<SeriesColorRange> FillRange { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color range of the stroke.
|
||||
/// </summary>
|
||||
/// <value>The color range of the stroke.</value>
|
||||
[Parameter]
|
||||
public IList<SeriesColorRange> StrokeRange { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Color
|
||||
{
|
||||
@@ -106,7 +120,7 @@ namespace Radzen.Blazor
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
var color = PickColor(index, Fills, Fill);
|
||||
var color = PickColor(index, Fills, Fill, FillRange, Value(item));
|
||||
|
||||
if (color != null)
|
||||
{
|
||||
@@ -147,7 +161,7 @@ namespace Radzen.Blazor
|
||||
/// <inheritdoc />
|
||||
public override bool Contains(double x, double y, double tolerance)
|
||||
{
|
||||
return DataAt(x, y) != null;
|
||||
return DataAt(x, y).Item1 != null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -173,7 +187,7 @@ namespace Radzen.Blazor
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object DataAt(double x, double y)
|
||||
public override (object, Point) DataAt(double x, double y)
|
||||
{
|
||||
var value = ComposeValue(Chart.CategoryScale);
|
||||
var category = ComposeCategory(Chart.ValueScale);
|
||||
@@ -196,11 +210,11 @@ namespace Radzen.Blazor
|
||||
|
||||
if (startX <= x && x <= endX && startY <= y && y <= endY)
|
||||
{
|
||||
return data;
|
||||
return (data, new Point() { X = x, Y = y });
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -221,13 +235,17 @@ namespace Radzen.Blazor
|
||||
public override IEnumerable<ChartDataLabel> GetDataLabels(double offsetX, double offsetY)
|
||||
{
|
||||
var list = new List<ChartDataLabel>();
|
||||
|
||||
|
||||
(string Anchor, int Sign) position;
|
||||
|
||||
foreach (var d in Data)
|
||||
{
|
||||
list.Add(new ChartDataLabel
|
||||
{
|
||||
Position = new Point() { X = TooltipX(d) + offsetX + 8, Y = TooltipY(d) + offsetY },
|
||||
TextAnchor = "start",
|
||||
position = Value(d) < 0 ? ("end", -1) : Value(d) == 0 ? ("middle", 0) : ("start", 1);
|
||||
|
||||
list.Add(new ChartDataLabel
|
||||
{
|
||||
Position = new Point() { X = TooltipX(d) + offsetX + (8 * position.Sign), Y = TooltipY(d) + offsetY },
|
||||
TextAnchor = position.Anchor,
|
||||
Text = Chart.ValueAxis.Format(Chart.CategoryScale, Value(d))
|
||||
});
|
||||
}
|
||||
@@ -235,4 +253,4 @@ namespace Radzen.Blazor
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ using Radzen.Blazor.Rendering;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
using Microsoft.JSInterop;
|
||||
using System.Threading;
|
||||
using System.Runtime.ExceptionServices;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
@@ -25,9 +27,8 @@ namespace Radzen.Blazor
|
||||
protected override string GetComponentCssClass()
|
||||
{
|
||||
var classList = ClassList.Create("rz-body")
|
||||
.Add("body")
|
||||
.Add("body-expanded", Expanded);
|
||||
|
||||
.Add("rz-body-expanded", Expanded);
|
||||
|
||||
return classList.ToString();
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ namespace Radzen.Blazor
|
||||
{
|
||||
if (IsJSRuntimeAvailable && Layout != null)
|
||||
{
|
||||
JSRuntime.InvokeVoidAsync("eval", $"document.getElementById('{GetId()}').scrollTop = 0");
|
||||
JSRuntime.InvokeVoidAsync("eval", $"try{{document.getElementById('{GetId()}').scrollTop = 0}}catch(e){{}}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
{
|
||||
@if (!string.IsNullOrWhiteSpace(Path))
|
||||
{
|
||||
<RadzenLink Icon="@Icon" Path="@Path" Text="@Text" />
|
||||
<RadzenLink Icon="@Icon" IconColor="@IconColor" Path="@Path" Text="@Text" />
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (!string.IsNullOrWhiteSpace(Icon))
|
||||
{
|
||||
<RadzenIcon Icon="@Icon" />
|
||||
<RadzenIcon Icon="@Icon" IconColor="@IconColor" />
|
||||
}
|
||||
<RadzenLabel Text="@Text" />
|
||||
<span class="rz-label">@Text</span>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -31,6 +31,13 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon color.
|
||||
/// </summary>
|
||||
/// <value>The icon color.</value>
|
||||
[Parameter]
|
||||
public string IconColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Template Parameter used only for this Item
|
||||
/// Note: this overrides the <see cref="Template"/> Cascading Parameter
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@if (Visible)
|
||||
{
|
||||
<button @ref="@Element" style="@Style" disabled="@IsDisabled"
|
||||
<button @ref="@Element" style="@Style" disabled="@IsDisabled" tabindex="@(Disabled ? -1 : TabIndex)"
|
||||
type="@Enum.GetName(typeof(ButtonType), ButtonType).ToLower()"
|
||||
@attributes="Attributes" class="@GetCssClass()" id="@GetId()"
|
||||
@onclick="@OnClick">
|
||||
@@ -25,11 +25,11 @@
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(@Icon))
|
||||
{
|
||||
<i class="rz-button-icon-left rzi">@((MarkupString)Icon)</i>
|
||||
<i class="notranslate rz-button-icon-left rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@((MarkupString)Icon)</i>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Image))
|
||||
{
|
||||
<img class="rz-button-icon-left rzi" src="@Image" />
|
||||
<img class="notranslate rz-button-icon-left rzi" src="@Image" alt="@ImageAlternateText" />
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Text))
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Radzen.Blazor
|
||||
/// </example>
|
||||
public partial class RadzenButton : RadzenComponent
|
||||
{
|
||||
private string getButtonSize()
|
||||
internal string getButtonSize()
|
||||
{
|
||||
return Size == ButtonSize.Medium ? "md" : Size == ButtonSize.Large ? "lg" : Size == ButtonSize.Small ? "sm" : "xs";
|
||||
}
|
||||
@@ -27,6 +27,13 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the tab.
|
||||
/// </summary>
|
||||
/// <value>The index of the tab.</value>
|
||||
[Parameter]
|
||||
public int TabIndex { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text.
|
||||
/// </summary>
|
||||
@@ -34,6 +41,13 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public string Text { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text.
|
||||
/// </summary>
|
||||
/// <value>The text.</value>
|
||||
[Parameter]
|
||||
public string ImageAlternateText { get; set; } = "button";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon.
|
||||
/// </summary>
|
||||
@@ -41,6 +55,13 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon color.
|
||||
/// </summary>
|
||||
/// <value>The icon color.</value>
|
||||
[Parameter]
|
||||
public string IconColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image.
|
||||
/// </summary>
|
||||
@@ -123,7 +144,7 @@ namespace Radzen.Blazor
|
||||
/// Handles the <see cref="E:Click" /> event.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
|
||||
public async Task OnClick(MouseEventArgs args)
|
||||
public virtual async Task OnClick(MouseEventArgs args)
|
||||
{
|
||||
if (clicking)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
@@ -13,7 +14,18 @@ namespace Radzen.Blazor
|
||||
/// <inheritdoc />
|
||||
protected override string GetComponentCssClass()
|
||||
{
|
||||
return "rz-card";
|
||||
var classList = new List<string>();
|
||||
classList.Add("rz-card");
|
||||
classList.Add($"rz-variant-{Variant.ToString().ToLowerInvariant()}");
|
||||
|
||||
return string.Join(" ", classList);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the card variant.
|
||||
/// </summary>
|
||||
/// <value>The card variant.</value>
|
||||
[Parameter]
|
||||
public Variant Variant { get; set; } = Variant.Filled;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Radzen.Blazor/RadzenCardGroup.razor
Normal file
6
Radzen.Blazor/RadzenCardGroup.razor
Normal file
@@ -0,0 +1,6 @@
|
||||
@inherits RadzenComponentWithChildren
|
||||
|
||||
@if (Visible)
|
||||
{
|
||||
<div @ref="@Element" @attributes="Attributes" class="@GetCssClass()" style="@Style" id="@GetId()">@ChildContent</div>
|
||||
}
|
||||
35
Radzen.Blazor/RadzenCardGroup.razor.cs
Normal file
35
Radzen.Blazor/RadzenCardGroup.razor.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// RadzenCardGroup component.
|
||||
/// </summary>
|
||||
public partial class RadzenCardGroup : RadzenComponentWithChildren
|
||||
{
|
||||
/// <summary>
|
||||
/// Toggles the responsive mode of the component. If set to <c>true</c> (the default) the component will be
|
||||
/// expanded on larger displays and collapsed on touch devices. Set to <c>false</c> if you want to disable this behavior.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool Responsive { get; set; } = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string GetComponentCssClass()
|
||||
{
|
||||
var classList = new List<string>();
|
||||
classList.Add("rz-card-group");
|
||||
|
||||
if (Responsive)
|
||||
{
|
||||
classList.Add("rz-card-group-responsive");
|
||||
}
|
||||
|
||||
return string.Join(" ", classList);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Radzen.Blazor/RadzenCarousel.razor
Normal file
43
Radzen.Blazor/RadzenCarousel.razor
Normal file
@@ -0,0 +1,43 @@
|
||||
@inherits RadzenComponent
|
||||
@using System.Linq
|
||||
@using Microsoft.JSInterop
|
||||
<CascadingValue Value=this>
|
||||
@if (Visible)
|
||||
{
|
||||
<section @ref=@Element style=@Style @attributes=@Attributes class=@GetCssClass() id=@GetId() tabindex="0">
|
||||
@if(AllowPaging && (PagerPosition == PagerPosition.Top || PagerPosition == PagerPosition.TopAndBottom))
|
||||
{
|
||||
<RadzenStack class="rz-carousel-pager rz-carousel-pager-top" Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="JustifyContent.Center" Wrap="FlexWrap.Wrap">
|
||||
@foreach (var item in items)
|
||||
{
|
||||
var index = items.IndexOf(item);
|
||||
<a @onclick="@(args => Navigate(index))" class="rz-carousel-pager-button @(index == selectedIndex ? "rz-state-active" : "")"></a>
|
||||
}
|
||||
</RadzenStack>
|
||||
}
|
||||
@if(AllowNavigation)
|
||||
{
|
||||
<RadzenButton class="rz-carousel-prev" Icon="@PrevIcon" Text="@PrevText" ButtonStyle="@ButtonStyle" Size="@ButtonSize" Variant="@ButtonVariant" Shade="@ButtonShade"
|
||||
Click=@Prev />
|
||||
<RadzenButton class="rz-carousel-next" Icon="@NextIcon" Text="@NextText" ButtonStyle="@ButtonStyle" Size="@ButtonSize" Variant="@ButtonVariant" Shade="@ButtonShade"
|
||||
Click=@Next/>
|
||||
}
|
||||
<ul class="rz-carousel-items"
|
||||
@ontouchstart="OnTouchStart"
|
||||
@ontouchend="OnTouchEnd"
|
||||
@ontouchcancel="OnTouchCancel">
|
||||
@Items
|
||||
</ul>
|
||||
@if(AllowPaging && (PagerPosition == PagerPosition.Bottom || PagerPosition == PagerPosition.TopAndBottom))
|
||||
{
|
||||
<RadzenStack class="rz-carousel-pager rz-carousel-pager-bottom" Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="JustifyContent.Center" Wrap="FlexWrap.Wrap">
|
||||
@foreach (var item in items)
|
||||
{
|
||||
var index = items.IndexOf(item);
|
||||
<a @onclick="@(args => Navigate(index))" class="rz-carousel-pager-button @(index == selectedIndex ? "rz-state-active" : "")"></a>
|
||||
}
|
||||
</RadzenStack>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
</CascadingValue>
|
||||
367
Radzen.Blazor/RadzenCarousel.razor.cs
Normal file
367
Radzen.Blazor/RadzenCarousel.razor.cs
Normal file
@@ -0,0 +1,367 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.JSInterop;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// RadzenCarousel component.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <RadzenCarousel Change=@(args => Console.WriteLine($"Selected index is: {args}"))>
|
||||
/// <Items>
|
||||
/// <RadzenCarouselItem>
|
||||
/// Details for Orders
|
||||
/// </RadzenCarouselItem>
|
||||
/// <RadzenCarousel>
|
||||
/// Details for Employees
|
||||
/// </RadzenCarouselItem>
|
||||
/// </Items>
|
||||
/// </RadzenCarousel>
|
||||
/// </code>
|
||||
/// </example>
|
||||
public partial class RadzenCarousel : RadzenComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the items.
|
||||
/// </summary>
|
||||
/// <value>The items.</value>
|
||||
[Parameter]
|
||||
public RenderFragment Items { get; set; }
|
||||
|
||||
internal List<RadzenCarouselItem> items = new List<RadzenCarouselItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void AddItem(RadzenCarouselItem item)
|
||||
{
|
||||
if (!items.Contains(item))
|
||||
{
|
||||
items.Add(item);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void RemoveItem(RadzenCarouselItem item)
|
||||
{
|
||||
if (items.Contains(item))
|
||||
{
|
||||
items.Remove(item);
|
||||
|
||||
if (!disposed)
|
||||
{
|
||||
try { InvokeAsync(StateHasChanged); } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string GetComponentCssClass()
|
||||
{
|
||||
return $"rz-carousel {(AllowNavigation ? "" : "rz-carousel-no-navigation")} {(PagerOverlay ? "rz-carousel-pager-overlay" : "")}".Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to specific index.
|
||||
/// </summary>
|
||||
public async Task Navigate(int index)
|
||||
{
|
||||
if (Auto)
|
||||
{
|
||||
await Reset();
|
||||
}
|
||||
|
||||
await GoTo(index);
|
||||
}
|
||||
|
||||
async Task Prev()
|
||||
{
|
||||
await Navigate(selectedIndex == 0 ? items.Count - 1 : selectedIndex - 1);
|
||||
}
|
||||
|
||||
async Task Next()
|
||||
{
|
||||
await Navigate(selectedIndex == items.Count - 1 ? 0 : selectedIndex + 1);
|
||||
}
|
||||
|
||||
async Task GoTo(int index)
|
||||
{
|
||||
if (index >= 0 && index <= items.Count - 1 && selectedIndex != index)
|
||||
{
|
||||
selectedIndex = index;
|
||||
await SelectedIndexChanged.InvokeAsync(selectedIndex);
|
||||
await Change.InvokeAsync(selectedIndex);
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.scrollCarouselItem", items[selectedIndex].element);
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the auto-cycle timer.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
timer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the auto-cycle timer.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
timer?.Change(TimeSpan.FromMilliseconds(Interval), TimeSpan.FromMilliseconds(Interval));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the auto-cycle timer.
|
||||
/// </summary>
|
||||
public async Task Reset()
|
||||
{
|
||||
Stop();
|
||||
Start();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected index.
|
||||
/// </summary>
|
||||
/// <value>The selected index.</value>
|
||||
[Parameter]
|
||||
public int SelectedIndex { get; set; }
|
||||
|
||||
private int selectedIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected index changed callback.
|
||||
/// </summary>
|
||||
/// <value>The selected index changed callback.</value>
|
||||
[Parameter]
|
||||
public EventCallback<int> SelectedIndexChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the change callback.
|
||||
/// </summary>
|
||||
/// <value>The change callback.</value>
|
||||
[Parameter]
|
||||
public EventCallback<int> Change { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
var shouldUpdate = false;
|
||||
if (parameters.DidParameterChange(nameof(SelectedIndex), SelectedIndex))
|
||||
{
|
||||
selectedIndex = parameters.GetValueOrDefault<int>(nameof(SelectedIndex));
|
||||
shouldUpdate = true;
|
||||
}
|
||||
|
||||
if (parameters.DidParameterChange(nameof(Auto), Auto) ||
|
||||
parameters.DidParameterChange(nameof(Interval), Interval))
|
||||
{
|
||||
if (parameters.GetValueOrDefault<bool>(nameof(Auto)))
|
||||
{
|
||||
await Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
|
||||
await base.SetParametersAsync(parameters);
|
||||
|
||||
if (shouldUpdate)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.scrollCarouselItem", items[selectedIndex].element);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="RadzenCarousel"/> cycle is automatic.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if cycle automatic; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool Auto { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the auto-cycle interval in milliseconds.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public double Interval { get; set; } = 4000;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pager position. Set to <c>PagerPosition.Bottom</c> by default.
|
||||
/// </summary>
|
||||
/// <value>The pager position.</value>
|
||||
[Parameter]
|
||||
public PagerPosition PagerPosition { get; set; } = PagerPosition.Bottom;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether pager overlays the carousel items. Set to <c>true</c> by default.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if pager overlay is allowed; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool PagerOverlay { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether paging is allowed. Set to <c>true</c> by default.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if paging is allowed; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool AllowPaging { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether previous/next navigation is allowed. Set to <c>true</c> by default.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if previous/next navigation is allowed; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool AllowNavigation { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the buttons style
|
||||
/// </summary>
|
||||
/// <value>The buttons style.</value>
|
||||
[Parameter]
|
||||
public ButtonStyle ButtonStyle { get; set; } = ButtonStyle.Base;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the design variant of the buttons.
|
||||
/// </summary>
|
||||
/// <value>The variant of the buttons.</value>
|
||||
[Parameter]
|
||||
public Variant ButtonVariant { get; set; } = Variant.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color shade of the buttons.
|
||||
/// </summary>
|
||||
/// <value>The color shade of the buttons.</value>
|
||||
[Parameter]
|
||||
public Shade ButtonShade { get; set; } = Shade.Lighter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the buttons size.
|
||||
/// </summary>
|
||||
/// <value>The buttons size.</value>
|
||||
[Parameter]
|
||||
public ButtonSize ButtonSize { get; set; } = ButtonSize.Large;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the next button text.
|
||||
/// </summary>
|
||||
/// <value>The next button text.</value>
|
||||
[Parameter]
|
||||
public string NextText { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the previous button text.
|
||||
/// </summary>
|
||||
/// <value>The previous button text.</value>
|
||||
[Parameter]
|
||||
public string PrevText { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the next button icon.
|
||||
/// </summary>
|
||||
/// <value>The next button icon.</value>
|
||||
[Parameter]
|
||||
public string NextIcon { get; set; } = "arrow_forward_ios";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the previous button icon.
|
||||
/// </summary>
|
||||
/// <value>The previous button icon.</value>
|
||||
[Parameter]
|
||||
public string PrevIcon { get; set; } = "arrow_back_ios_new";
|
||||
|
||||
System.Threading.Timer timer;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
var ts = TimeSpan.FromMilliseconds(Interval);
|
||||
timer = new System.Threading.Timer(state => InvokeAsync(Next),
|
||||
null, Auto ? ts : Timeout.InfiniteTimeSpan, ts);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
||||
if (timer != null)
|
||||
{
|
||||
timer.Dispose();
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
double? x;
|
||||
double? y;
|
||||
|
||||
void OnTouchStart(TouchEventArgs args)
|
||||
{
|
||||
x = args.Touches[0].ClientX;
|
||||
y = args.Touches[0].ClientY;
|
||||
}
|
||||
|
||||
async Task OnTouchEnd(TouchEventArgs args)
|
||||
{
|
||||
if (x == null || y == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var xDiff = x.Value - args.ChangedTouches[0].ClientX;
|
||||
var yDiff = y.Value - args.ChangedTouches[0].ClientY;
|
||||
|
||||
if (Math.Abs(xDiff) < 100 && Math.Abs(yDiff) < 100)
|
||||
{
|
||||
x = null;
|
||||
y = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(xDiff) > Math.Abs(yDiff))
|
||||
{
|
||||
if (xDiff > 0)
|
||||
{
|
||||
await Next();
|
||||
}
|
||||
else
|
||||
{
|
||||
await Prev();
|
||||
}
|
||||
}
|
||||
|
||||
x = null;
|
||||
y = null;
|
||||
}
|
||||
|
||||
void OnTouchCancel(TouchEventArgs args)
|
||||
{
|
||||
x = null;
|
||||
y = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
Radzen.Blazor/RadzenCarouselItem.razor
Normal file
8
Radzen.Blazor/RadzenCarouselItem.razor
Normal file
@@ -0,0 +1,8 @@
|
||||
@using Microsoft.JSInterop
|
||||
@implements IDisposable
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
||||
<li @ref="element" @attributes=@Attributes class=@ClassList tabindex="0">
|
||||
@ChildContent
|
||||
<div class="rz-carousel-snapper"></div>
|
||||
</li>
|
||||
63
Radzen.Blazor/RadzenCarouselItem.razor.cs
Normal file
63
Radzen.Blazor/RadzenCarouselItem.razor.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// RadzenCarouselItem component.
|
||||
/// </summary>
|
||||
public partial class RadzenCarouselItem : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the class list.
|
||||
/// </summary>
|
||||
/// <value>The class list.</value>
|
||||
ClassList ClassList => ClassList.Create()
|
||||
.Add("rz-carousel-item")
|
||||
.Add(Attributes);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the arbitrary attributes.
|
||||
/// </summary>
|
||||
/// <value>The arbitrary attributes.</value>
|
||||
[Parameter(CaptureUnmatchedValues = true)]
|
||||
public IDictionary<string, object> Attributes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the child content.
|
||||
/// </summary>
|
||||
/// <value>The child content.</value>
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tabs.
|
||||
/// </summary>
|
||||
/// <value>The tabs.</value>
|
||||
[CascadingParameter]
|
||||
public RadzenCarousel Carousel { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
|
||||
Carousel.AddItem(this);
|
||||
|
||||
itemIndex = Carousel.items.IndexOf(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Carousel?.RemoveItem(this);
|
||||
}
|
||||
|
||||
int itemIndex;
|
||||
internal ElementReference element;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
{
|
||||
<CascadingValue Value="@this">
|
||||
<Legend />
|
||||
<svg style="width: 100%; height: 100%">
|
||||
<svg style="width: 100%; height: 100%; overflow: visible;">
|
||||
<g transform="@($"translate({MarginLeft.ToInvariantString()}, {MarginTop.ToInvariantString()})")">
|
||||
<ClipPath />
|
||||
@if(ShouldRenderAxes())
|
||||
@@ -38,10 +38,7 @@
|
||||
@donut.RenderTitle(MarginLeft, MarginTop)
|
||||
}
|
||||
}
|
||||
@if (tooltip != null)
|
||||
{
|
||||
@tooltip
|
||||
}
|
||||
|
||||
</CascadingValue>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -42,27 +42,49 @@ namespace Radzen.Blazor
|
||||
public ColorScheme ColorScheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A callback that will be invoked when the user clicks on a series.
|
||||
/// A callback that will be invoked when the user clicks on a series.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<SeriesClickEventArgs> SeriesClick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A callback that will be invoked when the user clicks on a legend.
|
||||
/// A callback that will be invoked when the user clicks on a legend.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<LegendClickEventArgs> LegendClick { get; set; }
|
||||
double? Width { get; set; }
|
||||
|
||||
[Inject]
|
||||
TooltipService TooltipService { get; set; }
|
||||
|
||||
double? Height { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the runtime width of the chart.
|
||||
/// </summary>
|
||||
protected double? Width { get; set; }
|
||||
|
||||
double MarginTop { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the runtime height of the chart.
|
||||
/// </summary>
|
||||
protected double? Height { get; set; }
|
||||
|
||||
double MarginLeft { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the top margin of the plot area.
|
||||
/// </summary>
|
||||
protected double MarginTop { get; set; }
|
||||
|
||||
double MarginRight { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the left margin of the plot area.
|
||||
/// </summary>
|
||||
protected double MarginLeft { get; set; }
|
||||
|
||||
double MarginBottom { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the right margin of the plot area.
|
||||
/// </summary>
|
||||
protected double MarginRight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bottom margin of the plot area.
|
||||
/// </summary>
|
||||
protected double MarginBottom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the child content. Used to specify series and other configuration.
|
||||
@@ -93,7 +115,11 @@ namespace Radzen.Blazor
|
||||
Series.Remove(series);
|
||||
}
|
||||
|
||||
private bool ShouldRenderAxes()
|
||||
/// <summary>
|
||||
/// Returns whether the chart should render axes.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool ShouldRenderAxes()
|
||||
{
|
||||
var pieType = typeof(RadzenPieSeries<>);
|
||||
var donutType = typeof(RadzenDonutSeries<>);
|
||||
@@ -111,7 +137,11 @@ namespace Radzen.Blazor
|
||||
return Series.Count > 0 && Series.All(series => series is IChartBarSeries);
|
||||
}
|
||||
|
||||
private bool UpdateScales()
|
||||
/// <summary>
|
||||
/// Updates the scales based on the configuration.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual bool UpdateScales()
|
||||
{
|
||||
var valueScale = ValueScale;
|
||||
var categoryScale = CategoryScale;
|
||||
@@ -207,12 +237,7 @@ namespace Radzen.Blazor
|
||||
ValueScale.Fit(ValueAxis.TickDistance);
|
||||
CategoryScale.Fit(CategoryAxis.TickDistance);
|
||||
|
||||
var stateHasChanged = false;
|
||||
|
||||
if (!ValueScale.IsEqualTo(valueScale))
|
||||
{
|
||||
stateHasChanged = true;
|
||||
}
|
||||
var stateHasChanged = !ValueScale.IsEqualTo(valueScale);
|
||||
|
||||
if (!CategoryScale.IsEqualTo(categoryScale))
|
||||
{
|
||||
@@ -273,6 +298,18 @@ namespace Radzen.Blazor
|
||||
await DisplayTooltip();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The minimum pixel distance from a data point to the mouse cursor required for the SeriesClick event to fire. Set to 25 by default.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int ClickTolerance { get; set; } = 25;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum pixel distance from a data point to the mouse cursor required by the tooltip to show. Set to 25 by default.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int TooltipTolerance { get; set; } = 25;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked via interop when the user clicks the RadzenChart. Raises the <see cref="SeriesClick" /> handler.
|
||||
/// </summary>
|
||||
@@ -281,20 +318,37 @@ namespace Radzen.Blazor
|
||||
[JSInvokable]
|
||||
public async Task Click(double x, double y)
|
||||
{
|
||||
IChartSeries closestSeries = null;
|
||||
object closestSeriesData = null;
|
||||
double closestSeriesDistanceSquared = ClickTolerance * ClickTolerance;
|
||||
|
||||
var queryX = x - MarginLeft;
|
||||
var queryY = y - MarginTop;
|
||||
|
||||
foreach (var series in Series)
|
||||
{
|
||||
if (series.Visible && series.Contains(mouseX - MarginLeft, mouseY - MarginTop, 5))
|
||||
if (series.Visible)
|
||||
{
|
||||
var data = series.DataAt(mouseX - MarginLeft, mouseY - MarginTop);
|
||||
|
||||
if (data != null)
|
||||
var (seriesData, seriesDataPoint) = series.DataAt(queryX, queryY);
|
||||
if (seriesData != null)
|
||||
{
|
||||
await series.InvokeClick(SeriesClick, data);
|
||||
double xDelta = queryX - seriesDataPoint.X;
|
||||
double yDelta = queryY - seriesDataPoint.Y;
|
||||
double squaredDistance = xDelta * xDelta + yDelta * yDelta;
|
||||
if (squaredDistance < closestSeriesDistanceSquared)
|
||||
{
|
||||
closestSeries = series;
|
||||
closestSeriesData = seriesData;
|
||||
closestSeriesDistanceSquared = squaredDistance;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (closestSeriesData != null)
|
||||
{
|
||||
await closestSeries.InvokeClick(SeriesClick, closestSeriesData);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task DisplayTooltip()
|
||||
@@ -302,6 +356,12 @@ namespace Radzen.Blazor
|
||||
if (Tooltip.Visible)
|
||||
{
|
||||
var orderedSeries = Series.OrderBy(s => s.RenderingOrder).Reverse();
|
||||
IChartSeries closestSeries = null;
|
||||
object closestSeriesData = null;
|
||||
double closestSeriesDistanceSquared = TooltipTolerance * TooltipTolerance;
|
||||
|
||||
var queryX = mouseX - MarginLeft;
|
||||
var queryY = mouseY - MarginTop;
|
||||
|
||||
foreach (var series in orderedSeries)
|
||||
{
|
||||
@@ -309,43 +369,62 @@ namespace Radzen.Blazor
|
||||
{
|
||||
foreach (var overlay in series.Overlays.Reverse())
|
||||
{
|
||||
if (overlay.Visible && overlay.Contains(mouseX - MarginLeft, mouseY - MarginTop, 25))
|
||||
if (overlay.Visible && overlay.Contains(queryX, queryY, TooltipTolerance))
|
||||
{
|
||||
tooltipData = null;
|
||||
tooltip = overlay.RenderTooltip(mouseX, mouseY, MarginLeft, MarginTop);
|
||||
StateHasChanged();
|
||||
tooltip = overlay.RenderTooltip(queryX, queryY);
|
||||
var tooltipPosition = overlay.GetTooltipPosition(queryX, queryY);
|
||||
TooltipService.OpenChartTooltip(Element, tooltipPosition.X + MarginLeft, tooltipPosition.Y + MarginTop, _ => tooltip, new ChartTooltipOptions
|
||||
{
|
||||
ColorScheme = ColorScheme
|
||||
});
|
||||
await Task.Yield();
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (series.Contains(mouseX - MarginLeft, mouseY - MarginTop, 25))
|
||||
var (seriesData, seriesDataPoint) = series.DataAt(queryX, queryY);
|
||||
if (seriesData != null)
|
||||
{
|
||||
var data = series.DataAt(mouseX - MarginLeft, mouseY - MarginTop);
|
||||
|
||||
if (data != tooltipData)
|
||||
double xDelta = queryX - seriesDataPoint.X;
|
||||
double yDelta = queryY - seriesDataPoint.Y;
|
||||
double squaredDistance = xDelta * xDelta + yDelta * yDelta;
|
||||
if (squaredDistance < closestSeriesDistanceSquared)
|
||||
{
|
||||
tooltipData = data;
|
||||
tooltip = series.RenderTooltip(data, MarginLeft, MarginTop);
|
||||
StateHasChanged();
|
||||
await Task.Yield();
|
||||
closestSeries = series;
|
||||
closestSeriesData = seriesData;
|
||||
closestSeriesDistanceSquared = squaredDistance;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tooltip != null)
|
||||
if (closestSeriesData != null)
|
||||
{
|
||||
tooltipData = null;
|
||||
tooltip = null;
|
||||
|
||||
StateHasChanged();
|
||||
await Task.Yield();
|
||||
if (closestSeriesData != tooltipData)
|
||||
{
|
||||
tooltipData = closestSeriesData;
|
||||
tooltip = closestSeries.RenderTooltip(closestSeriesData);
|
||||
var tooltipPosition = closestSeries.GetTooltipPosition(closestSeriesData);
|
||||
TooltipService.OpenChartTooltip(Element, tooltipPosition.X + MarginLeft, tooltipPosition.Y + MarginTop, _ => tooltip, new ChartTooltipOptions
|
||||
{
|
||||
ColorScheme = ColorScheme
|
||||
});
|
||||
await Task.Yield();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (tooltip != null)
|
||||
{
|
||||
tooltipData = null;
|
||||
tooltip = null;
|
||||
|
||||
TooltipService.Close();
|
||||
await Task.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
private bool widthAndHeightAreSet = false;
|
||||
|
||||
184
Radzen.Blazor/RadzenChartTooltip.razor
Normal file
184
Radzen.Blazor/RadzenChartTooltip.razor
Normal file
@@ -0,0 +1,184 @@
|
||||
@implements IAsyncDisposable
|
||||
@using Microsoft.JSInterop
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
||||
@foreach (var tooltip in tooltips)
|
||||
{
|
||||
<div id="@UniqueID"
|
||||
style="display: none; top: 0; left: 0; z-index: 1001; position: absolute;"
|
||||
class="@($"rz-scheme-{tooltip.Options.ColorScheme.ToString().ToLowerInvariant()}")">
|
||||
@tooltip.Options.ChildContent(Service)
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
public string UniqueID { get; set; }
|
||||
|
||||
[Inject] private TooltipService Service { get; set; }
|
||||
|
||||
List<ChartTooltip> tooltips = new List<ChartTooltip>();
|
||||
|
||||
async Task Open(ElementReference chart, double x, double y, ChartTooltipOptions options)
|
||||
{
|
||||
tooltips.Clear();
|
||||
tooltips.Add(new ChartTooltip { Options = options, Chart = chart, X = x, Y = y });
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
var tooltip = tooltips.LastOrDefault();
|
||||
|
||||
if (tooltip != null)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.openChartTooltip",
|
||||
tooltip.Chart,
|
||||
x,
|
||||
y,
|
||||
UniqueID);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsJSRuntimeAvailable { get; set; }
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
IsJSRuntimeAvailable = true;
|
||||
|
||||
var tooltip = tooltips.LastOrDefault();
|
||||
|
||||
if (tooltip != null)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.openChartTooltip",
|
||||
tooltip.Chart,
|
||||
tooltip.X,
|
||||
tooltip.Y,
|
||||
UniqueID,
|
||||
Reference,
|
||||
"RadzenChartTooltip.CloseTooltip");
|
||||
}
|
||||
}
|
||||
|
||||
private DotNetObjectReference<RadzenChartTooltip> reference;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reference for the current component.
|
||||
/// </summary>
|
||||
/// <value>The reference.</value>
|
||||
protected DotNetObjectReference<RadzenChartTooltip> Reference
|
||||
{
|
||||
get
|
||||
{
|
||||
if (reference == null)
|
||||
{
|
||||
reference = DotNetObjectReference.Create(this);
|
||||
}
|
||||
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes this instance.
|
||||
/// </summary>
|
||||
[JSInvokable("RadzenChartTooltip.CloseTooltip")]
|
||||
public void CloseTooltip()
|
||||
{
|
||||
Service.Close();
|
||||
}
|
||||
|
||||
public async Task Close()
|
||||
{
|
||||
var lastTooltip = tooltips.LastOrDefault();
|
||||
if (lastTooltip != null)
|
||||
{
|
||||
if (IsJSRuntimeAvailable)
|
||||
{
|
||||
try
|
||||
{
|
||||
tooltips.Remove(lastTooltip);
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.closeTooltip", UniqueID);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
while (tooltips.Count != 0)
|
||||
{
|
||||
await Close();
|
||||
}
|
||||
reference?.Dispose();
|
||||
reference = null;
|
||||
|
||||
if (IsJSRuntimeAvailable)
|
||||
{
|
||||
try
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", UniqueID);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
Service.OnOpenChartTooltip -= OnOpen;
|
||||
Service.OnClose -= OnClose;
|
||||
Service.OnNavigate -= OnNavigate;
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
UniqueID = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("/", "-").Replace("+", "-").Substring(0, 10);
|
||||
|
||||
Service.OnOpenChartTooltip += OnOpen;
|
||||
Service.OnClose += OnClose;
|
||||
Service.OnNavigate += OnNavigate;
|
||||
}
|
||||
|
||||
void OnOpen(ElementReference element, double x, double y, ChartTooltipOptions options)
|
||||
{
|
||||
Open(element, x, y, options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
void OnClose()
|
||||
{
|
||||
Close().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
void OnNavigate()
|
||||
{
|
||||
JSRuntime.InvokeVoidAsync("Radzen.closePopup", UniqueID);
|
||||
}
|
||||
|
||||
private sealed class ChartTooltip
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the owning chart.
|
||||
/// </summary>
|
||||
/// <value>The chart.</value>
|
||||
public ElementReference Chart { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the horizontal position of the tooltip.
|
||||
/// </summary>
|
||||
/// <value>The position.</value>
|
||||
public double X { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the vertical position of the tooltip.
|
||||
/// </summary>
|
||||
/// <value>The position.</value>
|
||||
public double Y { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the options.
|
||||
/// </summary>
|
||||
/// <value>The options.</value>
|
||||
public ChartTooltipOptions Options { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Radzen.Blazor
|
||||
public partial class RadzenChartTooltipOptions : RadzenChartComponentBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show tooltips. By defaults RadzenChart displays tooltips.
|
||||
/// Gets or sets a value indicating whether to show tooltips. By default RadzenChart displays tooltips.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> to display tooltips; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
@@ -21,6 +21,12 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public string Style { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable shared tooltips (one tooltip displaying data for all values for the same category). By default set to false (a separate tooltip is shown for each point in the category).
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool Shared { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Initialize()
|
||||
{
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
@inherits FormComponent<TValue>
|
||||
@if (Visible)
|
||||
{
|
||||
<div @ref="@Element" @attributes="Attributes" class="@GetCssClass()" @onkeypress=@OnKeyPress @onkeypress:preventDefault style="@Style" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" id="@GetId()">
|
||||
<div @ref="@Element" @attributes="Attributes" class="@GetCssClass()" @onkeypress=@OnKeyPress @onkeypress:preventDefault style="@Style" tabindex="@(Disabled || ReadOnly ? "-1" : $"{TabIndex}")" id="@GetId()">
|
||||
<div class="rz-helper-hidden-accessible">
|
||||
<input type="checkbox" @onchange=@Toggle value=@CheckBoxValue name=@Name id=@Name checked=@CheckBoxChecked
|
||||
tabindex="-1">
|
||||
<input type="checkbox" @onchange=@Toggle value=@CheckBoxValue name=@Name id=@Name checked=@CheckBoxChecked aria-checked="@((Value as bool? == true).ToString().ToLowerInvariant())" @attributes="InputAttributes"
|
||||
tabindex="-1" readonly="@ReadOnly">
|
||||
</div>
|
||||
<div class=@BoxClassList @onclick=@Toggle @onclick:preventDefault>
|
||||
<span class=@IconClassList></span>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
@@ -16,6 +17,13 @@ namespace Radzen.Blazor
|
||||
/// </example>
|
||||
public partial class RadzenCheckBox<TValue> : FormComponent<TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies additional custom attributes that will be rendered by the input.
|
||||
/// </summary>
|
||||
/// <value>The attributes.</value>
|
||||
[Parameter]
|
||||
public IReadOnlyDictionary<string, object> InputAttributes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether is tri-state (true, false or null).
|
||||
/// </summary>
|
||||
@@ -27,7 +35,7 @@ namespace Radzen.Blazor
|
||||
.Add("rz-state-active", !object.Equals(Value, false))
|
||||
.AddDisabled(Disabled);
|
||||
|
||||
ClassList IconClassList => ClassList.Create("rz-chkbox-icon")
|
||||
ClassList IconClassList => ClassList.Create("notranslate rz-chkbox-icon")
|
||||
.Add("rzi rzi-check", object.Equals(Value, true))
|
||||
.Add("rzi rzi-times", object.Equals(Value, null));
|
||||
|
||||
@@ -49,9 +57,16 @@ namespace Radzen.Blazor
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether is read only.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is read only; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; }
|
||||
|
||||
async Task Toggle()
|
||||
{
|
||||
if (Disabled)
|
||||
if (Disabled || ReadOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,22 +18,32 @@
|
||||
@if (AllowSelectAll)
|
||||
{
|
||||
<div class="rz-multiselect-header rz-helper-clearfix" @onclick:preventDefault>
|
||||
<RadzenCheckBox Name="SelectAll" TValue="bool?" Value="@IsAllSelected()" Change="@SelectAll" />
|
||||
<RadzenCheckBox ReadOnly="@ReadOnly" Disabled="@Disabled" Name="SelectAll" TValue="bool?" Value="@IsAllSelected()" Change="@SelectAll"
|
||||
InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", SelectAllText }})" />
|
||||
<RadzenLabel Component="SelectAll" Text="@SelectAllText" class="rz-chkbox-label" />
|
||||
</div>
|
||||
}
|
||||
@foreach (var item in allItems.Where(i => i.Visible))
|
||||
{
|
||||
<div @ref="@item.Element" id="@item.GetItemId()" @onclick="@(args => SelectItem(item))" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style">
|
||||
<div class="rz-chkbox " @onkeypress="@(async args => { if (args.Code == "Space") { await SelectItem(item); } })" tabindex="@(Disabled || item.Disabled ? "-1" : $"{TabIndex}")">
|
||||
<div class="rz-chkbox" @onkeypress="@(args => OnKeyPress(args, SelectItem(item)))" @onkeypress:preventDefault=preventKeyPress @onkeypress:stopPropagation tabindex="@(Disabled || ReadOnly || item.Disabled || item.ReadOnly ? "-1" : $"{TabIndex}")">
|
||||
<div class="rz-helper-hidden-accessible">
|
||||
<input type="checkbox" name="@Name" value="@item.Value" disabled="@Disabled" tabindex="-1">
|
||||
<input type="checkbox" name="@Name" value="@item.Value" disabled="@Disabled" readonly="@ReadOnly" tabindex="-1" aria-label="@(item.Text + " " + item.Value)" aria-checked="@(IsSelected(item).ToString().ToLowerInvariant())">
|
||||
</div>
|
||||
<div class=@ItemClassList(item)>
|
||||
<span class=@IconClassList(item)></span>
|
||||
</div>
|
||||
</div>
|
||||
<label class="rz-chkbox-label">@item.Text</label>
|
||||
@if (item.Template != null)
|
||||
{
|
||||
<div class="rz-chkbox-template" @onkeydown:stopPropagation>
|
||||
@item.Template(item)
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<label class="rz-chkbox-label" aria-hidden="true">@((MarkupString)item.Text)</label>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Radzen.Blazor
|
||||
.AddDisabled(Disabled || item.Disabled);
|
||||
|
||||
ClassList IconClassList(RadzenCheckBoxListItem<TValue> item) => ClassList.Create("rz-chkbox-icon")
|
||||
.Add("rzi rzi-check", IsSelected(item));
|
||||
.Add("notranslate rzi rzi-check", IsSelected(item));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value property.
|
||||
@@ -46,6 +46,20 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public string TextProperty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the disabled property.
|
||||
/// </summary>
|
||||
/// <value>The disabled property.</value>
|
||||
[Parameter]
|
||||
public string DisabledProperty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the read-only property.
|
||||
/// </summary>
|
||||
/// <value>The read-only property.</value>
|
||||
[Parameter]
|
||||
public string ReadOnlyProperty { get; set; }
|
||||
|
||||
IEnumerable<RadzenCheckBoxListItem<TValue>> allItems
|
||||
{
|
||||
get
|
||||
@@ -55,6 +69,17 @@ namespace Radzen.Blazor
|
||||
var item = new RadzenCheckBoxListItem<TValue>();
|
||||
item.SetText((string)PropertyAccess.GetItemOrValueFromProperty(i, TextProperty));
|
||||
item.SetValue((TValue)PropertyAccess.GetItemOrValueFromProperty(i, ValueProperty));
|
||||
|
||||
if (DisabledProperty != null && PropertyAccess.TryGetItemOrValueFromProperty<bool>(i, DisabledProperty, out var disabledResult))
|
||||
{
|
||||
item.SetDisabled(disabledResult);
|
||||
}
|
||||
|
||||
if (ReadOnlyProperty != null && PropertyAccess.TryGetItemOrValueFromProperty<bool>(i, ReadOnlyProperty, out var readOnlyResult))
|
||||
{
|
||||
item.SetReadOnly(readOnlyResult);
|
||||
}
|
||||
|
||||
return item;
|
||||
}));
|
||||
}
|
||||
@@ -76,14 +101,14 @@ namespace Radzen.Blazor
|
||||
|
||||
async Task SelectAll(bool? value)
|
||||
{
|
||||
if (Disabled)
|
||||
if (Disabled || ReadOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == true)
|
||||
{
|
||||
Value = items.Select(i => i.Value);
|
||||
Value = allItems.Where(i => !i.Disabled).Select(i => i.Value);
|
||||
}
|
||||
else if (value == false)
|
||||
{
|
||||
@@ -100,8 +125,8 @@ namespace Radzen.Blazor
|
||||
bool? IsAllSelected()
|
||||
{
|
||||
Func<RadzenCheckBoxListItem<TValue>, bool> predicate = i => Value != null && Value.Contains(i.Value);
|
||||
var all = items.All(predicate);
|
||||
var any = items.Any(predicate);
|
||||
var all = allItems.All(predicate);
|
||||
var any = allItems.Any(predicate);
|
||||
|
||||
if (all)
|
||||
{
|
||||
@@ -114,6 +139,7 @@ namespace Radzen.Blazor
|
||||
}
|
||||
|
||||
IEnumerable _data = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data used to generate items.
|
||||
/// </summary>
|
||||
@@ -135,6 +161,13 @@ namespace Radzen.Blazor
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether is read only.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is read only; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string GetComponentCssClass()
|
||||
{
|
||||
@@ -214,7 +247,7 @@ namespace Radzen.Blazor
|
||||
/// <param name="item">The item.</param>
|
||||
protected async System.Threading.Tasks.Task SelectItem(RadzenCheckBoxListItem<TValue> item)
|
||||
{
|
||||
if (Disabled || item.Disabled)
|
||||
if (Disabled || item.Disabled || ReadOnly || item.ReadOnly)
|
||||
return;
|
||||
|
||||
List<TValue> selectedValues = new List<TValue>(Value != null ? Value : Enumerable.Empty<TValue>());
|
||||
@@ -237,9 +270,21 @@ namespace Radzen.Blazor
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private string getDisabledState(RadzenCheckBoxListItem<TValue> item)
|
||||
bool preventKeyPress = true;
|
||||
async Task OnKeyPress(KeyboardEventArgs args, Task task)
|
||||
{
|
||||
return Disabled || item.Disabled ? " rz-state-disabled" : "";
|
||||
var key = args.Code != null ? args.Code : args.Key;
|
||||
|
||||
if (key == "Space" || key == "Enter")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
|
||||
await task;
|
||||
}
|
||||
else
|
||||
{
|
||||
preventKeyPress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,13 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the template.
|
||||
/// </summary>
|
||||
/// <value>The template.</value>
|
||||
[Parameter]
|
||||
public RenderFragment<RadzenCheckBoxListItem<TValue>> Template { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
@@ -29,6 +36,13 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public virtual bool Disabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether is read only.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is read only; otherwise, <c>false</c>.</value>
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; }
|
||||
|
||||
RadzenCheckBoxList<TValue> _checkBoxList;
|
||||
|
||||
/// <summary>
|
||||
@@ -70,6 +84,17 @@ namespace Radzen.Blazor
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
internal void SetDisabled(bool value)
|
||||
{
|
||||
Disabled = value;
|
||||
}
|
||||
|
||||
internal void SetReadOnly(bool value)
|
||||
{
|
||||
ReadOnly = value;
|
||||
}
|
||||
|
||||
internal string GetItemId()
|
||||
{
|
||||
return GetId();
|
||||
|
||||
@@ -5,28 +5,31 @@
|
||||
|
||||
@if (Visible)
|
||||
{
|
||||
<div @ref=@Element style=@Style @onclick=@Toggle @attributes=@Attributes class=@GetCssClass() id=@GetId()>
|
||||
@if(Icon != null)
|
||||
<div @ref=@Element style=@Style @onclick=@Toggle @attributes=@Attributes class=@GetCssClass() id=@GetId() tabindex="@(Disabled ? -1 : TabIndex)" @onkeypress="@(args => OnKeyPress(args, Toggle()))" @onkeypress:preventDefault=preventKeyPress @onkeypress:stopPropagation>
|
||||
@if (Icon != null)
|
||||
{
|
||||
<i class="rzi">@Icon</i>
|
||||
<i class="notranslate rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@Icon</i>
|
||||
}
|
||||
<div class="rz-colorpicker-value" style="background-color: @Color" ></div>
|
||||
<button type="button" tabindex="-1" class="rz-colorpicker-trigger" disabled=@Disabled @onclick:preventDefault><i class="rzi" /></button>
|
||||
<Popup Lazy=@(PopupRenderMode == PopupRenderMode.OnDemand) @ref=@Popup class="rz-colorpicker-popup" Close=@Close Open=@Open>
|
||||
<div class="rz-colorpicker-value" style="background-color: @Value" ></div>
|
||||
<button aria-label="@ToggleAriaLabel" type="button" tabindex="-1" class="rz-colorpicker-trigger" disabled=@Disabled @onclick:preventDefault><i class="notranslate rzi" /></button>
|
||||
<Popup Lazy=@(PopupRenderMode == PopupRenderMode.OnDemand) @ref=@Popup class="rz-colorpicker-popup" Close=@OnClosePopup Open=@Open>
|
||||
@if (ShowHSV)
|
||||
{
|
||||
<Draggable class="rz-saturation-picker rz-colorpicker-section" style=@($"background-color: {HSV.ToRGB().ToCSS()}") Drag=@OnSaturationMove>
|
||||
<Draggable class="rz-saturation-picker rz-colorpicker-section" style=@($"background-color: hsl({(HueHandleLeft * 360).ToInvariantString()}, 100%, 50%)") Drag=@OnSaturationMove
|
||||
id=@(GetId() + "hsl" ) tabindex="@(Disabled ? -1 : 0)" @onkeydown="@(args => OnHslKeyPress(args))">
|
||||
<div class="rz-saturation-white">
|
||||
<div class="rz-saturation-black"></div>
|
||||
<div class="rz-saturation-handle" style=@($"top: {(SaturationHandleTop*100).ToInvariantString()}%; left: {(SaturationHandleLeft * 100).ToInvariantString()}%")></div>
|
||||
<div class="rz-saturation-handle" style=@($"top: {(SaturationHandleTop * 100).ToInvariantString()}%; left: {(SaturationHandleLeft * 100).ToInvariantString()}%")></div>
|
||||
</div>
|
||||
</Draggable>
|
||||
<div class="rz-colorpicker-preview-area rz-colorpicker-section">
|
||||
<div class="rz-hue-and-alpha">
|
||||
<Draggable class="rz-hue-picker" Drag=@OnHueMove>
|
||||
<Draggable class="rz-hue-picker" Drag=@OnHueMove
|
||||
id=@(GetId() + "hue" ) tabindex="@(Disabled ? -1 : 0)" @onkeydown="@(args => OnHueKeyPress(args))">
|
||||
<div class="rz-hue-handle" style=@($"top: 0; left: {(HueHandleLeft * 100).ToInvariantString()}%")></div>
|
||||
</Draggable>
|
||||
<Draggable style=@($"background-image: linear-gradient(to right, {AlphaGradientStart} 0%, {AlphaGradientEnd} 100%)") class="rz-alpha-picker" Drag=@OnAlphaMove>
|
||||
<Draggable style=@($"background-image: linear-gradient(to right, {AlphaGradientStart} 0%, {AlphaGradientEnd} 100%)") class="rz-alpha-picker" Drag=@OnAlphaMove
|
||||
id=@(GetId() + "alpha" ) tabindex="@(Disabled ? -1 : 0)" @onkeydown="@(args => OnAlphaKeyPress(args))">
|
||||
<div class="rz-alpha-handle" style=@($"top: 0; left: {(AlphaHandleLeft * 100).ToInvariantString()}%")></div>
|
||||
</Draggable>
|
||||
</div>
|
||||
@@ -37,32 +40,32 @@
|
||||
{
|
||||
<div class="rz-colorpicker-rgba rz-colorpicker-section" @onmousedown:stopPropagation>
|
||||
<div class="rz-color-box">
|
||||
<RadzenTextBox Value=@Hex @oninput=@(args => ChangeRGB(args.Value)) />
|
||||
<RadzenTextBox Value=@Hex @oninput=@(args => ChangeRGB(args.Value)) aria-label=@HexText />
|
||||
@HexText
|
||||
</div>
|
||||
<div class="rz-color-box">
|
||||
<RadzenNumeric TValue="double" Value=@Red Min="0" Max="255"
|
||||
<RadzenNumeric TValue="double" Value=@Red Min="0" Max="255" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", RedText }})"
|
||||
@oninput=@(args => ChangeColor(args.Value, (color, value) => color.Red = value))
|
||||
Change=@(red => ChangeColor(red, (color, value) => color.Red = value))
|
||||
/>
|
||||
@RedText
|
||||
</div>
|
||||
<div class="rz-color-box">
|
||||
<RadzenNumeric TValue="double" Value=@Green Min="0" Max="255"
|
||||
<RadzenNumeric TValue="double" Value=@Green Min="0" Max="255" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", GreenText }})"
|
||||
@oninput=@(args => ChangeColor(args.Value, (color, value) => color.Green = value))
|
||||
Change=@(green => ChangeColor(green, (color, value) => color.Green = value))
|
||||
/>
|
||||
@GreenText
|
||||
</div>
|
||||
<div class="rz-color-box">
|
||||
<RadzenNumeric TValue="double" Value=@Blue Min="0" Max="255"
|
||||
<RadzenNumeric TValue="double" Value=@Blue Min="0" Max="255" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", BlueText }})"
|
||||
@oninput=@(args => ChangeColor(args.Value, (color, value) => color.Blue = value))
|
||||
Change=@(blue => ChangeColor(blue, (color, value) => color.Blue = value))
|
||||
/>
|
||||
@BlueText
|
||||
</div>
|
||||
<div class="rz-color-box">
|
||||
<RadzenNumeric TValue="double" Value=@Alpha Min="0" Max="100"
|
||||
<RadzenNumeric TValue="double" Value=@Alpha Min="0" Max="100" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", AlphaText }})"
|
||||
@oninput=@(args => ChangeAlpha(args.Value))
|
||||
Change=@(alpha => ChangeAlpha(alpha))
|
||||
/>
|
||||
@@ -107,7 +110,7 @@
|
||||
</div>
|
||||
|
||||
}
|
||||
@if(ShowButton)
|
||||
@if (ShowButton)
|
||||
{
|
||||
<div class="rz-colorpicker-button rz-colorpicker-section">
|
||||
<RadzenButton ButtonStyle="ButtonStyle.Primary" Click=@OnClick Text=@ButtonText @onclick:preventDefault />
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
@@ -16,6 +17,13 @@ namespace Radzen.Blazor
|
||||
/// </example>
|
||||
public partial class RadzenColorPicker : FormComponent<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the toggle popup aria label text.
|
||||
/// </summary>
|
||||
/// <value>The toggle popup aria label text.</value>
|
||||
[Parameter]
|
||||
public string ToggleAriaLabel { get; set; } = "Toggle";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the open callback.
|
||||
/// </summary>
|
||||
@@ -37,6 +45,13 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon color.
|
||||
/// </summary>
|
||||
/// <value>The icon color.</value>
|
||||
[Parameter]
|
||||
public string IconColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the hexadecimal color label text.
|
||||
/// </summary>
|
||||
@@ -151,48 +166,67 @@ namespace Radzen.Blazor
|
||||
}
|
||||
}
|
||||
|
||||
void OnSaturationMove(DraggableEventArgs args)
|
||||
async Task UpdateColorUsingHsvHandles()
|
||||
{
|
||||
SaturationHandleLeft = Math.Clamp((args.ClientX - args.Rect.Left) / args.Rect.Width, 0, 1);
|
||||
SaturationHandleTop = Math.Clamp((args.ClientY - args.Rect.Top) / args.Rect.Height, 0, 1);
|
||||
|
||||
var hsv = new HSV { Hue = HSV.Hue, Saturation = SaturationHandleLeft, Value = 1 - SaturationHandleTop, Alpha = AlphaHandleLeft };
|
||||
var hsv = new HSV {
|
||||
Hue = HueHandleLeft,
|
||||
Saturation = SaturationHandleLeft,
|
||||
Value = 1 - SaturationHandleTop,
|
||||
Alpha = AlphaHandleLeft
|
||||
};
|
||||
|
||||
Color = hsv.ToRGB().ToCSS();
|
||||
|
||||
TriggerChange();
|
||||
await TriggerChange();
|
||||
}
|
||||
|
||||
void TriggerChange()
|
||||
Rect lastHslRect;
|
||||
|
||||
async Task OnSaturationMove(DraggableEventArgs args)
|
||||
{
|
||||
lastHslRect = args.Rect; ;
|
||||
|
||||
SaturationHandleLeft = Math.Clamp((args.ClientX - args.Rect.Left) / args.Rect.Width, 0, 1);
|
||||
SaturationHandleTop = Math.Clamp((args.ClientY - args.Rect.Top) / args.Rect.Height, 0, 1);
|
||||
|
||||
await UpdateColorUsingHsvHandles();
|
||||
}
|
||||
|
||||
async Task TriggerChange()
|
||||
{
|
||||
if (!ShowButton)
|
||||
{
|
||||
ValueChanged.InvokeAsync(Color);
|
||||
Change.InvokeAsync(Color);
|
||||
await OnChanged();
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
void ChangeRGB(object value)
|
||||
async Task OnChanged()
|
||||
{
|
||||
SetValue(value as string);
|
||||
await ValueChanged.InvokeAsync(Color);
|
||||
|
||||
if (FieldIdentifier.FieldName != null)
|
||||
{
|
||||
EditContext?.NotifyFieldChanged(FieldIdentifier);
|
||||
}
|
||||
|
||||
await Change.InvokeAsync(Color);
|
||||
}
|
||||
|
||||
void SetValue(string value)
|
||||
async Task ChangeRGB(object value)
|
||||
{
|
||||
var rgb = RGB.Parse(value);
|
||||
|
||||
var rgb = RGB.Parse(value as string);
|
||||
if (rgb != null)
|
||||
{
|
||||
Color = rgb.ToCSS();
|
||||
UpdateColor(rgb);
|
||||
rgb.Alpha = AlphaHandleLeft;
|
||||
await UpdateColor(rgb);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task SelectColor(string value)
|
||||
{
|
||||
SetValue(value);
|
||||
await UpdateColor(RGB.Parse(value));
|
||||
|
||||
if (!ShowButton)
|
||||
{
|
||||
@@ -200,20 +234,21 @@ namespace Radzen.Blazor
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateColor(RGB rgb)
|
||||
async Task UpdateColor(RGB rgb)
|
||||
{
|
||||
Color = rgb.ToCSS();
|
||||
|
||||
HSV = rgb.ToHSV();
|
||||
var hsv = rgb.ToHSV();
|
||||
|
||||
SaturationHandleLeft = HSV.Saturation;
|
||||
SaturationHandleTop = 1 - HSV.Value;
|
||||
HueHandleLeft = HSV.Hue;
|
||||
SaturationHandleLeft = hsv.Saturation;
|
||||
SaturationHandleTop = 1 - hsv.Value;
|
||||
HueHandleLeft = hsv.Hue;
|
||||
AlphaHandleLeft = hsv.Alpha;
|
||||
|
||||
TriggerChange();
|
||||
await TriggerChange();
|
||||
}
|
||||
|
||||
void ChangeAlpha(double value)
|
||||
async Task ChangeAlpha(double value)
|
||||
{
|
||||
if (value >= 0 && value <= 100)
|
||||
{
|
||||
@@ -222,19 +257,19 @@ namespace Radzen.Blazor
|
||||
|
||||
Color = rgb.ToCSS();
|
||||
|
||||
TriggerChange();
|
||||
await TriggerChange();
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeAlpha(object alpha)
|
||||
async Task ChangeAlpha(object alpha)
|
||||
{
|
||||
if (Double.TryParse((string)alpha, out var value))
|
||||
{
|
||||
ChangeAlpha(value);
|
||||
await ChangeAlpha(value);
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeColor(double value, Action<RGB, double> update)
|
||||
async Task ChangeColor(double value, Action<RGB, double> update)
|
||||
{
|
||||
if (value >= 0 && value <= 255)
|
||||
{
|
||||
@@ -242,50 +277,55 @@ namespace Radzen.Blazor
|
||||
|
||||
update(rgb, value);
|
||||
|
||||
UpdateColor(rgb);
|
||||
await UpdateColor(rgb);
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeColor(object color, Action<RGB, double> update)
|
||||
async Task ChangeColor(object color, Action<RGB, double> update)
|
||||
{
|
||||
if (Double.TryParse((string)color, out var value))
|
||||
{
|
||||
ChangeColor(value, update);
|
||||
await ChangeColor(value, update);
|
||||
}
|
||||
}
|
||||
|
||||
void OnAlphaMove(DraggableEventArgs args)
|
||||
Rect lastAlphaRect;
|
||||
|
||||
async Task OnAlphaMove(DraggableEventArgs args)
|
||||
{
|
||||
lastAlphaRect = args.Rect;
|
||||
|
||||
AlphaHandleLeft = Math.Round(Math.Clamp((args.ClientX - args.Rect.Left) / args.Rect.Width, 0, 1), 2);
|
||||
|
||||
HSV.Alpha = AlphaHandleLeft;
|
||||
|
||||
var hsv = new HSV { Hue = HSV.Hue, Saturation = SaturationHandleLeft, Value = 1 - SaturationHandleTop, Alpha = AlphaHandleLeft };
|
||||
|
||||
Color = hsv.ToRGB().ToCSS();
|
||||
|
||||
TriggerChange();
|
||||
await UpdateColorUsingHsvHandles();
|
||||
}
|
||||
|
||||
void OnHueMove(DraggableEventArgs args)
|
||||
Rect lastHueRect;
|
||||
async Task OnHueMove(DraggableEventArgs args)
|
||||
{
|
||||
lastHueRect = args.Rect;
|
||||
|
||||
HueHandleLeft = Math.Clamp((args.ClientX - args.Rect.Left) / args.Rect.Width, 0, 1);
|
||||
|
||||
HSV.Hue = HueHandleLeft;
|
||||
var hsv = new HSV { Hue = HSV.Hue, Saturation = SaturationHandleLeft, Value = 1 - SaturationHandleTop, Alpha = AlphaHandleLeft };
|
||||
|
||||
Color = hsv.ToRGB().ToCSS();
|
||||
|
||||
TriggerChange();
|
||||
await UpdateColorUsingHsvHandles();
|
||||
}
|
||||
|
||||
async Task OnClick()
|
||||
{
|
||||
await ValueChanged.InvokeAsync(Color);
|
||||
await Change.InvokeAsync(Color);
|
||||
await OnChanged();
|
||||
await Popup.CloseAsync();
|
||||
}
|
||||
|
||||
async Task OnClosePopup()
|
||||
{
|
||||
if (ShowButton)
|
||||
{
|
||||
SetInitialValue();
|
||||
}
|
||||
|
||||
await Close.InvokeAsync(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether button is shown.
|
||||
/// </summary>
|
||||
@@ -328,11 +368,10 @@ namespace Radzen.Blazor
|
||||
[Parameter]
|
||||
public PopupRenderMode PopupRenderMode { get; set; } = PopupRenderMode.Initial;
|
||||
|
||||
double SaturationHandleLeft { get; set; }
|
||||
double HueHandleLeft { get; set; }
|
||||
double SaturationHandleLeft { get; set; } = 0;
|
||||
double SaturationHandleTop { get; set; } = 0;
|
||||
double HueHandleLeft { get; set; } = 0;
|
||||
double AlphaHandleLeft { get; set; } = 1;
|
||||
double SaturationHandleTop { get; set; }
|
||||
HSV HSV { get; set; } = new HSV { Hue = 0, Saturation = 1, Value = 1 };
|
||||
string Color { get; set; } = "rgb(255, 255, 255)";
|
||||
|
||||
async Task Toggle()
|
||||
@@ -345,25 +384,18 @@ namespace Radzen.Blazor
|
||||
/// <inheritdoc />
|
||||
protected override string GetComponentCssClass()
|
||||
{
|
||||
var classList = new List<string>() { "rz-colorpicker" };
|
||||
|
||||
if (Disabled)
|
||||
{
|
||||
classList.Add("rz-disabled");
|
||||
}
|
||||
|
||||
return string.Join(" ", classList);
|
||||
return GetClassList("rz-colorpicker").ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Init();
|
||||
SetInitialValue();
|
||||
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
void Init()
|
||||
void SetInitialValue()
|
||||
{
|
||||
var value = Value;
|
||||
|
||||
@@ -376,17 +408,11 @@ namespace Radzen.Blazor
|
||||
{
|
||||
Color = value;
|
||||
|
||||
HSV = RGB.Parse(Color).ToHSV();
|
||||
SaturationHandleLeft = HSV.Saturation;
|
||||
SaturationHandleTop = 1 - HSV.Value;
|
||||
HSV.Saturation = 1;
|
||||
HSV.Value = 1;
|
||||
HueHandleLeft = HSV.Hue;
|
||||
|
||||
if (value.StartsWith("rgba"))
|
||||
{
|
||||
AlphaHandleLeft = HSV.Alpha;
|
||||
}
|
||||
var hsv = RGB.Parse(Color).ToHSV();
|
||||
SaturationHandleLeft = hsv.Saturation;
|
||||
SaturationHandleTop = 1 - hsv.Value;
|
||||
HueHandleLeft = hsv.Hue;
|
||||
AlphaHandleLeft = hsv.Alpha;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,8 +425,115 @@ namespace Radzen.Blazor
|
||||
|
||||
if (valueChanged)
|
||||
{
|
||||
Init();
|
||||
SetInitialValue();
|
||||
}
|
||||
}
|
||||
|
||||
async Task OnHueKeyPress(KeyboardEventArgs args)
|
||||
{
|
||||
var key = args.Code != null ? args.Code : args.Key;
|
||||
|
||||
if (key == "ArrowLeft" || key == "ArrowRight")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
|
||||
if (lastHueRect == null)
|
||||
{
|
||||
lastHueRect = await JSRuntime.InvokeAsync<Rect>("Radzen.clientRect", (GetId() + "hue"));
|
||||
}
|
||||
|
||||
await OnHueMove(new DraggableEventArgs() { Rect = lastHueRect, ClientX = lastHueRect.Left + lastHueRect.Width * HueHandleLeft + (key == "ArrowLeft" ? -1 : 1) });
|
||||
}
|
||||
else if (key == "Escape")
|
||||
{
|
||||
await ClosePopup();
|
||||
}
|
||||
else
|
||||
{
|
||||
preventKeyPress = false;
|
||||
}
|
||||
}
|
||||
|
||||
async Task OnAlphaKeyPress(KeyboardEventArgs args)
|
||||
{
|
||||
var key = args.Code != null ? args.Code : args.Key;
|
||||
|
||||
if (key == "ArrowLeft" || key == "ArrowRight")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
|
||||
if (lastAlphaRect == null)
|
||||
{
|
||||
lastAlphaRect = await JSRuntime.InvokeAsync<Rect>("Radzen.clientRect", (GetId() + "alpha"));
|
||||
}
|
||||
|
||||
await OnAlphaMove(new DraggableEventArgs() { Rect = lastAlphaRect, ClientX = lastAlphaRect.Left + lastAlphaRect.Width * AlphaHandleLeft + (key == "ArrowLeft" ? -3 : 3) });
|
||||
}
|
||||
else if (key == "Escape")
|
||||
{
|
||||
await ClosePopup();
|
||||
}
|
||||
else
|
||||
{
|
||||
preventKeyPress = false;
|
||||
}
|
||||
}
|
||||
|
||||
async Task OnHslKeyPress(KeyboardEventArgs args)
|
||||
{
|
||||
var key = args.Code != null ? args.Code : args.Key;
|
||||
|
||||
if (lastHslRect == null)
|
||||
{
|
||||
lastHslRect = await JSRuntime.InvokeAsync<Rect>("Radzen.clientRect", (GetId() + "hsl"));
|
||||
}
|
||||
|
||||
if (key == "ArrowLeft" || key == "ArrowRight" || key == "ArrowUp" || key == "ArrowDown")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
|
||||
await OnSaturationMove(new DraggableEventArgs()
|
||||
{
|
||||
Rect = lastHslRect,
|
||||
ClientX = lastHslRect.Left + lastHslRect.Width * SaturationHandleLeft + (key == "ArrowLeft" ? -1 : key == "ArrowRight" ? 1 : 0),
|
||||
ClientY = lastHslRect.Top + lastHslRect.Height * SaturationHandleTop + (key == "ArrowUp" ? -1 : key == "ArrowDown" ? 1 : 0)
|
||||
});
|
||||
}
|
||||
else if (key == "Escape")
|
||||
{
|
||||
await ClosePopup();
|
||||
}
|
||||
else
|
||||
{
|
||||
preventKeyPress = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool preventKeyPress = false;
|
||||
async Task OnKeyPress(KeyboardEventArgs args, Task task)
|
||||
{
|
||||
var key = args.Code != null ? args.Code : args.Key;
|
||||
|
||||
if (key == "Space" || key == "Enter")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
|
||||
await task;
|
||||
}
|
||||
else if (key == "Escape")
|
||||
{
|
||||
await ClosePopup();
|
||||
}
|
||||
else
|
||||
{
|
||||
preventKeyPress = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task ClosePopup()
|
||||
{
|
||||
await Popup.CloseAsync();
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", GetId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@using Radzen.Blazor.Rendering
|
||||
|
||||
<div class="rz-colorpicker-item" style="background-color: @Background" @onmousedown:preventDefault @onclick=@OnClick></div>
|
||||
<div class="rz-colorpicker-item" style="background-color: @Background" @onmousedown:preventDefault @onclick=@OnClick
|
||||
tabindex="@(ColorPicker.Disabled ? -1 : 0)" @onkeypress="@(args => OnKeyPress(args, OnClick()))" @onkeypress:preventDefault=preventKeyPress @onkeypress:stopPropagation></div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user