Compare commits
10 Commits
barret-deb
...
config-cle
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
281dffde10 | ||
|
|
0f714cff34 | ||
|
|
98f17e0cd2 | ||
|
|
9b2c04f298 | ||
|
|
ed4a97154d | ||
|
|
9dcd62f944 | ||
|
|
213c645524 | ||
|
|
f1c0ac2b30 | ||
|
|
16c6d55f60 | ||
|
|
6e40a3dd39 |
@@ -6,7 +6,6 @@ extends:
|
||||
- 'eslint:recommended'
|
||||
- 'plugin:@typescript-eslint/recommended'
|
||||
- 'plugin:jest/recommended'
|
||||
- 'prettier/@typescript-eslint'
|
||||
- 'plugin:prettier/recommended'
|
||||
- 'plugin:jest-dom/recommended'
|
||||
globals:
|
||||
@@ -64,6 +63,7 @@ rules:
|
||||
- error
|
||||
- default: array-simple
|
||||
readonly: array-simple
|
||||
|
||||
"@typescript-eslint/consistent-indexed-object-style":
|
||||
- error
|
||||
- index-signature
|
||||
|
||||
55
.yarn/releases/yarn-2.4.0.cjs
vendored
783
.yarn/releases/yarn-3.2.3.cjs
vendored
Executable file
@@ -6,4 +6,4 @@ plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-2.4.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-3.2.3.cjs
|
||||
|
||||
@@ -198,6 +198,7 @@ Collate:
|
||||
'version_bs_date_picker.R'
|
||||
'version_ion_range_slider.R'
|
||||
'version_jquery.R'
|
||||
'version_jqueryui.R'
|
||||
'version_selectize.R'
|
||||
'version_strftime.R'
|
||||
'viewer.R'
|
||||
|
||||
11
NEWS.md
@@ -1,6 +1,17 @@
|
||||
shiny 1.7.2.9000
|
||||
================
|
||||
|
||||
## Full changelog
|
||||
|
||||
### Breaking changes
|
||||
|
||||
### New features and improvements
|
||||
|
||||
* Internal: Added clearer and strict TypeScript type definitions (#3644)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Closed #3687: Updated jQuery-UI to v1.13.2. (#3697)
|
||||
|
||||
|
||||
shiny 1.7.2
|
||||
|
||||
10
R/jqueryui.R
@@ -103,10 +103,10 @@ fixedPanel <- function(...,
|
||||
|
||||
jqueryuiDependency <- function() {
|
||||
htmlDependency(
|
||||
'jqueryui',
|
||||
'1.12.1',
|
||||
src = 'www/shared/jqueryui',
|
||||
package = 'shiny',
|
||||
script = 'jquery-ui.min.js'
|
||||
"jqueryui",
|
||||
version_jqueryui,
|
||||
src = "www/shared/jqueryui",
|
||||
package = "shiny",
|
||||
script = "jquery-ui.min.js"
|
||||
)
|
||||
}
|
||||
|
||||
2
R/version_jqueryui.R
Normal file
@@ -0,0 +1,2 @@
|
||||
# Generated by tools/updatejQueryUI.R; do not edit by hand
|
||||
version_jqueryui <- "1.13.2"
|
||||
1
inst/www/shared/jquery.min.js
vendored
1
inst/www/shared/jquery.min.js.map
Normal file
@@ -312,7 +312,7 @@ Mani Mishra <manimishra902@gmail.com>
|
||||
Hannah Methvin <hannahmethvin@gmail.com>
|
||||
Leonardo Balter <leonardo.balter@gmail.com>
|
||||
Benjamin Albert <benjamin_a5@yahoo.com>
|
||||
Michał Gołębiowski <m.goleb@gmail.com>
|
||||
Michał Gołębiowski-Owczarek <m.goleb@gmail.com>
|
||||
Alyosha Pushak <alyosha.pushak@gmail.com>
|
||||
Fahad Ahmad <fahadahmad41@hotmail.com>
|
||||
Matt Brundage <github@mattbrundage.com>
|
||||
@@ -331,3 +331,42 @@ Peter Dave Hello <hsu@peterdavehello.org>
|
||||
Johannes Schäfer <johnschaefer@gmx.de>
|
||||
Ville Skyttä <ville.skytta@iki.fi>
|
||||
Ryan Oriecuia <ryan.oriecuia@visioncritical.com>
|
||||
Sergei Ratnikov <sergeir82@gmail.com>
|
||||
milk54 <milk851@gmail.com>
|
||||
Evelyn Masso <evoutofambit@gmail.com>
|
||||
Robin <mail@robin-fowler.com>
|
||||
Simon Asika <asika32764@gmail.com>
|
||||
Kevin Cupp <kevin.cupp@gmail.com>
|
||||
Jeremy Mickelson <Jeremy.Mickelson@gmail.com>
|
||||
Kyle Rosenberg <kyle.rosenberg@gmail.com>
|
||||
Petri Partio <petri.partio@gmail.com>
|
||||
pallxk <github@pallxk.com>
|
||||
Luke Brookhart <luke@onjax.com>
|
||||
claudi <hirt-claudia@gmx.de>
|
||||
Eirik Sletteberg <eiriksletteberg@gmail.com>
|
||||
Albert Johansson <albert@intervaro.se>
|
||||
A. Wells <borgboyone@users.noreply.github.com>
|
||||
Robert Brignull <robertbrignull@gmail.com>
|
||||
Horus68 <pauloizidoro@gmail.com>
|
||||
Maksymenkov Eugene <foatei@gmail.com>
|
||||
OskarNS <soerensen.oskar@gmail.com>
|
||||
Gez Quinn <holla@gezquinn.design>
|
||||
jigar gala <jigar.gala140291@gmail.com>
|
||||
Florian Wegscheider <flo.wegscheider@gmail.com>
|
||||
Fatér Zsolt <fater.zsolt@gmail.com>
|
||||
Szabolcs Szabolcsi-Toth <nec@shell8.net>
|
||||
Jérémy Munsch <github@jeremydev.ovh>
|
||||
Hrvoje Novosel <hrvoje.novosel@gmail.com>
|
||||
Paul Capron <PaulCapron@users.noreply.github.com>
|
||||
Micah Miller <mikhey@runbox.com>
|
||||
sakshi87 <53863764+sakshi87@users.noreply.github.com>
|
||||
Mikolaj Wolicki <wolicki.mikolaj@gmail.com>
|
||||
Patrick McKay <patrick.mckay@vumc.org>
|
||||
c-lambert <58025159+c-lambert@users.noreply.github.com>
|
||||
Josep Sanz <josepsanzcamp@gmail.com>
|
||||
Ben Mullins <benm@umich.edu>
|
||||
Christian Oliff <christianoliff@pm.me>
|
||||
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
|
||||
Adam Lidén Hällgren <adamlh92@gmail.com>
|
||||
James Hinderks <hinderks@gmail.com>
|
||||
Denny Septian Panggabean <97607754+ddevsr@users.noreply.github.com>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
This a full jQuery UI build, downloaded from:
|
||||
https://jqueryui.com/resources/download/jquery-ui-1.12.1.zip
|
||||
|
||||
The copy of jQuery that is bundled with the download, under external/, is not
|
||||
included because Shiny already has its own copy of jQuery.
|
||||
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.4 KiB |
@@ -58,7 +58,6 @@
|
||||
|
||||
<h1>YOUR COMPONENTS:</h1>
|
||||
|
||||
|
||||
<!-- Accordion -->
|
||||
<h2 class="demoHeaders">Accordion</h2>
|
||||
<div id="accordion">
|
||||
@@ -70,23 +69,17 @@
|
||||
<div>Nam dui erat, auctor a, dignissim quis.</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Autocomplete -->
|
||||
<h2 class="demoHeaders">Autocomplete</h2>
|
||||
<div>
|
||||
<input id="autocomplete" title="type "a"">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Button -->
|
||||
<h2 class="demoHeaders">Button</h2>
|
||||
<button id="button">A button element</button>
|
||||
<button id="button-icon">An icon-only button</button>
|
||||
|
||||
|
||||
|
||||
<!-- Checkboxradio -->
|
||||
<h2 class="demoHeaders">Checkboxradio</h2>
|
||||
<form style="margin-top: 1em;">
|
||||
@@ -97,8 +90,6 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<!-- Controlgroup -->
|
||||
<h2 class="demoHeaders">Controlgroup</h2>
|
||||
<fieldset>
|
||||
@@ -125,8 +116,6 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
|
||||
|
||||
<!-- Tabs -->
|
||||
<h2 class="demoHeaders">Tabs</h2>
|
||||
<div id="tabs">
|
||||
@@ -140,8 +129,6 @@
|
||||
<div id="tabs-3">Nam dui erat, auctor a, dignissim quis, sollicitudin eu, felis. Pellentesque nisi urna, interdum eget, sagittis et, consequat vestibulum, lacus. Mauris porttitor ullamcorper augue.</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<h2 class="demoHeaders">Dialog</h2>
|
||||
<p>
|
||||
<button id="dialog-link" class="ui-button ui-corner-all ui-widget">
|
||||
@@ -167,7 +154,6 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<h2 class="demoHeaders">Framework Icons (content color preview)</h2>
|
||||
<ul id="icons" class="ui-widget ui-helper-clearfix">
|
||||
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-1-n"><span class="ui-icon ui-icon-caret-1-n"></span></li>
|
||||
@@ -345,25 +331,18 @@
|
||||
<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-diagonal-se"><span class="ui-icon ui-icon-grip-diagonal-se"></span></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<!-- Slider -->
|
||||
<h2 class="demoHeaders">Slider</h2>
|
||||
<div id="slider"></div>
|
||||
|
||||
|
||||
|
||||
<!-- Datepicker -->
|
||||
<h2 class="demoHeaders">Datepicker</h2>
|
||||
<div id="datepicker"></div>
|
||||
|
||||
|
||||
|
||||
<!-- Progressbar -->
|
||||
<h2 class="demoHeaders">Progressbar</h2>
|
||||
<div id="progressbar"></div>
|
||||
|
||||
|
||||
|
||||
<!-- Progressbar -->
|
||||
<h2 class="demoHeaders">Selectmenu</h2>
|
||||
<select id="selectmenu">
|
||||
@@ -374,14 +353,10 @@
|
||||
<option>Faster</option>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
<!-- Spinner -->
|
||||
<h2 class="demoHeaders">Spinner</h2>
|
||||
<input id="spinner">
|
||||
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
<h2 class="demoHeaders">Menu</h2>
|
||||
<ul style="width:100px;" id="menu">
|
||||
@@ -400,8 +375,6 @@
|
||||
<li><div>Item 5</div></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<!-- Tooltip -->
|
||||
<h2 class="demoHeaders">Tooltip</h2>
|
||||
<p id="tooltip">
|
||||
@@ -409,7 +382,6 @@
|
||||
the element with your mouse, the title attribute is displayed in a little box next to the element, just like a native tooltip.
|
||||
</p>
|
||||
|
||||
|
||||
<!-- Highlight / Error -->
|
||||
<h2 class="demoHeaders">Highlight / Error</h2>
|
||||
<div class="ui-widget">
|
||||
@@ -429,11 +401,8 @@ the element with your mouse, the title attribute is displayed in a little box ne
|
||||
<script src="external/jquery/jquery.js"></script>
|
||||
<script src="jquery-ui.js"></script>
|
||||
<script>
|
||||
|
||||
$( "#accordion" ).accordion();
|
||||
|
||||
|
||||
|
||||
var availableTags = [
|
||||
"ActionScript",
|
||||
"AppleScript",
|
||||
@@ -462,28 +431,18 @@ $( "#autocomplete" ).autocomplete({
|
||||
source: availableTags
|
||||
});
|
||||
|
||||
|
||||
|
||||
$( "#button" ).button();
|
||||
$( "#button-icon" ).button({
|
||||
icon: "ui-icon-gear",
|
||||
showLabel: false
|
||||
});
|
||||
|
||||
|
||||
|
||||
$( "#radioset" ).buttonset();
|
||||
|
||||
|
||||
|
||||
$( "#controlgroup" ).controlgroup();
|
||||
|
||||
|
||||
|
||||
$( "#tabs" ).tabs();
|
||||
|
||||
|
||||
|
||||
$( "#dialog" ).dialog({
|
||||
autoOpen: false,
|
||||
width: 400,
|
||||
@@ -509,42 +468,27 @@ $( "#dialog-link" ).click(function( event ) {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
|
||||
$( "#datepicker" ).datepicker({
|
||||
inline: true
|
||||
});
|
||||
|
||||
|
||||
|
||||
$( "#slider" ).slider({
|
||||
range: true,
|
||||
values: [ 17, 67 ]
|
||||
});
|
||||
|
||||
|
||||
|
||||
$( "#progressbar" ).progressbar({
|
||||
value: 20
|
||||
});
|
||||
|
||||
|
||||
|
||||
$( "#spinner" ).spinner();
|
||||
|
||||
|
||||
|
||||
$( "#menu" ).menu();
|
||||
|
||||
|
||||
|
||||
$( "#tooltip" ).tooltip();
|
||||
|
||||
|
||||
|
||||
$( "#selectmenu" ).selectmenu();
|
||||
|
||||
|
||||
// Hover states on the static widgets
|
||||
$( "#dialog-link, #icons li" ).hover(
|
||||
function() {
|
||||
|
||||
21
inst/www/shared/jqueryui/jquery-ui.css
vendored
@@ -1,4 +1,4 @@
|
||||
/*! jQuery UI - v1.12.1 - 2016-09-14
|
||||
/*! jQuery UI - v1.13.2 - 2022-07-14
|
||||
* http://jqueryui.com
|
||||
* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6
|
||||
@@ -45,7 +45,7 @@
|
||||
left: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
filter:Alpha(Opacity=0); /* support: IE8 */
|
||||
-ms-filter: "alpha(opacity=0)"; /* support: IE8 */
|
||||
}
|
||||
|
||||
.ui-front {
|
||||
@@ -664,7 +664,7 @@ button.ui-button::-moz-focus-inner {
|
||||
.ui-progressbar .ui-progressbar-overlay {
|
||||
background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
|
||||
height: 100%;
|
||||
filter: alpha(opacity=25); /* support: IE8 */
|
||||
-ms-filter: "alpha(opacity=25)"; /* support: IE8 */
|
||||
opacity: 0.25;
|
||||
}
|
||||
.ui-progressbar-indeterminate .ui-progressbar-value {
|
||||
@@ -728,7 +728,7 @@ button.ui-button::-moz-focus-inner {
|
||||
z-index: 2;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
@@ -1041,18 +1041,18 @@ a.ui-button:active,
|
||||
.ui-widget-content .ui-priority-secondary,
|
||||
.ui-widget-header .ui-priority-secondary {
|
||||
opacity: .7;
|
||||
filter:Alpha(Opacity=70); /* support: IE8 */
|
||||
-ms-filter: "alpha(opacity=70)"; /* support: IE8 */
|
||||
font-weight: normal;
|
||||
}
|
||||
.ui-state-disabled,
|
||||
.ui-widget-content .ui-state-disabled,
|
||||
.ui-widget-header .ui-state-disabled {
|
||||
opacity: .35;
|
||||
filter:Alpha(Opacity=35); /* support: IE8 */
|
||||
-ms-filter: "alpha(opacity=35)"; /* support: IE8 */
|
||||
background-image: none;
|
||||
}
|
||||
.ui-state-disabled .ui-icon {
|
||||
filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
|
||||
-ms-filter: "alpha(opacity=35)"; /* support: IE8 - See #6059 */
|
||||
}
|
||||
|
||||
/* Icons
|
||||
@@ -1093,7 +1093,10 @@ a.ui-button:active,
|
||||
}
|
||||
|
||||
/* positioning */
|
||||
.ui-icon-blank { background-position: 16px 16px; }
|
||||
/* Three classes needed to override `.ui-button:hover .ui-icon` */
|
||||
.ui-icon-blank.ui-icon-blank.ui-icon-blank {
|
||||
background-image: none;
|
||||
}
|
||||
.ui-icon-caret-1-n { background-position: 0 0; }
|
||||
.ui-icon-caret-1-ne { background-position: -16px 0; }
|
||||
.ui-icon-caret-1-e { background-position: -32px 0; }
|
||||
@@ -1304,7 +1307,7 @@ a.ui-button:active,
|
||||
.ui-widget-overlay {
|
||||
background: #aaaaaa;
|
||||
opacity: .003;
|
||||
filter: Alpha(Opacity=.3); /* support: IE8 */
|
||||
-ms-filter: Alpha(Opacity=.3); /* support: IE8 */
|
||||
}
|
||||
.ui-widget-shadow {
|
||||
-webkit-box-shadow: 0px 0px 5px #666666;
|
||||
|
||||
1806
inst/www/shared/jqueryui/jquery-ui.js
vendored
4
inst/www/shared/jqueryui/jquery-ui.min.css
vendored
13
inst/www/shared/jqueryui/jquery-ui.min.js
vendored
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* jQuery UI CSS Framework 1.12.1
|
||||
* jQuery UI CSS Framework 1.13.2
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
@@ -49,7 +49,7 @@
|
||||
left: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
filter:Alpha(Opacity=0); /* support: IE8 */
|
||||
-ms-filter: "alpha(opacity=0)"; /* support: IE8 */
|
||||
}
|
||||
|
||||
.ui-front {
|
||||
@@ -668,7 +668,7 @@ button.ui-button::-moz-focus-inner {
|
||||
.ui-progressbar .ui-progressbar-overlay {
|
||||
background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
|
||||
height: 100%;
|
||||
filter: alpha(opacity=25); /* support: IE8 */
|
||||
-ms-filter: "alpha(opacity=25)"; /* support: IE8 */
|
||||
opacity: 0.25;
|
||||
}
|
||||
.ui-progressbar-indeterminate .ui-progressbar-value {
|
||||
@@ -732,7 +732,7 @@ button.ui-button::-moz-focus-inner {
|
||||
z-index: 2;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
15
inst/www/shared/jqueryui/jquery-ui.theme.css
vendored
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* jQuery UI CSS Framework 1.12.1
|
||||
* jQuery UI CSS Framework 1.13.2
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
@@ -172,18 +172,18 @@ a.ui-button:active,
|
||||
.ui-widget-content .ui-priority-secondary,
|
||||
.ui-widget-header .ui-priority-secondary {
|
||||
opacity: .7;
|
||||
filter:Alpha(Opacity=70); /* support: IE8 */
|
||||
-ms-filter: "alpha(opacity=70)"; /* support: IE8 */
|
||||
font-weight: normal;
|
||||
}
|
||||
.ui-state-disabled,
|
||||
.ui-widget-content .ui-state-disabled,
|
||||
.ui-widget-header .ui-state-disabled {
|
||||
opacity: .35;
|
||||
filter:Alpha(Opacity=35); /* support: IE8 */
|
||||
-ms-filter: "alpha(opacity=35)"; /* support: IE8 */
|
||||
background-image: none;
|
||||
}
|
||||
.ui-state-disabled .ui-icon {
|
||||
filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
|
||||
-ms-filter: "alpha(opacity=35)"; /* support: IE8 - See #6059 */
|
||||
}
|
||||
|
||||
/* Icons
|
||||
@@ -224,7 +224,10 @@ a.ui-button:active,
|
||||
}
|
||||
|
||||
/* positioning */
|
||||
.ui-icon-blank { background-position: 16px 16px; }
|
||||
/* Three classes needed to override `.ui-button:hover .ui-icon` */
|
||||
.ui-icon-blank.ui-icon-blank.ui-icon-blank {
|
||||
background-image: none;
|
||||
}
|
||||
.ui-icon-caret-1-n { background-position: 0 0; }
|
||||
.ui-icon-caret-1-ne { background-position: -16px 0; }
|
||||
.ui-icon-caret-1-e { background-position: -32px 0; }
|
||||
@@ -435,7 +438,7 @@ a.ui-button:active,
|
||||
.ui-widget-overlay {
|
||||
background: #aaaaaa;
|
||||
opacity: .003;
|
||||
filter: Alpha(Opacity=.3); /* support: IE8 */
|
||||
-ms-filter: Alpha(Opacity=.3); /* support: IE8 */
|
||||
}
|
||||
.ui-widget-shadow {
|
||||
-webkit-box-shadow: 0px 0px 5px #666666;
|
||||
|
||||
2
inst/www/shared/shiny.min.js
vendored
32
package.json
@@ -32,33 +32,33 @@
|
||||
"@babel/preset-env": "^7.14.2",
|
||||
"@babel/preset-typescript": "^7.13.0",
|
||||
"@babel/runtime": "^7.14.0",
|
||||
"@deanc/esbuild-plugin-postcss": "^1.0.1",
|
||||
"@deanc/esbuild-plugin-postcss": "^1.0.2",
|
||||
"@testing-library/dom": "^7.31.0",
|
||||
"@testing-library/jest-dom": "^5.12.0",
|
||||
"@testing-library/user-event": "^13.1.9",
|
||||
"@types/highlightjs": "^9.12.1",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/jqueryui": "1.12.15",
|
||||
"@types/jqueryui": "1.12.16",
|
||||
"@types/lodash": "^4.14.170",
|
||||
"@types/node": "^15.6.1",
|
||||
"@types/showdown": "^1.9.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.38.1",
|
||||
"@typescript-eslint/parser": "^5.38.1",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"bootstrap-datepicker": "1.9.0",
|
||||
"browserslist": "^4.19.1",
|
||||
"caniuse-lite": "^1.0.30001312",
|
||||
"core-js": "^3.13.0",
|
||||
"esbuild": "^0.12.4",
|
||||
"esbuild": "^0.15.10",
|
||||
"esbuild-plugin-babel": "https://github.com/schloerke/esbuild-plugin-babel#patch-2",
|
||||
"esbuild-plugin-globals": "^0.1.1",
|
||||
"esbuild-plugin-sass": "^0.5.2",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"eslint-plugin-jest": "^24.3.6",
|
||||
"eslint-plugin-jest-dom": "^3.9.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-unicorn": "^33.0.1",
|
||||
"esbuild-plugin-sass": "^1.0.1",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-jest": "^27.0.4",
|
||||
"eslint-plugin-jest-dom": "^4.0.2",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-unicorn": "^43.0.2",
|
||||
"ion-rangeslider": "2.3.1",
|
||||
"jest": "^26.6.3",
|
||||
"jquery": "3.6.0",
|
||||
@@ -67,15 +67,15 @@
|
||||
"node-gyp": "^8.1.0",
|
||||
"phantomjs-prebuilt": "^2.1.16",
|
||||
"postcss": "^8.3.5",
|
||||
"prettier": "2.3.0",
|
||||
"prettier": "^2.7.1",
|
||||
"readcontrol": "^1.0.0",
|
||||
"replace": "^1.2.1",
|
||||
"selectize": "0.12.4",
|
||||
"strftime": "0.9.2",
|
||||
"ts-jest": "^26",
|
||||
"ts-node": "^10.0.0",
|
||||
"type-coverage": "^2.17.5",
|
||||
"typescript": "~4.1.5",
|
||||
"ts-node": "^10.9.1",
|
||||
"type-coverage": "^2.22.0",
|
||||
"typescript": "^4.8.4",
|
||||
"util-inspect": "https://github.com/deecewan/browser-util-inspect#c0b4350df4378ffd743e8c36dd3898ce3992823e"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import {
|
||||
build as esbuildBuild,
|
||||
import type {
|
||||
BuildFailure,
|
||||
BuildIncremental,
|
||||
BuildOptions,
|
||||
BuildResult,
|
||||
WatchMode,
|
||||
} from "esbuild";
|
||||
import readcontrol from "readcontrol";
|
||||
import { build as esbuildBuild } from "esbuild";
|
||||
|
||||
import process from "process";
|
||||
import { basename } from "path";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore; Type definitions are not found. This occurs when `strict: true` in tsconfig.json
|
||||
import readcontrol from "readcontrol";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore; Type definitions are not found. This occurs when `strict: true` in tsconfig.json
|
||||
import babelPlugin from "esbuild-plugin-babel";
|
||||
|
||||
const outDir = "./inst/www/shared/";
|
||||
|
||||
type ShinyDesc = { version: string; package: string; license: string };
|
||||
@@ -42,7 +52,7 @@ async function build(
|
||||
}
|
||||
}
|
||||
|
||||
const onRebuild = function (error?: string) {
|
||||
const onRebuild = function (error: BuildFailure | null) {
|
||||
if (error) {
|
||||
console.error(printNames.join(", "), "watch build failed:\n", error);
|
||||
} else {
|
||||
@@ -54,7 +64,7 @@ async function build(
|
||||
};
|
||||
|
||||
let incremental = false;
|
||||
let watch: false | { onRebuild: (error, result) => void } = false;
|
||||
let watch: WatchMode | false = false;
|
||||
|
||||
if (process.argv.length >= 3 && process.argv[2] == "--watch") {
|
||||
incremental = true;
|
||||
@@ -69,13 +79,14 @@ async function build(
|
||||
return esbuildBuild({
|
||||
incremental: incremental,
|
||||
watch: watch,
|
||||
target: "es5",
|
||||
target: "es2020",
|
||||
format: "iife",
|
||||
preserveSymlinks: true,
|
||||
...opts,
|
||||
}).then((x) => {
|
||||
onRebuild();
|
||||
onRebuild(null);
|
||||
return x;
|
||||
});
|
||||
}
|
||||
|
||||
export { outDir, build, shinyDesc, banner };
|
||||
export { outDir, build, shinyDesc, banner, babelPlugin };
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
// - TypeScript -----------------------------------------------------------
|
||||
|
||||
import { banner, build, outDir } from "./_build";
|
||||
import babelPlugin from "esbuild-plugin-babel";
|
||||
import { banner, build, outDir, babelPlugin } from "./_build";
|
||||
|
||||
build({
|
||||
bundle: true,
|
||||
@@ -25,8 +24,10 @@ build({
|
||||
// - Sass -----------------------------------------------------------
|
||||
|
||||
import autoprefixer from "autoprefixer";
|
||||
import postCssPlugin from "@deanc/esbuild-plugin-postcss";
|
||||
import sassPlugin from "esbuild-plugin-sass";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore; Type definitions are not found. This occurs when `strict: true` in tsconfig.json
|
||||
import postCssPlugin from "@deanc/esbuild-plugin-postcss";
|
||||
|
||||
const sassOpts = {
|
||||
minify: true,
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
// yarn build
|
||||
// ```
|
||||
|
||||
import { banner, build, outDir, shinyDesc } from "./_build";
|
||||
import { banner, build, outDir, shinyDesc, babelPlugin } from "./_build";
|
||||
import globalsPlugin from "esbuild-plugin-globals";
|
||||
import babelPlugin from "esbuild-plugin-babel";
|
||||
import type { BuildOptions } from "esbuild";
|
||||
|
||||
const opts: BuildOptions = {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import $ from "jquery";
|
||||
import { hasDefinedProperty } from "../../utils";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
|
||||
import { hasOwnProperty } from "../../utils";
|
||||
|
||||
type ActionButtonReceiveMessageData = { label?: string; icon?: string };
|
||||
type ActionButtonReceiveMessageData = { label?: string; icon?: string | [] };
|
||||
|
||||
class ActionButtonInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement> {
|
||||
@@ -40,7 +39,7 @@ class ActionButtonInputBinding extends InputBinding {
|
||||
const $el = $(el);
|
||||
|
||||
// retrieve current label and icon
|
||||
let label = $el.text();
|
||||
let label: string = $el.text();
|
||||
let icon = "";
|
||||
|
||||
// to check (and store) the previous icon, we look for a $el child
|
||||
@@ -57,16 +56,18 @@ class ActionButtonInputBinding extends InputBinding {
|
||||
}
|
||||
|
||||
// update the requested properties
|
||||
if (hasOwnProperty(data, "label")) label = data.label;
|
||||
if (hasOwnProperty(data, "icon")) {
|
||||
icon = data.icon;
|
||||
// if the user entered icon=character(0), remove the icon
|
||||
if (icon.length === 0) icon = "";
|
||||
if (hasDefinedProperty(data, "label")) {
|
||||
label = data.label;
|
||||
}
|
||||
if (hasDefinedProperty(data, "icon")) {
|
||||
// `data.icon` can be an [] if user gave `character(0)`.
|
||||
icon = Array.isArray(data.icon) ? "" : data.icon ?? "";
|
||||
}
|
||||
|
||||
// produce new html
|
||||
$el.html(icon + " " + label);
|
||||
}
|
||||
|
||||
unsubscribe(el: HTMLElement): void {
|
||||
$(el).off(".actionButtonInputBinding");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import $ from "jquery";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
import { hasOwnProperty } from "../../utils";
|
||||
import { hasDefinedProperty } from "../../utils";
|
||||
|
||||
type CheckedHTMLElement = HTMLInputElement;
|
||||
|
||||
@@ -35,12 +35,15 @@ class CheckboxInputBinding extends InputBinding {
|
||||
el: CheckedHTMLElement,
|
||||
data: CheckboxReceiveMessageData
|
||||
): void {
|
||||
if (hasOwnProperty(data, "value")) el.checked = data.value;
|
||||
if (hasDefinedProperty(data, "value")) {
|
||||
el.checked = data.value;
|
||||
}
|
||||
|
||||
// checkboxInput()'s label works different from other
|
||||
// input labels...the label container should always exist
|
||||
if (hasOwnProperty(data, "label"))
|
||||
if (hasDefinedProperty(data, "label")) {
|
||||
$(el).parent().find("span").text(data.label);
|
||||
}
|
||||
|
||||
$(el).trigger("change");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import $ from "jquery";
|
||||
|
||||
import { InputBinding } from "./inputBinding";
|
||||
import { $escape, hasOwnProperty, updateLabel } from "../../utils";
|
||||
import { $escape, updateLabel, hasDefinedProperty } from "../../utils";
|
||||
import type { CheckedHTMLElement } from "./checkbox";
|
||||
|
||||
type CheckboxGroupHTMLElement = CheckedHTMLElement;
|
||||
@@ -24,9 +24,11 @@ function getLabelNode(el: CheckboxGroupHTMLElement): JQuery<HTMLElement> {
|
||||
// Given an input DOM object, get the associated label. Handles labels
|
||||
// that wrap the input as well as labels associated with 'for' attribute.
|
||||
function getLabel(obj: HTMLElement): string | null {
|
||||
const parentNode = obj.parentNode as HTMLElement;
|
||||
|
||||
// If <label><input /><span>label text</span></label>
|
||||
if ((obj.parentNode as HTMLElement).tagName === "LABEL") {
|
||||
return $(obj.parentNode).find("span").text().trim();
|
||||
if (parentNode.tagName === "LABEL") {
|
||||
return $(parentNode).find("span").text().trim();
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -35,9 +37,11 @@ function getLabel(obj: HTMLElement): string | null {
|
||||
// that wrap the input as well as labels associated with 'for' attribute.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function setLabel(obj: HTMLElement, value: string): null {
|
||||
const parentNode = obj.parentNode as HTMLElement;
|
||||
|
||||
// If <label><input /><span>label text</span></label>
|
||||
if ((obj.parentNode as HTMLElement).tagName === "LABEL") {
|
||||
$(obj.parentNode).find("span").text(value);
|
||||
if (parentNode.tagName === "LABEL") {
|
||||
$(parentNode).find("span").text(value);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -58,7 +62,10 @@ class CheckboxGroupInputBinding extends InputBinding {
|
||||
}
|
||||
return values;
|
||||
}
|
||||
setValue(el: HTMLElement, value: string[] | string): void {
|
||||
setValue(el: HTMLElement, value: string[] | string | null): void {
|
||||
// Null value should be treated as empty array
|
||||
value = value ?? [];
|
||||
|
||||
// Clear all checkboxes
|
||||
$('input:checkbox[name="' + $escape(el.id) + '"]').prop("checked", false);
|
||||
|
||||
@@ -113,7 +120,7 @@ class CheckboxGroupInputBinding extends InputBinding {
|
||||
const $el = $(el);
|
||||
|
||||
// This will replace all the options
|
||||
if (hasOwnProperty(data, "options")) {
|
||||
if (hasDefinedProperty(data, "options")) {
|
||||
// Clear existing options and add each new one
|
||||
$el.find("div.shiny-options-group").remove();
|
||||
// Backward compatibility: for HTML generated by shinybootstrap2 package
|
||||
@@ -121,7 +128,9 @@ class CheckboxGroupInputBinding extends InputBinding {
|
||||
$el.append(data.options);
|
||||
}
|
||||
|
||||
if (hasOwnProperty(data, "value")) this.setValue(el, data.value);
|
||||
if (hasDefinedProperty(data, "value")) {
|
||||
this.setValue(el, data.value);
|
||||
}
|
||||
|
||||
updateLabel(data.label, getLabelNode(el));
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@ import {
|
||||
updateLabel,
|
||||
$escape,
|
||||
parseDate,
|
||||
hasOwnProperty,
|
||||
hasDefinedProperty,
|
||||
} from "../../utils";
|
||||
import type { NotUndefined } from "../../utils/extraTypes";
|
||||
|
||||
declare global {
|
||||
interface JQuery {
|
||||
@@ -14,7 +15,9 @@ declare global {
|
||||
bsDatepicker(methodName: "getUTCDate"): Date;
|
||||
// Infinity is not allowed as a literal return type. Using `1e9999` as a placeholder that resolves to Infinity
|
||||
// https://github.com/microsoft/TypeScript/issues/32277
|
||||
// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
|
||||
bsDatepicker(methodName: "getStartDate"): Date | -1e9999;
|
||||
// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
|
||||
bsDatepicker(methodName: "getEndDate"): Date | 1e9999;
|
||||
bsDatepicker(methodName: string): void;
|
||||
bsDatepicker(methodName: string, params: Date | null): void;
|
||||
@@ -122,8 +125,7 @@ class DateInputBindingBase extends InputBinding {
|
||||
}
|
||||
// Given an unambiguous date string or a Date object, set the min (start) date.
|
||||
// null will unset. undefined will result in no change,
|
||||
protected _setMin(el: HTMLElement, date: Date | null | undefined): void {
|
||||
if (date === undefined) return;
|
||||
protected _setMin(el: HTMLElement, date: Date | null): void {
|
||||
if (date === null) {
|
||||
$(el).bsDatepicker("setStartDate", null);
|
||||
return;
|
||||
@@ -161,8 +163,7 @@ class DateInputBindingBase extends InputBinding {
|
||||
}
|
||||
// Given an unambiguous date string or a Date object, set the max (end) date
|
||||
// null will unset.
|
||||
protected _setMax(el: HTMLElement, date: Date): void {
|
||||
if (date === undefined) return;
|
||||
protected _setMax(el: HTMLElement, date: Date | null): void {
|
||||
if (date === null) {
|
||||
$(el).bsDatepicker("setEndDate", null);
|
||||
return;
|
||||
@@ -236,7 +237,7 @@ class DateInputBinding extends DateInputBindingBase {
|
||||
return formatDateUTC(date);
|
||||
}
|
||||
// value must be an unambiguous string like '2001-01-01', or a Date object.
|
||||
setValue(el: HTMLElement, value: Date): void {
|
||||
setValue(el: HTMLElement, value: Date | null): void {
|
||||
// R's NA, which is null here will remove current value
|
||||
if (value === null) {
|
||||
$(el).find("input").val("").bsDatepicker("update");
|
||||
@@ -286,7 +287,7 @@ class DateInputBinding extends DateInputBindingBase {
|
||||
return {
|
||||
label: this._getLabelNode(el).text(),
|
||||
value: this.getValue(el),
|
||||
valueString: $input.val(),
|
||||
valueString: $input.val() as NotUndefined<ReturnType<typeof $input.val>>,
|
||||
min: min,
|
||||
max: max,
|
||||
language: $input.data("datepicker").language,
|
||||
@@ -300,14 +301,14 @@ class DateInputBinding extends DateInputBindingBase {
|
||||
|
||||
updateLabel(data.label, this._getLabelNode(el));
|
||||
|
||||
if (hasOwnProperty(data, "min")) this._setMin($input[0], data.min);
|
||||
if (hasDefinedProperty(data, "min")) this._setMin($input[0], data.min);
|
||||
|
||||
if (hasOwnProperty(data, "max")) this._setMax($input[0], data.max);
|
||||
if (hasDefinedProperty(data, "max")) this._setMax($input[0], data.max);
|
||||
|
||||
// Must set value only after min and max have been set. If new value is
|
||||
// outside the bounds of the previous min/max, then the result will be a
|
||||
// blank input.
|
||||
if (hasOwnProperty(data, "value")) this.setValue(el, data.value);
|
||||
if (hasDefinedProperty(data, "value")) this.setValue(el, data.value);
|
||||
|
||||
$(el).trigger("change");
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import $ from "jquery";
|
||||
import {
|
||||
$escape,
|
||||
formatDateUTC,
|
||||
hasOwnProperty,
|
||||
hasDefinedProperty,
|
||||
updateLabel,
|
||||
} from "../../utils";
|
||||
import { DateInputBindingBase } from "./date";
|
||||
@@ -114,12 +114,12 @@ class DateRangeInputBinding extends DateInputBindingBase {
|
||||
|
||||
updateLabel(data.label, getLabelNode(el));
|
||||
|
||||
if (hasOwnProperty(data, "min")) {
|
||||
if (hasDefinedProperty(data, "min")) {
|
||||
this._setMin($startinput[0], data.min);
|
||||
this._setMin($endinput[0], data.min);
|
||||
}
|
||||
|
||||
if (hasOwnProperty(data, "max")) {
|
||||
if (hasDefinedProperty(data, "max")) {
|
||||
this._setMax($startinput[0], data.max);
|
||||
this._setMax($endinput[0], data.max);
|
||||
}
|
||||
@@ -127,7 +127,9 @@ class DateRangeInputBinding extends DateInputBindingBase {
|
||||
// Must set value only after min and max have been set. If new value is
|
||||
// outside the bounds of the previous min/max, then the result will be a
|
||||
// blank input.
|
||||
if (hasOwnProperty(data, "value")) this.setValue(el, data.value);
|
||||
if (hasDefinedProperty(data, "value")) {
|
||||
this.setValue(el, data.value);
|
||||
}
|
||||
|
||||
$el.trigger("change");
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ function enableDraghover(el: JQuery<HTMLElement>): JQuery<HTMLElement> {
|
||||
const $el = $(el);
|
||||
let childCounter = 0;
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
$el.on({
|
||||
"dragenter.draghover": (e) => {
|
||||
if (childCounter++ === 0) {
|
||||
@@ -91,7 +92,7 @@ function canSetFiles(fileList: FileList): boolean {
|
||||
return true;
|
||||
}
|
||||
function handleDrop(e: JQuery.DragEventBase, el: HTMLInputElement): void {
|
||||
const files = e.originalEvent.dataTransfer.files,
|
||||
const files = e.originalEvent?.dataTransfer?.files,
|
||||
$el = $(el);
|
||||
|
||||
if (files === undefined || files === null) {
|
||||
@@ -109,7 +110,7 @@ function handleDrop(e: JQuery.DragEventBase, el: HTMLInputElement): void {
|
||||
// 3. The browser supports FileList and input.files assignment.
|
||||
// (Chrome, Safari)
|
||||
$el.val("");
|
||||
el.files = e.originalEvent.dataTransfer.files;
|
||||
el.files = files;
|
||||
// Recent versions of Firefox (57+, or "Quantum" and beyond) don't seem to
|
||||
// automatically trigger a change event, so we trigger one manually here.
|
||||
// On browsers that do trigger change, this operation appears to be
|
||||
@@ -188,7 +189,7 @@ function uploadFiles(evt: JQuery.DragEvent): void {
|
||||
// TODO-barret ; Should this be an internal class property?
|
||||
let $fileInputs = $();
|
||||
|
||||
function fileInputBindingGetId(el: HTMLInputElement): string {
|
||||
function fileInputBindingGetId(this: any, el: HTMLInputElement): string {
|
||||
return InputBinding.prototype.getId.call(this, el) || el.name;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,13 @@ import type { RatePolicyModes } from "../../inputPolicies/inputRateDecorator";
|
||||
import type { BindScope } from "../../shiny/bind";
|
||||
|
||||
class InputBinding {
|
||||
name: string;
|
||||
name!: string;
|
||||
|
||||
// Returns a jQuery object or element array that contains the
|
||||
// descendants of scope that match this binding
|
||||
find(scope: BindScope): JQuery<HTMLElement> {
|
||||
throw "Not implemented";
|
||||
// add so that typescript isn't mad about an unused var
|
||||
scope;
|
||||
scope; // unused var
|
||||
}
|
||||
|
||||
getId(el: HTMLElement): string {
|
||||
@@ -18,9 +17,9 @@ class InputBinding {
|
||||
|
||||
// Gives the input a type in case the server needs to know it
|
||||
// to deserialize the JSON correctly
|
||||
getType(el: HTMLElement): string | false {
|
||||
return false;
|
||||
el;
|
||||
getType(el: HTMLElement): string | null {
|
||||
return null;
|
||||
el; // unused var
|
||||
}
|
||||
getValue(el: HTMLElement): any {
|
||||
throw "Not implemented";
|
||||
@@ -32,12 +31,12 @@ class InputBinding {
|
||||
// getRatePolicy. If false, send value immediately. Default behavior is `false`
|
||||
subscribe(el: HTMLElement, callback: (value: boolean) => void): void {
|
||||
// empty
|
||||
el;
|
||||
callback;
|
||||
el; // unused var
|
||||
callback; // unused var
|
||||
}
|
||||
unsubscribe(el: HTMLElement): void {
|
||||
// empty
|
||||
el;
|
||||
el; // unused var
|
||||
}
|
||||
|
||||
// This is used for receiving messages that tell the input object to do
|
||||
@@ -47,19 +46,19 @@ class InputBinding {
|
||||
// trigger a change event.
|
||||
receiveMessage(el: HTMLElement, data: unknown): void {
|
||||
throw "Not implemented";
|
||||
el;
|
||||
data;
|
||||
el; // unused var
|
||||
data; // unused var
|
||||
}
|
||||
getState(el: HTMLElement): unknown {
|
||||
throw "Not implemented";
|
||||
el;
|
||||
el; // unused var
|
||||
}
|
||||
|
||||
getRatePolicy(
|
||||
el: HTMLElement
|
||||
): { policy: RatePolicyModes; delay: number } | null {
|
||||
return null;
|
||||
el;
|
||||
el; // unused var
|
||||
}
|
||||
|
||||
// Some input objects need initialization before being bound. This is
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import $ from "jquery";
|
||||
import { $escape, hasOwnProperty, updateLabel } from "../../utils";
|
||||
import { $escape, hasDefinedProperty, updateLabel } from "../../utils";
|
||||
import { TextInputBindingBase } from "./text";
|
||||
|
||||
type NumberHTMLElement = HTMLInputElement;
|
||||
@@ -23,7 +23,9 @@ class NumberInputBinding extends TextInputBindingBase {
|
||||
return $(scope).find('input[type="number"]');
|
||||
}
|
||||
|
||||
getValue(el: NumberHTMLElement): string[] | number | string {
|
||||
getValue(
|
||||
el: NumberHTMLElement
|
||||
): string[] | number | string | null | undefined {
|
||||
const numberVal = $(el).val();
|
||||
|
||||
if (typeof numberVal == "string") {
|
||||
@@ -49,10 +51,12 @@ class NumberInputBinding extends TextInputBindingBase {
|
||||
el;
|
||||
}
|
||||
receiveMessage(el: NumberHTMLElement, data: NumberReceiveMessageData): void {
|
||||
if (hasOwnProperty(data, "value")) el.value = data.value;
|
||||
if (hasOwnProperty(data, "min")) el.min = data.min;
|
||||
if (hasOwnProperty(data, "max")) el.max = data.max;
|
||||
if (hasOwnProperty(data, "step")) el.step = data.step;
|
||||
// Setting values to `""` will remove the attribute value from the DOM element.
|
||||
// The attr key will still remain, but there is not value... ex: `<input id="foo" type="number" min max/>`
|
||||
if (hasDefinedProperty(data, "value")) el.value = data.value ?? "";
|
||||
if (hasDefinedProperty(data, "min")) el.min = data.min ?? "";
|
||||
if (hasDefinedProperty(data, "max")) el.max = data.max ?? "";
|
||||
if (hasDefinedProperty(data, "step")) el.step = data.step ?? "";
|
||||
|
||||
updateLabel(data.label, getLabelNode(el));
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import $ from "jquery";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
import { $escape, hasOwnProperty, updateLabel } from "../../utils";
|
||||
import { $escape, hasDefinedProperty, updateLabel } from "../../utils";
|
||||
|
||||
type RadioHTMLElement = HTMLInputElement;
|
||||
|
||||
@@ -10,7 +10,7 @@ type ValueLabelObject = {
|
||||
};
|
||||
|
||||
type RadioReceiveMessageData = {
|
||||
value?: string;
|
||||
value?: string | [];
|
||||
options?: ValueLabelObject[];
|
||||
label: string;
|
||||
};
|
||||
@@ -24,9 +24,11 @@ function getLabelNode(el: RadioHTMLElement): JQuery<HTMLElement> {
|
||||
// Given an input DOM object, get the associated label. Handles labels
|
||||
// that wrap the input as well as labels associated with 'for' attribute.
|
||||
function getLabel(obj: HTMLElement): string | null {
|
||||
const parentNode = obj.parentNode as HTMLElement;
|
||||
|
||||
// If <label><input /><span>label text</span></label>
|
||||
if ((obj.parentNode as HTMLElement).tagName === "LABEL") {
|
||||
return $(obj.parentNode).find("span").text().trim();
|
||||
if (parentNode.tagName === "LABEL") {
|
||||
return $(parentNode).find("span").text().trim();
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -35,9 +37,11 @@ function getLabel(obj: HTMLElement): string | null {
|
||||
// that wrap the input as well as labels associated with 'for' attribute.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function setLabel(obj: HTMLElement, value: string): null {
|
||||
const parentNode = obj.parentNode as HTMLElement;
|
||||
|
||||
// If <label><input /><span>label text</span></label>
|
||||
if ((obj.parentNode as HTMLElement).tagName === "LABEL") {
|
||||
$(obj.parentNode).find("span").text(value);
|
||||
if (parentNode.tagName === "LABEL") {
|
||||
$(parentNode).find("span").text(value);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -47,7 +51,9 @@ class RadioInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement> {
|
||||
return $(scope).find(".shiny-input-radiogroup");
|
||||
}
|
||||
getValue(el: RadioHTMLElement): string[] | number | string | null {
|
||||
getValue(
|
||||
el: RadioHTMLElement
|
||||
): string[] | number | string | null | undefined {
|
||||
// Select the radio objects that have name equal to the grouping div's id
|
||||
const checkedItems = $(
|
||||
'input:radio[name="' + $escape(el.id) + '"]:checked'
|
||||
@@ -61,8 +67,8 @@ class RadioInputBinding extends InputBinding {
|
||||
|
||||
return checkedItems.val();
|
||||
}
|
||||
setValue(el: RadioHTMLElement, value: string): void {
|
||||
if ($.isArray(value) && value.length === 0) {
|
||||
setValue(el: RadioHTMLElement, value: string | []): void {
|
||||
if (Array.isArray(value) && value.length === 0) {
|
||||
// Removing all checked item if the sent data is empty
|
||||
$('input:radio[name="' + $escape(el.id) + '"]').prop("checked", false);
|
||||
} else {
|
||||
@@ -77,7 +83,7 @@ class RadioInputBinding extends InputBinding {
|
||||
}
|
||||
getState(el: RadioHTMLElement): {
|
||||
label: string;
|
||||
value: string[] | number | string;
|
||||
value: ReturnType<RadioInputBinding["getValue"]>;
|
||||
options: ValueLabelObject[];
|
||||
} {
|
||||
const $objs = $(
|
||||
@@ -101,16 +107,20 @@ class RadioInputBinding extends InputBinding {
|
||||
const $el = $(el);
|
||||
// This will replace all the options
|
||||
|
||||
if (hasOwnProperty(data, "options")) {
|
||||
if (hasDefinedProperty(data, "options")) {
|
||||
// Clear existing options and add each new one
|
||||
$el.find("div.shiny-options-group").remove();
|
||||
// Backward compatibility: for HTML generated by shinybootstrap2 package
|
||||
$el.find("label.radio").remove();
|
||||
// @ts-expect-error; TODO-barret; IDK what this line is doing
|
||||
// TODO-barret; Should this line be setting attributes instead?
|
||||
// `data.options` is an array of `{value, label}` objects
|
||||
$el.append(data.options);
|
||||
}
|
||||
|
||||
if (hasOwnProperty(data, "value")) this.setValue(el, data.value);
|
||||
if (hasDefinedProperty(data, "value")) {
|
||||
this.setValue(el, data.value);
|
||||
}
|
||||
|
||||
updateLabel(data.label, getLabelNode(el));
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import $ from "jquery";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
import { $escape, hasOwnProperty, updateLabel } from "../../utils";
|
||||
import { $escape, hasDefinedProperty, updateLabel } from "../../utils";
|
||||
import { indirectEval } from "../../utils/eval";
|
||||
import type { NotUndefined } from "../../utils/extraTypes";
|
||||
|
||||
type SelectHTMLElement = HTMLSelectElement & { nonempty: boolean };
|
||||
|
||||
@@ -13,8 +14,9 @@ type SelectInputReceiveMessageData = {
|
||||
value?: string;
|
||||
};
|
||||
|
||||
type SelectizeOptions = Selectize.IOptions<string, unknown>;
|
||||
type SelectizeInfo = Selectize.IApi<string, unknown> & {
|
||||
settings: Selectize.IOptions<string, unknown>;
|
||||
settings: SelectizeOptions;
|
||||
};
|
||||
|
||||
function getLabelNode(el: SelectHTMLElement): JQuery<HTMLElement> {
|
||||
@@ -42,7 +44,7 @@ class SelectInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement> {
|
||||
return $(scope).find("select");
|
||||
}
|
||||
getType(el: HTMLElement): string {
|
||||
getType(el: HTMLElement): string | null {
|
||||
const $el = $(el);
|
||||
|
||||
if (!$el.hasClass("symbol")) {
|
||||
@@ -58,8 +60,10 @@ class SelectInputBinding extends InputBinding {
|
||||
getId(el: SelectHTMLElement): string {
|
||||
return InputBinding.prototype.getId.call(this, el) || el.name;
|
||||
}
|
||||
getValue(el: HTMLElement): string[] | number | string {
|
||||
return $(el).val();
|
||||
getValue(
|
||||
el: HTMLElement
|
||||
): NotUndefined<ReturnType<JQuery<HTMLElement>["val"]>> {
|
||||
return $(el).val() as NotUndefined<ReturnType<JQuery<HTMLElement>["val"]>>;
|
||||
}
|
||||
setValue(el: SelectHTMLElement, value: string): void {
|
||||
if (!isSelectize(el)) {
|
||||
@@ -67,14 +71,12 @@ class SelectInputBinding extends InputBinding {
|
||||
} else {
|
||||
const selectize = this._selectize(el);
|
||||
|
||||
if (selectize) {
|
||||
selectize.setValue(value);
|
||||
}
|
||||
selectize.setValue(value);
|
||||
}
|
||||
}
|
||||
getState(el: SelectHTMLElement): {
|
||||
label: JQuery<HTMLElement>;
|
||||
value: string[] | number | string;
|
||||
value: ReturnType<SelectInputBinding["getValue"]>;
|
||||
options: Array<{ value: string; label: string }>;
|
||||
} {
|
||||
// Store options in an array of objects, each with with value and label
|
||||
@@ -101,13 +103,13 @@ class SelectInputBinding extends InputBinding {
|
||||
data: SelectInputReceiveMessageData
|
||||
): void {
|
||||
const $el = $(el);
|
||||
let selectize;
|
||||
|
||||
// This will replace all the options
|
||||
if (hasOwnProperty(data, "options")) {
|
||||
selectize = this._selectize(el);
|
||||
if (hasDefinedProperty(data, "options")) {
|
||||
const selectize = this._selectize(el);
|
||||
// Must destroy selectize before appending new options, otherwise
|
||||
// selectize will restore the original select
|
||||
|
||||
if (selectize) selectize.destroy();
|
||||
// Clear existing options and add each new one
|
||||
$el.empty().append(data.options);
|
||||
@@ -115,7 +117,7 @@ class SelectInputBinding extends InputBinding {
|
||||
}
|
||||
|
||||
// re-initialize selectize
|
||||
if (hasOwnProperty(data, "config")) {
|
||||
if (hasDefinedProperty(data, "config")) {
|
||||
$el
|
||||
.parent()
|
||||
.find('script[data-for="' + $escape(el.id) + '"]')
|
||||
@@ -124,12 +126,22 @@ class SelectInputBinding extends InputBinding {
|
||||
}
|
||||
|
||||
// use server-side processing for selectize
|
||||
if (hasOwnProperty(data, "url")) {
|
||||
selectize = this._selectize(el);
|
||||
if (hasDefinedProperty(data, "url")) {
|
||||
type CallbackFn = Parameters<
|
||||
NonNullable<SelectizeInfo["settings"]["load"]>
|
||||
>[1];
|
||||
const selectize = this._selectize(el) as ReturnType<
|
||||
SelectInputBinding["_selectize"]
|
||||
> & {
|
||||
settings: {
|
||||
load: (query: string, callback: CallbackFn) => any;
|
||||
};
|
||||
};
|
||||
|
||||
selectize.clearOptions();
|
||||
let loaded = false;
|
||||
|
||||
selectize.settings.load = function (query, callback) {
|
||||
selectize.settings.load = function (query: string, callback: CallbackFn) {
|
||||
const settings = selectize.settings;
|
||||
|
||||
$.ajax({
|
||||
@@ -155,7 +167,7 @@ class SelectInputBinding extends InputBinding {
|
||||
// the group's label and value. We use the current settings of
|
||||
// the selectize object to decide the fieldnames of that obj.
|
||||
const optgroupId = elem[settings.optgroupField || "optgroup"];
|
||||
const optgroup = {};
|
||||
const optgroup: { [key: string]: string } = {};
|
||||
|
||||
optgroup[settings.optgroupLabelField || "label"] = optgroupId;
|
||||
optgroup[settings.optgroupValueField || "value"] = optgroupId;
|
||||
@@ -163,8 +175,10 @@ class SelectInputBinding extends InputBinding {
|
||||
});
|
||||
callback(res);
|
||||
if (!loaded) {
|
||||
if (hasOwnProperty(data, "value")) {
|
||||
selectize.setValue(data.value);
|
||||
if (hasDefinedProperty(data, "value")) {
|
||||
if (typeof data.value === "string") {
|
||||
selectize.setValue(data.value);
|
||||
}
|
||||
} else if (settings.maxItems === 1) {
|
||||
// first item selected by default only for single-select
|
||||
selectize.setValue(res[0].value);
|
||||
@@ -178,7 +192,7 @@ class SelectInputBinding extends InputBinding {
|
||||
selectize.load(function (callback) {
|
||||
selectize.settings.load.apply(selectize, ["", callback]);
|
||||
});
|
||||
} else if (hasOwnProperty(data, "value")) {
|
||||
} else if (hasDefinedProperty(data, "value")) {
|
||||
this.setValue(el, data.value);
|
||||
}
|
||||
|
||||
@@ -208,21 +222,22 @@ class SelectInputBinding extends InputBinding {
|
||||
this._selectize(el);
|
||||
}
|
||||
protected _selectize(el: SelectHTMLElement, update = false): SelectizeInfo {
|
||||
if (!$.fn.selectize) return undefined;
|
||||
if (!$.fn.selectize) throw "selectize jquery is not defined";
|
||||
const $el = $(el);
|
||||
const config = $el
|
||||
.parent()
|
||||
.find('script[data-for="' + $escape(el.id) + '"]');
|
||||
|
||||
if (config.length === 0) return undefined;
|
||||
if (config.length === 0)
|
||||
throw "No config found for selectize with id:" + $escape(el.id);
|
||||
|
||||
let options: {
|
||||
let options: SelectizeOptions & {
|
||||
labelField: "label";
|
||||
valueField: "value";
|
||||
searchField: ["label"];
|
||||
onItemRemove?: (value: string) => void;
|
||||
onDropdownClose?: () => void;
|
||||
} & { [key: string]: unknown } = $.extend(
|
||||
} = $.extend(
|
||||
{
|
||||
labelField: "label",
|
||||
valueField: "value",
|
||||
@@ -235,7 +250,7 @@ class SelectInputBinding extends InputBinding {
|
||||
if (typeof config.data("nonempty") !== "undefined") {
|
||||
el.nonempty = true;
|
||||
options = $.extend(options, {
|
||||
onItemRemove: function (value) {
|
||||
onItemRemove: function (this: SelectizeInfo, value: string) {
|
||||
if (this.getValue() === "")
|
||||
$("select#" + $escape(el.id))
|
||||
.empty()
|
||||
@@ -249,9 +264,10 @@ class SelectInputBinding extends InputBinding {
|
||||
},
|
||||
onDropdownClose:
|
||||
// $dropdown: any
|
||||
function () {
|
||||
if (this.getValue() === "")
|
||||
this.setValue($("select#" + $escape(el.id)).val());
|
||||
function (this: SelectizeInfo) {
|
||||
if (this.getValue() === "") {
|
||||
this.setValue($("select#" + $escape(el.id)).val() as string);
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
@@ -259,8 +275,9 @@ class SelectInputBinding extends InputBinding {
|
||||
}
|
||||
// options that should be eval()ed
|
||||
if (config.data("eval") instanceof Array)
|
||||
$.each(config.data("eval"), function (i, x) {
|
||||
$.each(config.data("eval"), function (i, x: string) {
|
||||
/*jshint evil: true*/
|
||||
// @ts-expect-error; Need to type `options` keys to know exactly which values are accessed.
|
||||
options[x] = indirectEval("(" + options[x] + ")");
|
||||
});
|
||||
let control = $el.selectize(options)[0].selectize as SelectizeInfo;
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import type {
|
||||
IonRangeSliderEvent,
|
||||
IonRangeSliderOptions,
|
||||
} from "ion-rangeslider";
|
||||
import $ from "jquery";
|
||||
// import { NameValueHTMLElement } from ".";
|
||||
import {
|
||||
formatDateUTC,
|
||||
updateLabel,
|
||||
$escape,
|
||||
hasOwnProperty,
|
||||
hasDefinedProperty,
|
||||
} from "../../utils";
|
||||
|
||||
import type { TextHTMLElement } from "./text";
|
||||
@@ -28,6 +32,11 @@ type SliderReceiveMessageData = {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"data-type"?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"time-format"?: string;
|
||||
timezone?: string;
|
||||
};
|
||||
|
||||
// MUST use window.strftime as the javascript dependency is dynamic
|
||||
@@ -42,7 +51,7 @@ declare global {
|
||||
}
|
||||
|
||||
// Necessary to get hidden sliders to send their updated values
|
||||
function forceIonSliderUpdate(slider) {
|
||||
function forceIonSliderUpdate(slider: any) {
|
||||
if (slider.$cache && slider.$cache.input)
|
||||
slider.$cache.input.trigger("change");
|
||||
else console.log("Couldn't force ion slider to update");
|
||||
@@ -73,7 +82,7 @@ function getTypePrettifyer(
|
||||
// The default prettify function for ion.rangeSlider adds thousands
|
||||
// separators after the decimal mark, so we have our own version here.
|
||||
// (#1958)
|
||||
prettify = function (num) {
|
||||
prettify = function (this: IonRangeSliderOptions, num: number) {
|
||||
// When executed, `this` will refer to the `IonRangeSlider.options`
|
||||
// object.
|
||||
return formatNumber(num, this.prettify_separator);
|
||||
@@ -104,18 +113,18 @@ class SliderInputBinding extends TextInputBindingBase {
|
||||
return $(scope).find("input.js-range-slider");
|
||||
}
|
||||
|
||||
getType(el: HTMLElement): string | false {
|
||||
getType(el: HTMLElement): string | null {
|
||||
const dataType = $(el).data("data-type");
|
||||
|
||||
if (dataType === "date") return "shiny.date";
|
||||
else if (dataType === "datetime") return "shiny.datetime";
|
||||
else return false;
|
||||
else return null;
|
||||
}
|
||||
getValue(
|
||||
el: TextHTMLElement
|
||||
): number | string | [number | string, number | string] {
|
||||
const $el = $(el);
|
||||
const result = $(el).data("ionRangeSlider").result;
|
||||
const result = $(el).data("ionRangeSlider").result as IonRangeSliderEvent;
|
||||
|
||||
// Function for converting numeric value from slider to appropriate type.
|
||||
let convert: (val: unknown) => number | string;
|
||||
@@ -182,21 +191,28 @@ class SliderInputBinding extends TextInputBindingBase {
|
||||
prettify?: Prettify;
|
||||
} = {};
|
||||
|
||||
if (hasOwnProperty(data, "value")) {
|
||||
if (hasDefinedProperty(data, "value")) {
|
||||
if (numValues(el) === 2 && data.value instanceof Array) {
|
||||
msg.from = data.value[0];
|
||||
msg.to = data.value[1];
|
||||
} else {
|
||||
msg.from = data.value as number | string;
|
||||
if (Array.isArray(data.value)) {
|
||||
throw "Slider only contains a single value and cannot be updated with an array";
|
||||
}
|
||||
msg.from = data.value;
|
||||
}
|
||||
}
|
||||
|
||||
const sliderFeatures = ["min", "max", "step"];
|
||||
const sliderFeatures: Array<"max" | "min" | "step"> = [
|
||||
"min",
|
||||
"max",
|
||||
"step",
|
||||
];
|
||||
|
||||
for (let i = 0; i < sliderFeatures.length; i++) {
|
||||
const feats = sliderFeatures[i];
|
||||
|
||||
if (hasOwnProperty(data, feats)) {
|
||||
if (hasDefinedProperty(data, feats)) {
|
||||
msg[feats] = data[feats];
|
||||
}
|
||||
}
|
||||
@@ -204,12 +220,16 @@ class SliderInputBinding extends TextInputBindingBase {
|
||||
updateLabel(data.label, getLabelNode(el));
|
||||
|
||||
// (maybe) update data elements
|
||||
const domElements = ["data-type", "time-format", "timezone"];
|
||||
const domElements: Array<"data-type" | "time-format" | "timezone"> = [
|
||||
"data-type",
|
||||
"time-format",
|
||||
"timezone",
|
||||
];
|
||||
|
||||
for (let i = 0; i < domElements.length; i++) {
|
||||
const elem = domElements[i];
|
||||
|
||||
if (hasOwnProperty(data, elem)) {
|
||||
if (hasDefinedProperty(data, elem)) {
|
||||
$el.data(elem, data[elem]);
|
||||
}
|
||||
}
|
||||
@@ -284,12 +304,12 @@ function formatNumber(
|
||||
$(document).on("click", ".slider-animate-button", function (evt: Event) {
|
||||
evt.preventDefault();
|
||||
const self = $(this);
|
||||
const target = $("#" + $escape(self.attr("data-target-id")));
|
||||
const target = $("#" + $escape(self.attr("data-target-id") as string));
|
||||
const startLabel = "Play";
|
||||
const stopLabel = "Pause";
|
||||
const loop =
|
||||
self.attr("data-loop") !== undefined &&
|
||||
!/^\s*false\s*$/i.test(self.attr("data-loop"));
|
||||
!/^\s*false\s*$/i.test(self.attr("data-loop") as string);
|
||||
let animInterval = self.attr("data-interval") as number | string;
|
||||
|
||||
if (isNaN(animInterval as number)) animInterval = 1500;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import $ from "jquery";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
import { hasOwnProperty, isBS3 } from "../../utils";
|
||||
import { hasDefinedProperty, isBS3 } from "../../utils";
|
||||
|
||||
type TabInputReceiveMessageData = { value?: string };
|
||||
|
||||
@@ -25,7 +25,7 @@ class BootstrapTabInputBinding extends InputBinding {
|
||||
|
||||
return null;
|
||||
}
|
||||
setValue(el: HTMLElement, value: string): void {
|
||||
setValue(el: HTMLElement, value: string | undefined): void {
|
||||
let success = false;
|
||||
|
||||
if (value) {
|
||||
@@ -56,7 +56,7 @@ class BootstrapTabInputBinding extends InputBinding {
|
||||
return { value: this.getValue(el) };
|
||||
}
|
||||
receiveMessage(el: HTMLElement, data: TabInputReceiveMessageData): void {
|
||||
if (hasOwnProperty(data, "value")) this.setValue(el, data.value);
|
||||
if (hasDefinedProperty(data, "value")) this.setValue(el, data.value);
|
||||
$(el).trigger("change");
|
||||
}
|
||||
subscribe(el: HTMLElement, callback: (x: boolean) => void): void {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import $ from "jquery";
|
||||
import { $escape, updateLabel, hasOwnProperty } from "../../utils";
|
||||
import { $escape, updateLabel, hasDefinedProperty } from "../../utils";
|
||||
|
||||
import { InputBinding } from "./inputBinding";
|
||||
|
||||
@@ -109,11 +109,12 @@ class TextInputBinding extends TextInputBindingBase {
|
||||
};
|
||||
}
|
||||
receiveMessage(el: TextHTMLElement, data: TextReceiveMessageData): void {
|
||||
if (hasOwnProperty(data, "value")) this.setValue(el, data.value);
|
||||
if (hasDefinedProperty(data, "value")) this.setValue(el, data.value);
|
||||
|
||||
updateLabel(data.label, getLabelNode(el));
|
||||
|
||||
if (hasOwnProperty(data, "placeholder")) el.placeholder = data.placeholder;
|
||||
if (hasDefinedProperty(data, "placeholder"))
|
||||
el.placeholder = data.placeholder;
|
||||
|
||||
$(el).trigger("change");
|
||||
}
|
||||
|
||||
@@ -22,9 +22,12 @@ class DatatableOutputBinding extends OutputBinding {
|
||||
options?: {
|
||||
searching?: boolean;
|
||||
search?: { caseInsensitive?: boolean };
|
||||
// To be sent to data table;
|
||||
// Will copy in R value to this location
|
||||
escape?: string;
|
||||
} | null;
|
||||
escape?: string; // Incoming from R
|
||||
action?: string;
|
||||
escape?: string;
|
||||
evalOptions?: string[];
|
||||
callback?: string;
|
||||
searchDelay?: number;
|
||||
@@ -42,7 +45,7 @@ class DatatableOutputBinding extends OutputBinding {
|
||||
header = "<thead><tr>" + header + "</tr></thead>";
|
||||
let footer = "";
|
||||
|
||||
if (data.options === null || data.options.searching !== false) {
|
||||
if (data.options?.searching ?? true) {
|
||||
footer = $.map(colnames, function (x) {
|
||||
// placeholder needs to be escaped (and HTML tags are stripped off)
|
||||
return (
|
||||
@@ -62,17 +65,16 @@ class DatatableOutputBinding extends OutputBinding {
|
||||
$el.append(content);
|
||||
|
||||
// options that should be eval()ed
|
||||
if (data.evalOptions)
|
||||
if (data.evalOptions) {
|
||||
$.each(data.evalOptions, function (i, x) {
|
||||
/*jshint evil: true */
|
||||
// @ts-expect-error; If `evalOptions` is defined, `data.options` should be defined
|
||||
data.options[x] = indirectEval("(" + data.options[x] + ")");
|
||||
});
|
||||
}
|
||||
|
||||
// caseInsensitive searching? default true
|
||||
const searchCI =
|
||||
data.options === null ||
|
||||
typeof data.options.search === "undefined" ||
|
||||
data.options.search.caseInsensitive !== false;
|
||||
const searchCI = data.options?.search?.caseInsensitive !== false;
|
||||
const oTable = $(el)
|
||||
.children("table")
|
||||
.DataTable(
|
||||
@@ -86,8 +88,14 @@ class DatatableOutputBinding extends OutputBinding {
|
||||
ajax: {
|
||||
url: data.action,
|
||||
type: "POST",
|
||||
data: function (d) {
|
||||
data: function (d: NonNullable<typeof data.options>) {
|
||||
d.search || (d.search = {});
|
||||
d.search.caseInsensitive = searchCI;
|
||||
// Copy from the R value (`data.escape`) to the escape option
|
||||
// (`d.escape`) similar to `data.options.escape`;
|
||||
// Note: this logic may be wrong, but the method is strongly
|
||||
// deprecated in favor of DT package. So users should not
|
||||
// naturally run this line of code
|
||||
d.escape = data.escape;
|
||||
},
|
||||
},
|
||||
@@ -110,7 +118,7 @@ class DatatableOutputBinding extends OutputBinding {
|
||||
.first()
|
||||
.unbind("keyup")
|
||||
.keyup(
|
||||
debounce(data.searchDelay, function () {
|
||||
debounce(data.searchDelay, function (this: HTMLInputElement) {
|
||||
oTable.search(this.value).draw();
|
||||
})
|
||||
);
|
||||
@@ -124,7 +132,7 @@ class DatatableOutputBinding extends OutputBinding {
|
||||
if (!x.bSearchable) searchInputs.eq(i as number).hide();
|
||||
});
|
||||
searchInputs.keyup(
|
||||
debounce(data.searchDelay, function () {
|
||||
debounce(data.searchDelay, function (this: HTMLInputElement) {
|
||||
oTable.column(searchInputs.index(this)).search(this.value).draw();
|
||||
})
|
||||
);
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { isIE, IEVersion } from "../../utils/browser";
|
||||
import type { CoordmapInit } from "../../imageutils/initCoordmap";
|
||||
import type { ErrorsMessageValue } from "../../shiny/shinyapp";
|
||||
import { ifUndefined } from "../../utils/object";
|
||||
|
||||
class ImageOutputBinding extends OutputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement> {
|
||||
@@ -65,10 +66,6 @@ class ImageOutputBinding extends OutputBinding {
|
||||
|
||||
// If value is undefined, return alternate. Sort of like ||, except it won't
|
||||
// return alternate for other falsy values (0, false, null).
|
||||
function ifUndefined(value, alternate) {
|
||||
if (value === undefined) return alternate;
|
||||
return value;
|
||||
}
|
||||
|
||||
const opts = {
|
||||
clickId: $el.data("click-id"),
|
||||
|
||||
@@ -3,7 +3,7 @@ import { asArray } from "../../utils";
|
||||
import type { ErrorsMessageValue } from "../../shiny/shinyapp";
|
||||
|
||||
class OutputBinding {
|
||||
name: string;
|
||||
name!: string;
|
||||
|
||||
// Returns a jQuery object or element array that contains the
|
||||
// descendants of scope that match this binding
|
||||
|
||||
@@ -22,7 +22,8 @@ class OutputBindingAdapter {
|
||||
// onResize with a version that does a makeResizeFilter on the element.
|
||||
if (binding.resize) {
|
||||
this.onResize = makeResizeFilter(el, function (width, height) {
|
||||
binding.resize(el, width, height);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
binding.resize!(el, width, height);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ interface BindingObj<Binding> {
|
||||
}
|
||||
|
||||
class BindingRegistry<Binding extends BindingBase> {
|
||||
name: string;
|
||||
name!: string;
|
||||
bindings: Array<BindingObj<Binding>> = [];
|
||||
bindingNames: { [key: string]: BindingObj<Binding> } = {};
|
||||
|
||||
|
||||
56
srcts/src/events/jQueryEvents.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
// Used to avoid isolated module warning
|
||||
import type { JQueryEventHandlerBase } from "bootstrap";
|
||||
import "jquery";
|
||||
|
||||
type EvtPrefix<T extends string> = `${T}.${string}`;
|
||||
type EvtFn<T extends JQuery.Event> = ((evt: T) => void) | null | undefined;
|
||||
|
||||
declare global {
|
||||
interface JQuery {
|
||||
on(events: EvtPrefix<"change">, handler: EvtFn<JQuery.DragEvent>): this;
|
||||
|
||||
on(
|
||||
events: EvtPrefix<"mousdown">,
|
||||
handler: EvtFn<JQuery.MouseDownEvent>
|
||||
): this;
|
||||
on(
|
||||
events: EvtPrefix<"dblclick">,
|
||||
handler: EvtFn<JQuery.DoubleClickEvent>
|
||||
): this;
|
||||
on(
|
||||
events: EvtPrefix<"dblclick2">,
|
||||
// Note: This may not be the _right type_, but it is how it is handled internally
|
||||
handler: EvtFn<JQuery.MouseDownEvent>
|
||||
): this;
|
||||
on(
|
||||
events: EvtPrefix<"mousemove">,
|
||||
handler: EvtFn<JQuery.MouseMoveEvent>
|
||||
): this;
|
||||
on(
|
||||
events: EvtPrefix<"mouseout">,
|
||||
handler: EvtFn<JQuery.MouseOutEvent>
|
||||
): this;
|
||||
on(
|
||||
events: EvtPrefix<"mousedown">,
|
||||
handler: EvtFn<JQuery.MouseDownEvent>
|
||||
): this;
|
||||
on(
|
||||
events: EvtPrefix<"mousedown2">,
|
||||
handler: EvtFn<JQuery.MouseDownEvent>
|
||||
): this;
|
||||
on(events: EvtPrefix<"mouseup">, handler: EvtFn<JQuery.MouseUpEvent>): this;
|
||||
on(events: EvtPrefix<"resize">, handler: EvtFn<JQuery.ResizeEvent>): this;
|
||||
|
||||
on(
|
||||
events: `shown.bs.${string}.sendImageSize`,
|
||||
selector: string,
|
||||
handler: (
|
||||
this: HTMLElement,
|
||||
e: JQueryEventHandlerBase<HTMLElement, any>
|
||||
// e: JQuery.Event & {
|
||||
// namespace: string;
|
||||
// }
|
||||
) => void
|
||||
): this;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ type UploadEndValue = never;
|
||||
// FileList object. Subclass/clone it and override the `on*` functions to
|
||||
// make it do something useful.
|
||||
class FileProcessor {
|
||||
files: FileList;
|
||||
files: File[];
|
||||
fileIndex = -1;
|
||||
// Currently need to use small chunk size because R-Websockets can't
|
||||
// handle continuation frames
|
||||
@@ -21,7 +21,7 @@ class FileProcessor {
|
||||
completed = false;
|
||||
|
||||
constructor(files: FileList, exec$run = true) {
|
||||
this.files = files;
|
||||
this.files = Array.from(files);
|
||||
|
||||
// TODO: Register error/abort callbacks
|
||||
if (exec$run) {
|
||||
@@ -30,7 +30,7 @@ class FileProcessor {
|
||||
}
|
||||
|
||||
// Begin callbacks. Subclassers/cloners may override any or all of these.
|
||||
onBegin(files: FileList, cont: () => void): void {
|
||||
onBegin(files: File[], cont: () => void): void {
|
||||
files;
|
||||
setTimeout(cont, 0);
|
||||
}
|
||||
@@ -99,10 +99,10 @@ class FileUploader extends FileProcessor {
|
||||
id: string;
|
||||
el: HTMLElement;
|
||||
|
||||
jobId: JobId;
|
||||
uploadUrl: UploadUrl;
|
||||
progressBytes: number;
|
||||
totalBytes: number;
|
||||
jobId!: JobId;
|
||||
uploadUrl!: UploadUrl;
|
||||
progressBytes!: number;
|
||||
totalBytes!: number;
|
||||
|
||||
constructor(
|
||||
shinyapp: ShinyApp,
|
||||
@@ -142,7 +142,7 @@ class FileUploader extends FileProcessor {
|
||||
): void {
|
||||
this.shinyapp.makeRequest(method, args, onSuccess, onFailure, blobs);
|
||||
}
|
||||
onBegin(files: FileList, cont: () => void): void {
|
||||
onBegin(files: File[], cont: () => void): void {
|
||||
// Reset progress bar
|
||||
this.$setError(null);
|
||||
this.$setActive(true);
|
||||
@@ -155,13 +155,12 @@ class FileUploader extends FileProcessor {
|
||||
this.totalBytes += file.size;
|
||||
});
|
||||
|
||||
const fileInfo = $.map(files, function (file: File, i) {
|
||||
const fileInfo = $.map(files, function (file: File) {
|
||||
return {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
};
|
||||
i;
|
||||
});
|
||||
|
||||
this.makeRequest(
|
||||
@@ -185,6 +184,9 @@ class FileUploader extends FileProcessor {
|
||||
type: "POST",
|
||||
cache: false,
|
||||
xhr: () => {
|
||||
if (typeof $.ajaxSettings.xhr !== "function")
|
||||
throw "jQuery's XHR is not a function";
|
||||
|
||||
const xhrVal = $.ajaxSettings.xhr();
|
||||
|
||||
if (xhrVal.upload) {
|
||||
@@ -243,7 +245,7 @@ class FileUploader extends FileProcessor {
|
||||
this.$bar().text("Upload complete");
|
||||
// Reset the file input's value to "". This allows the same file to be
|
||||
// uploaded again. https://stackoverflow.com/a/22521275
|
||||
$(evt.el).val("");
|
||||
$(evt.el as HTMLElement).val("");
|
||||
},
|
||||
(error) => {
|
||||
this.onError(error);
|
||||
|
||||
@@ -18,30 +18,30 @@ type BoundsCss = Bounds;
|
||||
type BoundsData = Bounds;
|
||||
|
||||
type ImageState = {
|
||||
brushing?: boolean;
|
||||
dragging?: boolean;
|
||||
resizing?: boolean;
|
||||
brushing: boolean;
|
||||
dragging: boolean;
|
||||
resizing: boolean;
|
||||
|
||||
// Offset of last mouse down and up events (in CSS pixels)
|
||||
down?: Offset;
|
||||
up?: Offset;
|
||||
down: Offset;
|
||||
up: Offset;
|
||||
|
||||
// Which side(s) we're currently resizing
|
||||
resizeSides?: {
|
||||
resizeSides: {
|
||||
left: boolean;
|
||||
right: boolean;
|
||||
top: boolean;
|
||||
bottom: boolean;
|
||||
};
|
||||
|
||||
boundsCss?: BoundsCss;
|
||||
boundsData?: BoundsData;
|
||||
boundsCss: BoundsCss;
|
||||
boundsData: BoundsData;
|
||||
|
||||
// Panel object that the brush is in
|
||||
panel?: Panel;
|
||||
panel: Panel | null;
|
||||
|
||||
// The bounds at the start of a drag/resize (in CSS pixels)
|
||||
changeStartBounds?: Bounds;
|
||||
changeStartBounds: Bounds;
|
||||
};
|
||||
|
||||
type BrushOpts = {
|
||||
@@ -66,9 +66,6 @@ type Brush = {
|
||||
// A callback when the wrapper div or img is resized.
|
||||
onResize: () => void;
|
||||
|
||||
// TODO define this type as both a getter and a setter interfaces.
|
||||
// boundsCss: (boxCss: BoundsCss) => void;
|
||||
// boundsCss: () => BoundsCss;
|
||||
boundsCss: {
|
||||
(boxCss: BoundsCss): void;
|
||||
(): BoundsCss;
|
||||
@@ -82,11 +79,11 @@ type Brush = {
|
||||
|
||||
down: {
|
||||
(): ImageState["down"];
|
||||
(offsetCss): void;
|
||||
(offsetCss: Offset): void;
|
||||
};
|
||||
up: {
|
||||
(): ImageState["up"];
|
||||
(offsetCss): void;
|
||||
(offsetCss: Offset): void;
|
||||
};
|
||||
|
||||
isBrushing: () => ImageState["brushing"];
|
||||
@@ -117,9 +114,9 @@ function createBrush(
|
||||
const resizeExpand = 10;
|
||||
|
||||
const el = $el[0];
|
||||
let $div = null; // The div representing the brush
|
||||
let $div: JQuery<HTMLElement> | null = null; // The div representing the brush
|
||||
|
||||
const state: ImageState = {};
|
||||
const state = {} as ImageState;
|
||||
|
||||
// Aliases for conciseness
|
||||
const cssToImg = coordmap.scaleCssToImg;
|
||||
@@ -225,8 +222,8 @@ function createBrush(
|
||||
const boundsDataVal = boundsData();
|
||||
// Check to see if we have valid boundsData
|
||||
|
||||
for (const val in boundsDataVal) {
|
||||
if (isnan(boundsDataVal[val])) return;
|
||||
for (const val in Object.values(boundsDataVal)) {
|
||||
if (isnan(val)) return;
|
||||
}
|
||||
|
||||
boundsData(boundsDataVal);
|
||||
@@ -234,7 +231,7 @@ function createBrush(
|
||||
}
|
||||
|
||||
// Return true if the offset is inside min/max coords
|
||||
function isInsideBrush(offsetCss) {
|
||||
function isInsideBrush(offsetCss: Offset) {
|
||||
const bounds = state.boundsCss;
|
||||
|
||||
return (
|
||||
@@ -246,14 +243,14 @@ function createBrush(
|
||||
}
|
||||
|
||||
// Return true if offset is inside a region to start a resize
|
||||
function isInResizeArea(offsetCss) {
|
||||
function isInResizeArea(offsetCss: Offset) {
|
||||
const sides = whichResizeSides(offsetCss);
|
||||
|
||||
return sides.left || sides.right || sides.top || sides.bottom;
|
||||
}
|
||||
|
||||
// Return an object representing which resize region(s) the cursor is in.
|
||||
function whichResizeSides(offsetCss) {
|
||||
function whichResizeSides(offsetCss: Offset) {
|
||||
const b = state.boundsCss;
|
||||
// Bounds with expansion
|
||||
const e = {
|
||||
@@ -299,13 +296,14 @@ function createBrush(
|
||||
function boundsCss(boxCss: BoundsCss): void;
|
||||
function boundsCss(boxCss?: BoundsCss) {
|
||||
if (boxCss === undefined) {
|
||||
return $.extend({}, state.boundsCss);
|
||||
return { ...state.boundsCss };
|
||||
}
|
||||
|
||||
let minCss = { x: boxCss.xmin, y: boxCss.ymin };
|
||||
let maxCss = { x: boxCss.xmax, y: boxCss.ymax };
|
||||
let minCss: Offset = { x: boxCss.xmin, y: boxCss.ymin };
|
||||
let maxCss: Offset = { x: boxCss.xmax, y: boxCss.ymax };
|
||||
|
||||
const panel = state.panel;
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const panel = state.panel!;
|
||||
const panelBoundsImg = panel.range;
|
||||
|
||||
if (opts.brushClip) {
|
||||
@@ -332,8 +330,8 @@ function createBrush(
|
||||
};
|
||||
|
||||
// Positions in data space
|
||||
const minData = state.panel.scaleImgToData(cssToImg(minCss));
|
||||
const maxData = state.panel.scaleImgToData(cssToImg(maxCss));
|
||||
const minData = panel.scaleImgToData(cssToImg(minCss));
|
||||
const maxData = panel.scaleImgToData(cssToImg(maxCss));
|
||||
// For reversed scales, the min and max can be reversed, so use findBox
|
||||
// to ensure correct order.
|
||||
|
||||
@@ -342,25 +340,28 @@ function createBrush(
|
||||
// (#1634).
|
||||
state.boundsData = mapValues(state.boundsData, (val) =>
|
||||
roundSignif(val, 14)
|
||||
) as BoundsData;
|
||||
);
|
||||
|
||||
// We also need to attach the data bounds and panel as data attributes, so
|
||||
// that if the image is re-sent, we can grab the data bounds to create a new
|
||||
// brush. This should be fast because it doesn't actually modify the DOM.
|
||||
$div.data("bounds-data", state.boundsData);
|
||||
$div.data("panel", state.panel);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
$div!.data("bounds-data", state.boundsData);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
$div!.data("panel", state.panel);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Get or set the bounds of the brush using coordinates in the data space.
|
||||
function boundsData(): ImageState["boundsData"];
|
||||
function boundsData(boxData: Parameters<Panel["scaleDataToImg"]>[0]): void;
|
||||
function boundsData(boxData?: Parameters<Panel["scaleDataToImg"]>[0]) {
|
||||
if (boxData === undefined) {
|
||||
return $.extend({}, state.boundsData);
|
||||
function boundsData(): BoundsData;
|
||||
function boundsData(boxData: BoundsData): void;
|
||||
function boundsData(boxData?: BoundsData | undefined): BoundsData | void {
|
||||
if (typeof boxData === "undefined") {
|
||||
return { ...state.boundsData };
|
||||
}
|
||||
|
||||
let boxCss = imgToCss(state.panel.scaleDataToImg(boxData));
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
let boxCss = imgToCss(state.panel!.scaleDataToImg(boxData));
|
||||
// Round to 13 significant digits to avoid spurious changes in FP values
|
||||
// (#2197).
|
||||
|
||||
@@ -383,6 +384,7 @@ function createBrush(
|
||||
|
||||
// Add a new div representing the brush.
|
||||
function addDiv() {
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
if ($div) $div.remove();
|
||||
|
||||
// Start hidden; we'll show it when movement occurs
|
||||
@@ -415,7 +417,13 @@ function createBrush(
|
||||
}
|
||||
|
||||
$el.append($div);
|
||||
$div.offset({ x: 0, y: 0 }).width(0).outerHeight(0);
|
||||
$div
|
||||
.offset(
|
||||
// @ts-expect-error; This is a jQuery Typing issue
|
||||
{ x: 0, y: 0 }
|
||||
)
|
||||
.width(0)
|
||||
.outerHeight(0);
|
||||
}
|
||||
|
||||
// Update the brush div to reflect the current brush bounds.
|
||||
@@ -425,7 +433,8 @@ function createBrush(
|
||||
const imgOffsetCss = findOrigin($el.find("img"));
|
||||
const b = state.boundsCss;
|
||||
|
||||
$div
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
$div!
|
||||
.offset({
|
||||
top: imgOffsetCss.y + b.ymin,
|
||||
left: imgOffsetCss.x + b.xmin,
|
||||
@@ -434,14 +443,18 @@ function createBrush(
|
||||
.outerHeight(b.ymax - b.ymin + 1);
|
||||
}
|
||||
|
||||
function down(offsetCss?: Offset) {
|
||||
function down(): ImageState["down"];
|
||||
function down(offsetCss: Offset): void;
|
||||
function down(offsetCss?: Offset | undefined) {
|
||||
if (offsetCss === undefined) return state.down;
|
||||
|
||||
state.down = offsetCss;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function up(offsetCss?: Offset) {
|
||||
function up(): ImageState["up"];
|
||||
function up(offsetCss: Offset): void;
|
||||
function up(offsetCss?: Offset | undefined) {
|
||||
if (offsetCss === undefined) return state.up;
|
||||
|
||||
state.up = offsetCss;
|
||||
@@ -463,7 +476,8 @@ function createBrush(
|
||||
|
||||
function brushTo(offsetCss: Offset) {
|
||||
boundsCss(findBox(state.down, offsetCss));
|
||||
$div.show();
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
$div!.show();
|
||||
updateDiv();
|
||||
}
|
||||
|
||||
@@ -479,7 +493,7 @@ function createBrush(
|
||||
|
||||
function startDragging() {
|
||||
state.dragging = true;
|
||||
state.changeStartBounds = $.extend({}, state.boundsCss);
|
||||
state.changeStartBounds = { ...state.boundsCss };
|
||||
}
|
||||
|
||||
function dragTo(offsetCss: Offset) {
|
||||
@@ -498,7 +512,8 @@ function createBrush(
|
||||
|
||||
// Clip to the plotting area
|
||||
if (opts.brushClip) {
|
||||
const panelBoundsImg = state.panel.range;
|
||||
const panel = state.panel as Panel;
|
||||
const panelBoundsImg = panel.range;
|
||||
const newBoundsImg = cssToImg(newBoundsCss);
|
||||
|
||||
// Convert to format for shiftToRange
|
||||
@@ -539,7 +554,7 @@ function createBrush(
|
||||
|
||||
function startResizing() {
|
||||
state.resizing = true;
|
||||
state.changeStartBounds = $.extend({}, state.boundsCss);
|
||||
state.changeStartBounds = { ...state.boundsCss };
|
||||
state.resizeSides = whichResizeSides(state.down);
|
||||
}
|
||||
|
||||
@@ -554,7 +569,8 @@ function createBrush(
|
||||
|
||||
// Calculate what new positions would be, before clipping.
|
||||
const bImg = cssToImg(state.changeStartBounds);
|
||||
const panelBoundsImg = state.panel.range;
|
||||
const panel = state.panel as Panel;
|
||||
const panelBoundsImg = panel.range;
|
||||
|
||||
if (state.resizeSides.left) {
|
||||
const xminImg = shiftToRange(
|
||||
|
||||
@@ -14,8 +14,8 @@ function createClickInfo(
|
||||
mousedown: (e: JQuery.MouseDownEvent) => void;
|
||||
dblclickIE8: (e: JQuery.DoubleClickEvent) => void;
|
||||
} {
|
||||
let clickTimer: ReturnType<typeof setTimeout> = null;
|
||||
let pendingE: JQuery.MouseDownEvent = null; // A pending mousedown2 event
|
||||
let clickTimer: number | undefined = undefined;
|
||||
let pendingE: JQuery.MouseDownEvent | null = null; // A pending mousedown2 event
|
||||
|
||||
// Create a new event of type eventType (like 'mousedown2'), and trigger
|
||||
// it with the information stored in this.e.
|
||||
@@ -49,7 +49,7 @@ function createClickInfo(
|
||||
function scheduleMousedown2(e: JQuery.MouseDownEvent) {
|
||||
pendingE = e;
|
||||
|
||||
clickTimer = setTimeout(function () {
|
||||
clickTimer = window.setTimeout(function () {
|
||||
triggerPendingMousedown2();
|
||||
}, dblclickDelay);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ type CreateHandler = {
|
||||
mouseout?: (e: JQuery.MouseOutEvent) => void;
|
||||
mousedown?: (e: JQuery.MouseDownEvent) => void;
|
||||
onResetImg: () => void;
|
||||
onResize?: () => void;
|
||||
onResize: ((e: JQuery.ResizeEvent) => void) | null;
|
||||
};
|
||||
|
||||
type BrushInfo = {
|
||||
@@ -153,7 +153,17 @@ function createBrushHandler(
|
||||
// el instead of the brush div, because the brush div has
|
||||
// 'pointer-events:none' so that it won't intercept pointer events.
|
||||
// If `style` is null, don't add a cursor style.
|
||||
function setCursorStyle(style) {
|
||||
function setCursorStyle(
|
||||
style:
|
||||
| "crosshair"
|
||||
| "ew-resize"
|
||||
| "grabbable"
|
||||
| "grabbing"
|
||||
| "nesw-resize"
|
||||
| "ns-resize"
|
||||
| "nwse-resize"
|
||||
| null
|
||||
) {
|
||||
$el.removeClass(
|
||||
"crosshair grabbable grabbing ns-resize ew-resize nesw-resize nwse-resize"
|
||||
);
|
||||
@@ -177,7 +187,8 @@ function createBrushHandler(
|
||||
return;
|
||||
}
|
||||
|
||||
const panel = brush.getPanel();
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const panel = brush.getPanel()!;
|
||||
|
||||
// Add the panel (facet) variables, if present
|
||||
$.extend(coords, panel.panel_vars);
|
||||
@@ -212,7 +223,9 @@ function createBrushHandler(
|
||||
.trigger("shiny-internal:brushed", coords);
|
||||
}
|
||||
|
||||
let brushInfoSender;
|
||||
let brushInfoSender:
|
||||
| Debouncer<typeof sendBrushInfo>
|
||||
| Throttler<typeof sendBrushInfo>;
|
||||
|
||||
if (opts.brushDelayType === "throttle") {
|
||||
brushInfoSender = new Throttler(null, sendBrushInfo, opts.brushDelay);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { shinySetInputValue } from "../shiny/initedMethods";
|
||||
import { mapValues } from "../utils";
|
||||
import type { Offset } from "./findbox";
|
||||
import type { Bounds } from "./createBrush";
|
||||
import type { Panel } from "./initPanelScales";
|
||||
import type { Panel, PanelInit } from "./initPanelScales";
|
||||
import { initPanelScales } from "./initPanelScales";
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -16,13 +16,16 @@ function findScalingRatio($el: JQuery<HTMLElement>) {
|
||||
const boundingRect = $el[0].getBoundingClientRect();
|
||||
|
||||
return {
|
||||
x: boundingRect.width / $el.outerWidth(),
|
||||
y: boundingRect.height / $el.outerHeight(),
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
x: boundingRect.width / $el.outerWidth()!,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
y: boundingRect.height / $el.outerHeight()!,
|
||||
};
|
||||
}
|
||||
|
||||
function findOrigin($el: JQuery<HTMLElement>): Offset {
|
||||
const offset = $el.offset();
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const offset = $el.offset()!;
|
||||
const scalingRatio = findScalingRatio($el);
|
||||
|
||||
// Find the size of the padding and border, for the top and left. This is
|
||||
@@ -50,8 +53,10 @@ function findDims($el: JQuery<HTMLElement>) {
|
||||
// If there's any padding/border, we need to find the ratio of the actual
|
||||
// element content compared to the element plus padding and border.
|
||||
const contentRatio = {
|
||||
x: $el.width() / $el.outerWidth(),
|
||||
y: $el.height() / $el.outerHeight(),
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
x: $el.width()! / $el.outerWidth()!,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
y: $el.height()! / $el.outerHeight()!,
|
||||
};
|
||||
|
||||
// Get the dimensions of the element _after_ any CSS transforms. This
|
||||
@@ -85,11 +90,13 @@ type Coords = {
|
||||
};
|
||||
|
||||
type CoordmapInit = {
|
||||
panels: Panel[];
|
||||
dims: {
|
||||
height: number;
|
||||
width: number;
|
||||
};
|
||||
panels: PanelInit[];
|
||||
dims:
|
||||
| {
|
||||
height: number;
|
||||
width: number;
|
||||
}
|
||||
| { height: null; width: null };
|
||||
};
|
||||
type Coordmap = {
|
||||
panels: Panel[];
|
||||
@@ -107,18 +114,19 @@ type Coordmap = {
|
||||
(offsetImg: Bounds): Bounds;
|
||||
(offsetImg: Offset): Offset;
|
||||
(offsetImg: OffsetImg): OffsetCss;
|
||||
(offsetImg: { [key: string]: number }): { [key: string]: number | null };
|
||||
};
|
||||
imgToCssScalingRatio: () => Offset;
|
||||
cssToImgScalingRatio: () => Offset;
|
||||
|
||||
getPanelCss: (offsetCss: OffsetCss, expand?: number) => Panel;
|
||||
getPanelCss: (offsetCss: OffsetCss, expand?: number) => Panel | null;
|
||||
isInPanelCss: (offsetCss: OffsetCss, expand?: number) => boolean;
|
||||
|
||||
mouseCoordinateSender: (
|
||||
inputId: string,
|
||||
clip?: boolean,
|
||||
nullOutside?: boolean
|
||||
) => (e: JQuery.MouseDownEvent | JQuery.MouseMoveEvent) => void;
|
||||
) => (e: JQuery.MouseDownEvent | JQuery.MouseMoveEvent | null) => void;
|
||||
};
|
||||
|
||||
// This adds functions to the coordmap object to handle various
|
||||
@@ -144,14 +152,13 @@ function initCoordmap(
|
||||
$el: JQuery<HTMLElement>,
|
||||
coordmap_: CoordmapInit
|
||||
): Coordmap {
|
||||
const coordmap = coordmap_ as Coordmap;
|
||||
const $img = $el.find("img");
|
||||
const img = $img[0];
|
||||
|
||||
// If we didn't get any panels, create a dummy one where the domain and range
|
||||
// are simply the pixel dimensions.
|
||||
// that we modify.
|
||||
if (coordmap.panels.length === 0) {
|
||||
if (coordmap_.panels.length === 0) {
|
||||
const bounds = {
|
||||
top: 0,
|
||||
left: 0,
|
||||
@@ -159,20 +166,23 @@ function initCoordmap(
|
||||
bottom: img.clientHeight - 1,
|
||||
};
|
||||
|
||||
coordmap.panels[0] = {
|
||||
coordmap_.panels[0] = {
|
||||
domain: bounds,
|
||||
range: bounds,
|
||||
mapping: {},
|
||||
};
|
||||
}
|
||||
|
||||
const coordmap = coordmap_ as Coordmap;
|
||||
// If no dim height and width values are found, set them to the raw image height and width
|
||||
// These values should be the same...
|
||||
// This is only done to initialize an image output, whose height and width are unknown until the image is retrieved
|
||||
|
||||
coordmap.dims.height = coordmap.dims.height || img.naturalHeight;
|
||||
coordmap.dims.width = coordmap.dims.width || img.naturalWidth;
|
||||
|
||||
// Add scaling functions to each panel
|
||||
initPanelScales(coordmap.panels);
|
||||
coordmap.panels = initPanelScales(coordmap_.panels);
|
||||
|
||||
// This returns the offset of the mouse in CSS pixels relative to the img,
|
||||
// but not including the padding or border, if present.
|
||||
@@ -195,7 +205,7 @@ function initCoordmap(
|
||||
function scaleCssToImg(offsetCss: Bounds): Bounds;
|
||||
function scaleCssToImg(offsetCss: Offset): Offset;
|
||||
function scaleCssToImg(offsetCss: OffsetCss): OffsetImg;
|
||||
function scaleCssToImg(offsetCss) {
|
||||
function scaleCssToImg(offsetCss: OffsetCss) {
|
||||
const pixelScaling = coordmap.imgToCssScalingRatio();
|
||||
|
||||
const result = mapValues(offsetCss, (value, key) => {
|
||||
@@ -221,7 +231,7 @@ function initCoordmap(
|
||||
function scaleImgToCss(offsetImg: Offset): Offset;
|
||||
function scaleImgToCss(offsetImg: OffsetImg): OffsetCss;
|
||||
function scaleImgToCss(offsetImg: { [key: string]: number }): {
|
||||
[key: string]: number;
|
||||
[key: string]: number | null;
|
||||
} {
|
||||
const pixelScaling = coordmap.imgToCssScalingRatio();
|
||||
|
||||
@@ -368,14 +378,15 @@ function initCoordmap(
|
||||
shinySetInputValue(inputId, coords, { priority: "event" });
|
||||
return;
|
||||
}
|
||||
const panel = coordmap.getPanelCss(coordsCss);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const panel = coordmap.getPanelCss(coordsCss)!;
|
||||
|
||||
const coordsImg = coordmap.scaleCssToImg(coordsCss);
|
||||
const coordsData = panel.scaleImgToData(coordsImg);
|
||||
|
||||
const coords: Coords = {
|
||||
x: coordsData.x,
|
||||
y: coordsData.y,
|
||||
x: coordsData?.x,
|
||||
y: coordsData?.y,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
coords_css: coordsCss,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import type { Offset } from "./findbox";
|
||||
import { mapValues } from "../utils";
|
||||
import type { Bounds } from "./createBrush";
|
||||
|
||||
// range.
|
||||
function mapLinear(
|
||||
@@ -35,10 +36,10 @@ function scaler1D(
|
||||
domainMax: number,
|
||||
rangeMin: number,
|
||||
rangeMax: number,
|
||||
logbase: number
|
||||
logbase: number | null
|
||||
) {
|
||||
return {
|
||||
scale: function (val: number, clip: boolean) {
|
||||
scale: function (val: number, clip?: boolean) {
|
||||
if (logbase) val = Math.log(val) / Math.log(logbase);
|
||||
return mapLinear(val, domainMin, domainMax, rangeMin, rangeMax, clip);
|
||||
},
|
||||
@@ -52,7 +53,7 @@ function scaler1D(
|
||||
};
|
||||
}
|
||||
|
||||
type Panel = {
|
||||
type PanelInit = {
|
||||
domain: {
|
||||
top: number;
|
||||
bottom: number;
|
||||
@@ -72,22 +73,22 @@ type Panel = {
|
||||
mapping: { [key: string]: string };
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
panel_vars?: { [key: string]: number | string };
|
||||
|
||||
scaleDataToImg?: (
|
||||
val: { [key: string]: number },
|
||||
clip?: boolean
|
||||
) => { [key: string]: number };
|
||||
scaleImgToData?: {
|
||||
};
|
||||
type Panel = PanelInit & {
|
||||
scaleDataToImg: {
|
||||
(val: Bounds, clip?: boolean): Bounds;
|
||||
};
|
||||
scaleImgToData: {
|
||||
(val: Offset, clip?: boolean): Offset;
|
||||
(val: { [key: string]: number }, clip?: boolean): { [key: string]: number };
|
||||
};
|
||||
|
||||
clipImg?: (offsetImg: { x: number; y: number }) => { x: number; y: number };
|
||||
clipImg: (offsetImg: { x: number; y: number }) => { x: number; y: number };
|
||||
};
|
||||
|
||||
// Modify panel, adding scale and inverse-scale functions that take objects
|
||||
// like {x:1, y:3}, and also add clip function.
|
||||
function addScaleFuns(panel: Panel) {
|
||||
function addScaleFuns(panel_: PanelInit): Panel {
|
||||
const panel = panel_ as Panel;
|
||||
const d = panel.domain;
|
||||
const r = panel.range;
|
||||
const xlog = panel.log && panel.log.x ? panel.log.x : null;
|
||||
@@ -98,7 +99,16 @@ function addScaleFuns(panel: Panel) {
|
||||
// Given an object of form {x:1, y:2}, or {x:1, xmin:2:, ymax: 3}, convert
|
||||
// from data coordinates to img. Whether a value is converted as x or y
|
||||
// depends on the first character of the key.
|
||||
panel.scaleDataToImg = function (val, clip) {
|
||||
// (val: Offset, clip?: boolean): Offset;
|
||||
// (val: Bounds, clip?: boolean): Bounds;
|
||||
// (val: { [key: `${"x" | "y"}${string}`]: number }, clip?: boolean): { [key: `${"x" | "y"}${string}`]: number }
|
||||
// (val: { [key: string]: number | null }, clip?: boolean): {
|
||||
// [key: string]: number | null;
|
||||
// };
|
||||
function scaleDataToImg(
|
||||
val: Bounds,
|
||||
clip?: Parameters<typeof xscaler.scale>[1]
|
||||
): Bounds {
|
||||
return mapValues(val, (value, key) => {
|
||||
const prefix = key.substring(0, 1);
|
||||
|
||||
@@ -107,12 +117,13 @@ function addScaleFuns(panel: Panel) {
|
||||
} else if (prefix === "y") {
|
||||
return yscaler.scale(value, clip);
|
||||
}
|
||||
// TODO-future; If we know the input is a valid input (starts with x/y), why do we still have this value?
|
||||
return null;
|
||||
});
|
||||
};
|
||||
}) as Bounds;
|
||||
}
|
||||
panel.scaleDataToImg = scaleDataToImg;
|
||||
|
||||
function scaleImgToData(val: Offset, clip?: boolean);
|
||||
function scaleImgToData(val: { [key: string]: number }, clip?: boolean) {
|
||||
function scaleImgToData(val: Offset, clip?: boolean): Offset {
|
||||
return mapValues(val, (value, key) => {
|
||||
const prefix = key.substring(0, 1);
|
||||
|
||||
@@ -121,8 +132,9 @@ function addScaleFuns(panel: Panel) {
|
||||
} else if (prefix === "y") {
|
||||
return yscaler.scaleInv(value, clip);
|
||||
}
|
||||
// TODO-future; If we know the input is a valid input (starts with x/y), why do we still have this value?
|
||||
return null;
|
||||
});
|
||||
}) as Offset;
|
||||
}
|
||||
panel.scaleImgToData = scaleImgToData;
|
||||
|
||||
@@ -143,20 +155,18 @@ function addScaleFuns(panel: Panel) {
|
||||
|
||||
return newOffset;
|
||||
};
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
// Modifies the panel objects in a coordmap, adding scaleImgToData(),
|
||||
// scaleDataToImg(), and clipImg() functions to each one. The panel objects
|
||||
// use img and data coordinates only; they do not use css coordinates. The
|
||||
// domain is in data coordinates; the range is in img coordinates.
|
||||
function initPanelScales(panels: Panel[]): void {
|
||||
function initPanelScales(panels: PanelInit[]): Panel[] {
|
||||
// Add the functions to each panel object.
|
||||
for (let i = 0; i < panels.length; i++) {
|
||||
const panel = panels[i];
|
||||
|
||||
addScaleFuns(panel);
|
||||
}
|
||||
return panels.map((panel) => addScaleFuns(panel));
|
||||
}
|
||||
|
||||
export type { Panel };
|
||||
export type { Panel, PanelInit };
|
||||
export { initPanelScales };
|
||||
|
||||
@@ -4,8 +4,6 @@ import { determineBrowserInfo } from "./browser";
|
||||
|
||||
import { windowShiny } from "../window/libraries";
|
||||
import { setShiny } from "../shiny";
|
||||
import { setBlobBuilder } from "../utils/blob";
|
||||
import { windowBlobBuilder } from "../window/blobBuilder";
|
||||
import { setUserAgent } from "../utils/userAgent";
|
||||
import { windowUserAgent } from "../window/userAgent";
|
||||
|
||||
@@ -20,8 +18,6 @@ function init(): void {
|
||||
|
||||
disableFormSubmission();
|
||||
|
||||
setBlobBuilder(windowBlobBuilder());
|
||||
|
||||
initReactlog();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { EventPriority } from "./inputPolicy";
|
||||
import type { InputPolicy, InputPolicyOpts } from "./inputPolicy";
|
||||
import { hasOwnProperty } from "../utils";
|
||||
import { hasDefinedProperty } from "../utils";
|
||||
|
||||
class InputDeferDecorator implements InputPolicy {
|
||||
pendingInput: {
|
||||
@@ -18,7 +18,7 @@ class InputDeferDecorator implements InputPolicy {
|
||||
}
|
||||
submit(): void {
|
||||
for (const nameType in this.pendingInput) {
|
||||
if (hasOwnProperty(this.pendingInput, nameType)) {
|
||||
if (hasDefinedProperty(this.pendingInput, nameType)) {
|
||||
const { value, opts } = this.pendingInput[nameType];
|
||||
|
||||
this.target.setInput(nameType, value, opts);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { InputPolicy, InputPolicyOpts } from "./inputPolicy";
|
||||
import { hasOwnProperty } from "../utils";
|
||||
import { hasDefinedProperty } from "../utils";
|
||||
import { splitInputNameType } from "./splitInputNameType";
|
||||
|
||||
type LastSentValues = { [key: string]: { [key: string]: string } };
|
||||
@@ -40,7 +40,7 @@ class InputNoResendDecorator implements InputPolicy {
|
||||
} = {};
|
||||
|
||||
for (const inputName in values) {
|
||||
if (hasOwnProperty(values, inputName)) {
|
||||
if (hasDefinedProperty(values, inputName)) {
|
||||
const { name, inputType } = splitInputNameType(inputName);
|
||||
|
||||
cacheValues[name] = {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { AnyVoidFunction } from "../utils/extraTypes";
|
||||
import type { InputPolicy } from "./inputPolicy";
|
||||
|
||||
interface InputRatePolicy<X extends AnyVoidFunction> {
|
||||
target: InputPolicy;
|
||||
target: InputPolicy | null;
|
||||
func: X;
|
||||
|
||||
normalCall(...args: Parameters<X>): void;
|
||||
|
||||
@@ -9,12 +9,19 @@ import type {
|
||||
import { shinyAppBindOutput, shinyAppUnbindOutput } from "./initedMethods";
|
||||
import { sendImageSizeFns } from "./sendImageSize";
|
||||
|
||||
const boundInputs = {};
|
||||
const boundInputs: {
|
||||
[key: string]: { binding: InputBinding; node: HTMLElement };
|
||||
} = {};
|
||||
|
||||
type BindScope = HTMLElement | JQuery<HTMLElement>;
|
||||
|
||||
// todo make sure allowDeferred can NOT be supplied and still work
|
||||
function valueChangeCallback(inputs, binding, el, allowDeferred) {
|
||||
function valueChangeCallback(
|
||||
inputs: InputValidateDecorator,
|
||||
binding: InputBinding,
|
||||
el: HTMLElement,
|
||||
allowDeferred: boolean
|
||||
) {
|
||||
let id = binding.getId(el);
|
||||
|
||||
if (id) {
|
||||
@@ -23,7 +30,11 @@ function valueChangeCallback(inputs, binding, el, allowDeferred) {
|
||||
|
||||
if (type) id = id + ":" + type;
|
||||
|
||||
const opts = {
|
||||
const opts: {
|
||||
priority: "deferred" | "immediate";
|
||||
binding: typeof binding;
|
||||
el: typeof el;
|
||||
} = {
|
||||
priority: allowDeferred ? "deferred" : "immediate",
|
||||
binding: binding,
|
||||
el: el,
|
||||
@@ -54,7 +65,16 @@ function bindInputs(
|
||||
const { inputs, inputsRate, inputBindings } = shinyCtx;
|
||||
const bindings = inputBindings.getBindings();
|
||||
|
||||
const inputItems = {};
|
||||
const inputItems: {
|
||||
[key: string]: {
|
||||
value: any;
|
||||
opts: {
|
||||
immediate: true;
|
||||
binding: InputBinding;
|
||||
el: HTMLElement;
|
||||
};
|
||||
};
|
||||
} = {};
|
||||
|
||||
for (let i = 0; i < bindings.length; i++) {
|
||||
const binding = bindings[i].binding;
|
||||
@@ -84,7 +104,7 @@ function bindInputs(
|
||||
const thisBinding = binding;
|
||||
const thisEl = el;
|
||||
|
||||
return function (allowDeferred) {
|
||||
return function (allowDeferred: boolean) {
|
||||
valueChangeCallback(inputs, thisBinding, thisEl, allowDeferred);
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -14,7 +14,7 @@ import { debounce, Debouncer } from "../time";
|
||||
import {
|
||||
getComputedLinkColor,
|
||||
getStyle,
|
||||
hasOwnProperty,
|
||||
hasDefinedProperty,
|
||||
mapValues,
|
||||
pixelRatio,
|
||||
} from "../utils";
|
||||
@@ -164,13 +164,17 @@ function initShiny(windowShiny: Shiny): void {
|
||||
}
|
||||
);
|
||||
|
||||
function getComputedBgColor(el) {
|
||||
function getComputedBgColor(
|
||||
el: HTMLElement | null
|
||||
): string | null | undefined {
|
||||
if (!el) {
|
||||
// Top of document, can't recurse further
|
||||
return null;
|
||||
}
|
||||
|
||||
const bgColor = getStyle(el, "background-color");
|
||||
|
||||
if (!bgColor) return bgColor;
|
||||
const m = bgColor.match(
|
||||
/^rgba\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)$/
|
||||
);
|
||||
@@ -190,12 +194,12 @@ function initShiny(windowShiny: Shiny): void {
|
||||
return bgColor;
|
||||
}
|
||||
|
||||
function getComputedFont(el) {
|
||||
function getComputedFont(el: HTMLElement) {
|
||||
const fontFamily = getStyle(el, "font-family");
|
||||
const fontSize = getStyle(el, "font-size");
|
||||
|
||||
return {
|
||||
families: fontFamily.replace(/"/g, "").split(", "),
|
||||
families: fontFamily?.replace(/"/g, "").split(", "),
|
||||
size: fontSize,
|
||||
};
|
||||
}
|
||||
@@ -252,7 +256,7 @@ function initShiny(windowShiny: Shiny): void {
|
||||
$el.data("shiny-theme-observer", observer);
|
||||
}
|
||||
|
||||
function doSendTheme(el) {
|
||||
function doSendTheme(el: HTMLElement): void {
|
||||
// Sending theme info on error isn't necessary (it'd add an unnecessary additional round-trip)
|
||||
if (el.classList.contains("shiny-output-error")) {
|
||||
return;
|
||||
@@ -310,7 +314,7 @@ function initShiny(windowShiny: Shiny): void {
|
||||
|
||||
// Return true if the object or one of its ancestors in the DOM tree has
|
||||
// style='display:none'; otherwise return false.
|
||||
function isHidden(obj) {
|
||||
function isHidden(obj: HTMLElement | null): boolean {
|
||||
// null means we've hit the top of the tree. If width or height is
|
||||
// non-zero, then we know that no ancestor has display:none.
|
||||
if (obj === null || obj.offsetWidth !== 0 || obj.offsetHeight !== 0) {
|
||||
@@ -318,10 +322,10 @@ function initShiny(windowShiny: Shiny): void {
|
||||
} else if (getStyle(obj, "display") === "none") {
|
||||
return true;
|
||||
} else {
|
||||
return isHidden(obj.parentNode);
|
||||
return isHidden(obj.parentNode as HTMLElement | null);
|
||||
}
|
||||
}
|
||||
let lastKnownVisibleOutputs = {};
|
||||
let lastKnownVisibleOutputs: { [key: string]: boolean } = {};
|
||||
// Set initial state of outputs to hidden, if needed
|
||||
|
||||
$(".shiny-bound-output").each(function () {
|
||||
@@ -336,7 +340,7 @@ function initShiny(windowShiny: Shiny): void {
|
||||
});
|
||||
// Send update when hidden state changes
|
||||
function doSendOutputHiddenState() {
|
||||
const visibleOutputs = {};
|
||||
const visibleOutputs: { [key: string]: boolean } = {};
|
||||
|
||||
$(".shiny-bound-output").each(function () {
|
||||
const id = getIdFromEl(this);
|
||||
@@ -364,7 +368,7 @@ function initShiny(windowShiny: Shiny): void {
|
||||
});
|
||||
// Anything left in lastKnownVisibleOutputs is orphaned
|
||||
for (const name in lastKnownVisibleOutputs) {
|
||||
if (hasOwnProperty(lastKnownVisibleOutputs, name))
|
||||
if (hasDefinedProperty(lastKnownVisibleOutputs, name))
|
||||
inputs.setInput(".clientdata_output_" + name + "_hidden", true);
|
||||
}
|
||||
// Update the visible outputs for next time
|
||||
@@ -394,18 +398,22 @@ function initShiny(windowShiny: Shiny): void {
|
||||
// the handler only when e's namespace matches. For example, if the
|
||||
// namespace is "bs", it would match when e.namespace is "bs" or "bs.tab".
|
||||
// If the namespace is "bs.tab", it would match for "bs.tab", but not "bs".
|
||||
function filterEventsByNamespace(namespace, handler, ...args) {
|
||||
namespace = namespace.split(".");
|
||||
function filterEventsByNamespace(
|
||||
namespace: string,
|
||||
handler: (...handlerArgs: any[]) => void,
|
||||
...args: any[]
|
||||
) {
|
||||
const namespaceArr = namespace.split(".");
|
||||
|
||||
return function (e) {
|
||||
const eventNamespace = e.namespace.split(".");
|
||||
return function (this: HTMLElement, e: JQuery.TriggeredEvent) {
|
||||
const eventNamespace = e.namespace?.split(".") ?? [];
|
||||
|
||||
// If any of the namespace strings aren't present in this event, quit.
|
||||
for (let i = 0; i < namespace.length; i++) {
|
||||
if (eventNamespace.indexOf(namespace[i]) === -1) return;
|
||||
for (let i = 0; i < namespaceArr.length; i++) {
|
||||
if (eventNamespace.indexOf(namespaceArr[i]) === -1) return;
|
||||
}
|
||||
|
||||
handler.apply(this, [namespace, handler, ...args]);
|
||||
handler.apply(this, [namespaceArr, handler, ...args]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -553,6 +561,7 @@ function initDeferredIframes(): void {
|
||||
const $el = $(el);
|
||||
|
||||
$el.removeClass("shiny-frame-deferred");
|
||||
// @ts-expect-error; If it is undefined, set using the undefined value
|
||||
$el.attr("src", $el.attr("data-deferred-src"));
|
||||
$el.attr("data-deferred-src", null);
|
||||
});
|
||||
|
||||
@@ -5,10 +5,33 @@ import type { EventPriority } from "../inputPolicies";
|
||||
import type { BindScope } from "./bind";
|
||||
import type { Handler, ShinyApp } from "./shinyapp";
|
||||
|
||||
let fullShinyObj: Shiny = null;
|
||||
let fullShinyObj: FullShinyDef;
|
||||
|
||||
// TODO-future; It would be nice to have a way to export this type value instead of / in addition to `Shiny`
|
||||
type FullShinyDef = Required<
|
||||
Pick<
|
||||
Shiny,
|
||||
| "bindAll"
|
||||
| "forgetLastInputValue"
|
||||
| "initializeInputs"
|
||||
| "oncustommessage"
|
||||
| "setInputValue"
|
||||
| "shinyapp"
|
||||
| "unbindAll"
|
||||
| "user"
|
||||
>
|
||||
> &
|
||||
Shiny;
|
||||
|
||||
function setShinyObj(shiny: Shiny): void {
|
||||
fullShinyObj = shiny;
|
||||
fullShinyObj = shiny as FullShinyDef;
|
||||
}
|
||||
|
||||
function validateShinyHasBeenSet(): FullShinyDef {
|
||||
if (typeof fullShinyObj === "undefined") {
|
||||
throw "Shiny has not finish initialization yet. Please wait for the 'shiny-initialized' event.";
|
||||
}
|
||||
return fullShinyObj;
|
||||
}
|
||||
|
||||
//// 2021/03: TypeScript Conversion note
|
||||
@@ -21,40 +44,40 @@ function shinySetInputValue(
|
||||
value: unknown,
|
||||
opts?: { priority?: EventPriority }
|
||||
): void {
|
||||
fullShinyObj.setInputValue(name, value, opts);
|
||||
validateShinyHasBeenSet().setInputValue(name, value, opts);
|
||||
}
|
||||
function shinyShinyApp(): ShinyApp {
|
||||
return fullShinyObj.shinyapp;
|
||||
return validateShinyHasBeenSet().shinyapp;
|
||||
}
|
||||
function setShinyUser(user: string): void {
|
||||
fullShinyObj.user = user;
|
||||
validateShinyHasBeenSet().user = user;
|
||||
}
|
||||
function shinyForgetLastInputValue(name: string): void {
|
||||
fullShinyObj.forgetLastInputValue(name);
|
||||
validateShinyHasBeenSet().forgetLastInputValue(name);
|
||||
}
|
||||
function shinyBindAll(scope: BindScope): void {
|
||||
fullShinyObj.bindAll(scope);
|
||||
validateShinyHasBeenSet().bindAll(scope);
|
||||
}
|
||||
function shinyUnbindAll(scope: BindScope, includeSelf = false): void {
|
||||
fullShinyObj.unbindAll(scope, includeSelf);
|
||||
validateShinyHasBeenSet().unbindAll(scope, includeSelf);
|
||||
}
|
||||
function shinyInitializeInputs(scope: BindScope): void {
|
||||
fullShinyObj.initializeInputs(scope);
|
||||
validateShinyHasBeenSet().initializeInputs(scope);
|
||||
}
|
||||
|
||||
function shinyAppBindOutput(id: string, binding: OutputBindingAdapter): void {
|
||||
fullShinyObj.shinyapp.bindOutput(id, binding);
|
||||
shinyShinyApp().bindOutput(id, binding);
|
||||
}
|
||||
|
||||
function shinyAppUnbindOutput(
|
||||
id: string,
|
||||
binding: OutputBindingAdapter
|
||||
): boolean {
|
||||
return fullShinyObj.shinyapp.unbindOutput(id, binding);
|
||||
return shinyShinyApp().unbindOutput(id, binding);
|
||||
}
|
||||
|
||||
function getShinyOnCustomMessage(): Handler | null {
|
||||
return fullShinyObj.oncustommessage;
|
||||
return validateShinyHasBeenSet().oncustommessage;
|
||||
}
|
||||
|
||||
let fileInputBinding: FileInputBinding;
|
||||
@@ -67,7 +90,7 @@ function setFileInputBinding(fileInputBinding_: FileInputBinding): void {
|
||||
}
|
||||
|
||||
function getShinyCreateWebsocket(): (() => WebSocket) | void {
|
||||
return fullShinyObj.createSocket;
|
||||
return validateShinyHasBeenSet().createSocket;
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -2,6 +2,7 @@ import $ from "jquery";
|
||||
|
||||
import { $escape, randomId } from "../utils";
|
||||
import { shinyUnbindAll } from "./initedMethods";
|
||||
import type { HtmlDep } from "./render";
|
||||
import { renderContent } from "./render";
|
||||
|
||||
// Milliseconds to fade in or out
|
||||
@@ -15,6 +16,14 @@ function show({
|
||||
id = null,
|
||||
closeButton = true,
|
||||
type = null,
|
||||
}: {
|
||||
html?: string;
|
||||
action?: string;
|
||||
deps?: HtmlDep[];
|
||||
duration?: number | null;
|
||||
id?: string | null;
|
||||
closeButton?: boolean;
|
||||
type?: string | null;
|
||||
} = {}): ReturnType<typeof randomId> {
|
||||
if (!id) id = randomId();
|
||||
|
||||
@@ -22,9 +31,10 @@ function show({
|
||||
createPanel();
|
||||
|
||||
// Get existing DOM element for this ID, or create if needed.
|
||||
let $notification = get(id);
|
||||
let $notificationInit = get(id);
|
||||
|
||||
if ($notification.length === 0) $notification = create(id);
|
||||
if ($notificationInit?.length === 0) $notificationInit = create(id);
|
||||
const $notification = $notificationInit as JQuery<HTMLElement>;
|
||||
|
||||
// Render html and dependencies
|
||||
const newHtml =
|
||||
@@ -36,13 +46,16 @@ function show({
|
||||
|
||||
// Remove any existing classes of the form 'shiny-notification-xxxx'.
|
||||
// The xxxx would be strings like 'warning'.
|
||||
const classes = $notification
|
||||
.attr("class")
|
||||
.split(/\s+/)
|
||||
.filter((cls) => cls.match(/^shiny-notification-/))
|
||||
.join(" ");
|
||||
const classes = $notification?.attr("class");
|
||||
|
||||
$notification.removeClass(classes);
|
||||
if (classes) {
|
||||
const classVal = classes
|
||||
.split(/\s+/)
|
||||
.filter((cls: string) => cls.match(/^shiny-notification-/))
|
||||
.join(" ");
|
||||
|
||||
$notification.removeClass(classVal);
|
||||
}
|
||||
|
||||
// Add class. 'default' means no additional CSS class.
|
||||
if (type && type !== "default")
|
||||
@@ -67,9 +80,8 @@ function show({
|
||||
return id;
|
||||
}
|
||||
|
||||
// TODO-barret - Should `id` be required? (some places do not supply one)
|
||||
function remove(id?: string): void {
|
||||
get(id).fadeOut(fadeDuration, function () {
|
||||
function remove(id: string): void {
|
||||
get(id)?.fadeOut(fadeDuration, function () {
|
||||
shinyUnbindAll(this);
|
||||
$(this).remove();
|
||||
|
||||
@@ -81,7 +93,7 @@ function remove(id?: string): void {
|
||||
}
|
||||
|
||||
// Returns an individual notification DOM object (wrapped in jQuery).
|
||||
function get(id?: string) {
|
||||
function get(id: string | null | undefined) {
|
||||
if (!id) return null;
|
||||
return getPanel().find("#shiny-notification-" + $escape(id));
|
||||
}
|
||||
@@ -115,10 +127,10 @@ function createPanel() {
|
||||
|
||||
// Create a notification DOM element and return the jQuery object. If the
|
||||
// DOM element already exists for the ID, just return it without creating.
|
||||
function create(id) {
|
||||
function create(id: string): JQuery<HTMLElement> {
|
||||
let $notification = get(id);
|
||||
|
||||
if ($notification.length === 0) {
|
||||
if ($notification?.length === 0) {
|
||||
$notification = $(
|
||||
`<div id="shiny-notification-${id}" class="shiny-notification">` +
|
||||
'<div class="shiny-notification-close">×</div>' +
|
||||
@@ -135,11 +147,11 @@ function create(id) {
|
||||
getPanel().append($notification);
|
||||
}
|
||||
|
||||
return $notification;
|
||||
return $notification as JQuery<HTMLElement>;
|
||||
}
|
||||
|
||||
// Add a callback to remove a notification after a delay in ms.
|
||||
function addRemovalCallback(id, delay) {
|
||||
function addRemovalCallback(id: string, delay: number) {
|
||||
// If there's an existing removalCallback, clear it before adding the new
|
||||
// one.
|
||||
clearRemovalCallback(id);
|
||||
@@ -149,14 +161,14 @@ function addRemovalCallback(id, delay) {
|
||||
remove(id);
|
||||
}, delay);
|
||||
|
||||
get(id).data("removalCallback", removalCallback);
|
||||
get(id)?.data("removalCallback", removalCallback);
|
||||
}
|
||||
|
||||
// Clear a removal callback from a notification, if present.
|
||||
function clearRemovalCallback(id) {
|
||||
function clearRemovalCallback(id: string) {
|
||||
const $notification = get(id);
|
||||
const oldRemovalCallback: ReturnType<typeof setTimeout> =
|
||||
$notification.data("removalCallback");
|
||||
$notification?.data("removalCallback");
|
||||
|
||||
if (oldRemovalCallback) {
|
||||
clearTimeout(oldRemovalCallback);
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import $ from "jquery";
|
||||
import { shinyShinyApp } from "./initedMethods";
|
||||
import { showNotification } from "./notifications";
|
||||
import type { ShinyApp } from "./shinyapp";
|
||||
|
||||
// We can use this method as `shinyShinyApp()` will throw if not initialized
|
||||
function shinyAppConfig() {
|
||||
return shinyShinyApp().config as NonNullable<ShinyApp["config"]>;
|
||||
}
|
||||
|
||||
function initReactlog(): void {
|
||||
$(document).on("keydown", function (e) {
|
||||
@@ -8,9 +14,9 @@ function initReactlog(): void {
|
||||
return;
|
||||
const url =
|
||||
"reactlog?w=" +
|
||||
window.escape(shinyShinyApp().config.workerId) +
|
||||
window.escape(shinyAppConfig().workerId) +
|
||||
"&s=" +
|
||||
window.escape(shinyShinyApp().config.sessionId);
|
||||
window.escape(shinyAppConfig().sessionId);
|
||||
|
||||
window.open(url);
|
||||
e.preventDefault();
|
||||
@@ -39,9 +45,9 @@ function initReactlog(): void {
|
||||
|
||||
const url =
|
||||
"reactlog/mark?w=" +
|
||||
window.escape(shinyShinyApp().config.workerId) +
|
||||
window.escape(shinyAppConfig().workerId) +
|
||||
"&s=" +
|
||||
window.escape(shinyShinyApp().config.sessionId);
|
||||
window.escape(shinyAppConfig().sessionId);
|
||||
|
||||
// send notification
|
||||
$.get(url, function (result: "marked" | void) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import $ from "jquery";
|
||||
|
||||
import { showNotification, removeNotification } from "./notifications";
|
||||
|
||||
function updateTime(reconnectTime): void {
|
||||
function updateTime(reconnectTime: number): void {
|
||||
const $time = $("#shiny-reconnect-time");
|
||||
// If the time has been removed, exit and don't reschedule this function.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import $ from "jquery";
|
||||
import { asArray, hasOwnProperty } from "../utils";
|
||||
import { asArray, hasDefinedProperty } from "../utils";
|
||||
import { isIE } from "../utils/browser";
|
||||
import type { BindScope } from "./bind";
|
||||
import {
|
||||
@@ -86,7 +86,8 @@ type MetaItem = {
|
||||
|
||||
type StylesheetItem = {
|
||||
href: string;
|
||||
[x: string]: string;
|
||||
rel?: string;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
type ScriptItem = {
|
||||
@@ -154,14 +155,17 @@ function renderDependency(dep_: HtmlDep) {
|
||||
// pass them through to `addStylesheetsAndRestyle` below.
|
||||
const stylesheetLinks = dep.stylesheet.map((x) => {
|
||||
// Add "rel" and "type" fields if not already present.
|
||||
if (!hasOwnProperty(x, "rel")) x.rel = "stylesheet";
|
||||
if (!hasOwnProperty(x, "type")) x.type = "text/css";
|
||||
if (!hasDefinedProperty(x, "rel")) x.rel = "stylesheet";
|
||||
if (!hasDefinedProperty(x, "type")) x.type = "text/css";
|
||||
|
||||
const link = document.createElement("link");
|
||||
|
||||
Object.entries(x).forEach(function ([attr, val]) {
|
||||
Object.entries(x).forEach(function ([attr, val]: [
|
||||
string,
|
||||
string | undefined
|
||||
]) {
|
||||
if (attr === "href") {
|
||||
val = encodeURI(val);
|
||||
val = encodeURI(val as string);
|
||||
}
|
||||
// If val isn't truthy (e.g., null), consider it a boolean attribute
|
||||
link.setAttribute(attr, val ? val : "");
|
||||
@@ -178,7 +182,7 @@ function renderDependency(dep_: HtmlDep) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasOwnProperty(htmlDependencies, dep.name)) return false;
|
||||
if (hasDefinedProperty(htmlDependencies, dep.name)) return false;
|
||||
|
||||
registerDependency(dep.name, dep.version);
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import { debounce, Debouncer } from "../time";
|
||||
class SendImageSize {
|
||||
// This function gets defined in initShiny() and 'hoisted' so it can be reused
|
||||
// (to send CSS info) inside of Shiny.renderDependencies()
|
||||
regular: () => void;
|
||||
transitioned: () => void;
|
||||
regular!: () => void;
|
||||
transitioned!: () => void;
|
||||
|
||||
setImageSend(
|
||||
inputBatchSender: InputBatchSender,
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import $ from "jquery";
|
||||
import {
|
||||
$escape,
|
||||
hasOwnProperty,
|
||||
makeBlob,
|
||||
randomId,
|
||||
scopeExprToFunc,
|
||||
} from "../utils";
|
||||
import { $escape, hasOwnProperty, randomId, scopeExprToFunc } from "../utils";
|
||||
import {
|
||||
getShinyCreateWebsocket,
|
||||
getShinyOnCustomMessage,
|
||||
@@ -49,6 +43,8 @@ type OnSuccessRequest = (value: ResponseValue) => void;
|
||||
type OnErrorRequest = (err: string) => void;
|
||||
type InputValues = { [key: string]: unknown };
|
||||
|
||||
type MessageValue = Parameters<WebSocket["send"]>[0];
|
||||
|
||||
//// 2021/03 - TypeScript conversion note:
|
||||
// These four variables were moved from being internally defined to being defined globally within the file.
|
||||
// Before the TypeScript conversion, the values where attached to `window.Shiny.addCustomMessageHandler()`.
|
||||
@@ -58,16 +54,16 @@ type InputValues = { [key: string]: unknown };
|
||||
// Records insertion order of handlers. Maps number to name. This is so
|
||||
// we can dispatch messages to handlers in the order that handlers were
|
||||
// added.
|
||||
const messageHandlerOrder = [];
|
||||
const messageHandlerOrder: string[] = [];
|
||||
// Keep track of handlers by name. Maps name to handler function.
|
||||
const messageHandlers = {};
|
||||
const messageHandlers: { [key: string]: Handler } = {};
|
||||
|
||||
// Two categories of message handlers: those that are from Shiny, and those
|
||||
// that are added by the user. The Shiny ones handle messages in
|
||||
// msgObj.values, msgObj.errors, and so on. The user ones handle messages
|
||||
// in msgObj.custom.foo and msgObj.custom.bar.
|
||||
const customMessageHandlerOrder = [];
|
||||
const customMessageHandlers = {};
|
||||
const customMessageHandlerOrder: string[] = [];
|
||||
const customMessageHandlers: { [key: string]: Handler } = {};
|
||||
|
||||
// Adds Shiny (internal) message handler
|
||||
function addMessageHandler(type: string, handler: Handler) {
|
||||
@@ -110,30 +106,30 @@ function addCustomMessageHandler(type: string, handler: Handler): void {
|
||||
//// End message handler variables
|
||||
|
||||
class ShinyApp {
|
||||
$socket: ShinyWebSocket = null;
|
||||
$socket: ShinyWebSocket | null = null;
|
||||
|
||||
config: {
|
||||
workerId: string;
|
||||
sessionId: string;
|
||||
} = null;
|
||||
} | null = null;
|
||||
|
||||
// Cached input values
|
||||
$inputValues: InputValues = {};
|
||||
|
||||
// Input values at initialization (and reconnect)
|
||||
$initialInput: InputValues;
|
||||
$initialInput: InputValues | null = null;
|
||||
|
||||
// Output bindings
|
||||
$bindings: { [key: string]: OutputBindingAdapter } = {};
|
||||
|
||||
// Cached values/errors
|
||||
$values = {};
|
||||
$values: { [key: string]: any } = {};
|
||||
$errors: { [key: string]: ErrorsMessageValue } = {};
|
||||
|
||||
// Conditional bindings (show/hide element based on expression)
|
||||
$conditionals = {};
|
||||
|
||||
$pendingMessages: string[] = [];
|
||||
$pendingMessages: MessageValue[] = [];
|
||||
$activeRequests: {
|
||||
[key: number]: { onSuccess: OnSuccessRequest; onError: OnErrorRequest };
|
||||
} = {};
|
||||
@@ -160,7 +156,7 @@ class ShinyApp {
|
||||
return !!this.$socket;
|
||||
}
|
||||
|
||||
private scheduledReconnect: ReturnType<typeof setTimeout> = null;
|
||||
private scheduledReconnect: number | undefined = undefined;
|
||||
|
||||
reconnect(): void {
|
||||
// This function can be invoked directly even if there's a scheduled
|
||||
@@ -230,7 +226,7 @@ class ShinyApp {
|
||||
while (this.$pendingMessages.length) {
|
||||
const msg = this.$pendingMessages.shift();
|
||||
|
||||
socket.send(msg);
|
||||
socket.send(msg as string);
|
||||
}
|
||||
};
|
||||
socket.onmessage = (e) => {
|
||||
@@ -280,7 +276,7 @@ class ShinyApp {
|
||||
}
|
||||
|
||||
$scheduleReconnect(delay: Parameters<typeof setTimeout>[1]): void {
|
||||
this.scheduledReconnect = setTimeout(() => {
|
||||
this.scheduledReconnect = window.setTimeout(() => {
|
||||
this.reconnect();
|
||||
}, delay);
|
||||
}
|
||||
@@ -327,7 +323,9 @@ class ShinyApp {
|
||||
// session$allowReconnect("force") was called. The "force" option should
|
||||
// only be used for testing.
|
||||
if (
|
||||
(this.$allowReconnect === true && this.$socket.allowReconnect === true) ||
|
||||
(this.$allowReconnect === true &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.$socket!.allowReconnect === true) ||
|
||||
this.$allowReconnect === "force"
|
||||
) {
|
||||
const delay = this.reconnectDelay.next();
|
||||
@@ -371,7 +369,7 @@ class ShinyApp {
|
||||
args: unknown[],
|
||||
onSuccess: OnSuccessRequest,
|
||||
onError: OnErrorRequest,
|
||||
blobs: Array<ArrayBuffer | Blob | string>
|
||||
blobs: Array<ArrayBuffer | Blob | string> | undefined
|
||||
): void {
|
||||
let requestId = this.$nextRequestId;
|
||||
|
||||
@@ -385,7 +383,7 @@ class ShinyApp {
|
||||
onError: onError,
|
||||
};
|
||||
|
||||
let msg = JSON.stringify({
|
||||
let msg: Blob | string = JSON.stringify({
|
||||
method: method,
|
||||
args: args,
|
||||
tag: requestId,
|
||||
@@ -397,7 +395,7 @@ class ShinyApp {
|
||||
// the length followed by the blob. The json payload is UTF-8 encoded
|
||||
// and used as the first blob.
|
||||
|
||||
const uint32ToBuf = function (val) {
|
||||
const uint32ToBuf = function (val: number) {
|
||||
const buffer = new ArrayBuffer(4);
|
||||
const view = new DataView(buffer);
|
||||
|
||||
@@ -409,7 +407,7 @@ class ShinyApp {
|
||||
|
||||
payload.push(uint32ToBuf(0x01020202)); // signature
|
||||
|
||||
const jsonBuf = makeBlob([msg]);
|
||||
const jsonBuf: Blob = new Blob([msg]);
|
||||
|
||||
payload.push(uint32ToBuf(jsonBuf.size));
|
||||
payload.push(jsonBuf);
|
||||
@@ -425,19 +423,21 @@ class ShinyApp {
|
||||
payload.push(blob);
|
||||
}
|
||||
|
||||
const blob = makeBlob(payload) as unknown;
|
||||
const blob: Blob = new Blob(payload);
|
||||
|
||||
msg = blob as string;
|
||||
msg = blob;
|
||||
}
|
||||
|
||||
this.$sendMsg(msg);
|
||||
}
|
||||
|
||||
$sendMsg(msg: string): void {
|
||||
if (!this.$socket.readyState) {
|
||||
$sendMsg(msg: MessageValue): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
if (!this.$socket!.readyState) {
|
||||
this.$pendingMessages.push(msg);
|
||||
} else {
|
||||
this.$socket.send(msg);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.$socket!.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +459,7 @@ class ShinyApp {
|
||||
}
|
||||
}
|
||||
|
||||
receiveOutput<T>(name: string, value: T): T {
|
||||
receiveOutput<T>(name: string, value: T): T | undefined {
|
||||
const binding = this.$bindings[name];
|
||||
const evt: ShinyEventValue = jQuery.Event("shiny:value");
|
||||
|
||||
@@ -510,7 +510,7 @@ class ShinyApp {
|
||||
// necessary.
|
||||
private _narrowScopeComponent<T>(
|
||||
scopeComponent: { [key: string]: T },
|
||||
nsPrefix: string | null
|
||||
nsPrefix: string
|
||||
) {
|
||||
return Object.keys(scopeComponent)
|
||||
.filter((k) => k.indexOf(nsPrefix) === 0)
|
||||
@@ -525,7 +525,13 @@ class ShinyApp {
|
||||
//
|
||||
// Otherwise, returns a new object with keys in subComponents removed and
|
||||
// renamed as necessary.
|
||||
private _narrowScope(scope, nsPrefix: string) {
|
||||
private _narrowScope(
|
||||
scope: {
|
||||
input: InputValues;
|
||||
output: { [key: string]: any };
|
||||
},
|
||||
nsPrefix: string
|
||||
) {
|
||||
if (nsPrefix) {
|
||||
return {
|
||||
input: this._narrowScopeComponent(scope.input, nsPrefix),
|
||||
@@ -541,7 +547,7 @@ class ShinyApp {
|
||||
type: "shiny:conditional",
|
||||
});
|
||||
|
||||
const inputs = {};
|
||||
const inputs: InputValues = {};
|
||||
|
||||
// Input keys use "name:type" format; we don't want the user to
|
||||
// have to know about the type suffix when referring to inputs.
|
||||
@@ -562,13 +568,13 @@ class ShinyApp {
|
||||
let condFunc = el.data("data-display-if-func");
|
||||
|
||||
if (!condFunc) {
|
||||
const condExpr = el.attr("data-display-if");
|
||||
const condExpr = el.attr("data-display-if") as string;
|
||||
|
||||
condFunc = scopeExprToFunc(condExpr);
|
||||
el.data("data-display-if-func", condFunc);
|
||||
}
|
||||
|
||||
const nsPrefix = el.attr("data-ns-prefix");
|
||||
const nsPrefix = el.attr("data-ns-prefix") as string;
|
||||
const nsScope = this._narrowScope(scope, nsPrefix);
|
||||
const show = condFunc(nsScope);
|
||||
const showing = el.css("display") !== "none";
|
||||
@@ -610,6 +616,7 @@ class ShinyApp {
|
||||
|
||||
data = data.slice(len + 1);
|
||||
msgObj.custom = {};
|
||||
// @ts-expect-error; `custom` value is of unknown type. So setting within it is not allowed
|
||||
msgObj.custom[type] = data;
|
||||
}
|
||||
|
||||
@@ -655,7 +662,7 @@ class ShinyApp {
|
||||
// * Use arrow functions to allow the Types to propagate.
|
||||
// * However, `_sendMessagesToHandlers()` will adjust the `this` context to the same _`this`_.
|
||||
|
||||
addMessageHandler("values", (message: { [key: string]: unknown }) => {
|
||||
addMessageHandler("values", (message: { [key: string]: any }) => {
|
||||
for (const name in this.$bindings) {
|
||||
if (hasOwnProperty(this.$bindings, name))
|
||||
this.$bindings[name].showProgress(false);
|
||||
@@ -670,7 +677,10 @@ class ShinyApp {
|
||||
|
||||
addMessageHandler(
|
||||
"errors",
|
||||
function (message: { [key: string]: ErrorsMessageValue }) {
|
||||
function (
|
||||
this: ShinyApp,
|
||||
message: { [key: string]: ErrorsMessageValue }
|
||||
) {
|
||||
for (const key in message) {
|
||||
if (hasOwnProperty(message, key))
|
||||
this.receiveError(key, message[key]);
|
||||
@@ -716,8 +726,12 @@ class ShinyApp {
|
||||
|
||||
addMessageHandler(
|
||||
"progress",
|
||||
(message: { type: string; message: { id: string } }) => {
|
||||
function (
|
||||
this: ShinyApp,
|
||||
message: { type: string; message: { id: string } }
|
||||
) {
|
||||
if (message.type && message.message) {
|
||||
// @ts-expect-error; Unknown values handled with followup if statement
|
||||
const handler = this.progressHandlers[message.type];
|
||||
|
||||
if (handler) handler.call(this, message.message);
|
||||
@@ -756,14 +770,15 @@ class ShinyApp {
|
||||
|
||||
addMessageHandler(
|
||||
"response",
|
||||
(message: { tag: string; value?: ResponseValue; error?: string }) => {
|
||||
(message: { tag: number; value?: ResponseValue; error?: string }) => {
|
||||
const requestId = message.tag;
|
||||
const request = this.$activeRequests[requestId];
|
||||
|
||||
if (request) {
|
||||
delete this.$activeRequests[requestId];
|
||||
if ("value" in message) request.onSuccess(message.value);
|
||||
else request.onError(message.error);
|
||||
if ("value" in message)
|
||||
request.onSuccess(message.value as UploadInitValue);
|
||||
else request.onError(message.error as string);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -827,12 +842,13 @@ class ShinyApp {
|
||||
hasOwnProperty(message, "name") &&
|
||||
hasOwnProperty(message, "status")
|
||||
) {
|
||||
const binding = this.$bindings[message.name];
|
||||
const binding = this.$bindings[message.name as string];
|
||||
|
||||
// @ts-expect-error; TODO-barret; Could this be transformed into `.trigger(TYPE)`?
|
||||
$(binding ? binding.el : null).trigger({
|
||||
type: "shiny:" + message.status,
|
||||
});
|
||||
if (binding) {
|
||||
$(binding.el).trigger("shiny:" + message.status);
|
||||
} else {
|
||||
$().trigger("shiny:" + message.status);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -908,7 +924,7 @@ class ShinyApp {
|
||||
}
|
||||
|
||||
function getTabContent($tabset: JQuery<HTMLElement>) {
|
||||
const tabsetId = $tabset.attr("data-tabsetid");
|
||||
const tabsetId = $tabset.attr("data-tabsetid") as string;
|
||||
const $tabContent = $(
|
||||
"div.tab-content[data-tabsetid='" + $escape(tabsetId) + "']"
|
||||
);
|
||||
@@ -933,13 +949,13 @@ class ShinyApp {
|
||||
"'"
|
||||
);
|
||||
}
|
||||
const $liTags = [];
|
||||
const $divTags = [];
|
||||
const $liTags: Array<JQuery<HTMLElement>> = [];
|
||||
const $divTags: Array<JQuery<HTMLElement>> = [];
|
||||
|
||||
if ($aTag.attr("data-toggle") === "dropdown") {
|
||||
// dropdown
|
||||
const $dropdownTabset = $aTag.find("+ ul.dropdown-menu");
|
||||
const dropdownId = $dropdownTabset.attr("data-tabsetid");
|
||||
const dropdownId = $dropdownTabset.attr("data-tabsetid") as string;
|
||||
|
||||
const $dropdownLiTags = $dropdownTabset
|
||||
.find("a[data-toggle='tab']")
|
||||
@@ -983,12 +999,16 @@ class ShinyApp {
|
||||
|
||||
// Unless the item is being prepended/appended, the target tab
|
||||
// must be provided
|
||||
let target = null;
|
||||
let $targetLiTag = null;
|
||||
let $targetLiTag: JQuery<HTMLElement> | null = null;
|
||||
|
||||
if (message.target !== null) {
|
||||
target = getTargetTabs($tabset, $tabContent, message.target);
|
||||
$targetLiTag = target.$liTag;
|
||||
const targetInfo = getTargetTabs(
|
||||
$tabset,
|
||||
$tabContent,
|
||||
message.target as string
|
||||
);
|
||||
|
||||
$targetLiTag = targetInfo.$liTag;
|
||||
}
|
||||
|
||||
// If the item is to be placed inside a navbarMenu (dropdown),
|
||||
@@ -1106,7 +1126,10 @@ class ShinyApp {
|
||||
fix the dummy id given to the tab in the R side -- there, we always
|
||||
set the tab id (counter dummy) to "id" and the tabset id to "tsid")
|
||||
*/
|
||||
function getTabIndex($tabset, tabsetId) {
|
||||
function getTabIndex(
|
||||
$tabset: JQuery<HTMLElement>,
|
||||
tabsetId: string | undefined
|
||||
) {
|
||||
// The 0 is to ensure this works for empty tabsetPanels as well
|
||||
const existingTabIds = [0];
|
||||
// loop through all existing tabs, find the one with highest id
|
||||
@@ -1117,9 +1140,11 @@ class ShinyApp {
|
||||
|
||||
if ($tab.length > 0) {
|
||||
// remove leading url if it exists. (copy of bootstrap url stripper)
|
||||
const href = $tab.attr("href").replace(/.*(?=#[^\s]+$)/, "");
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const href = $tab.attr("href")!.replace(/.*(?=#[^\s]+$)/, "");
|
||||
// remove tab id to get the index
|
||||
const index = href.replace("#tab-" + tabsetId + "-", "");
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const index = href!.replace("#tab-" + tabsetId + "-", "");
|
||||
|
||||
existingTabIds.push(Number(index));
|
||||
}
|
||||
@@ -1151,7 +1176,7 @@ class ShinyApp {
|
||||
const dropdownId = $dropdownTabset.attr("data-tabsetid");
|
||||
|
||||
return { $tabset: $dropdownTabset, id: dropdownId };
|
||||
} else if (message.target !== null) {
|
||||
} else if (message.target !== null && $targetLiTag !== null) {
|
||||
// if our item is to be placed next to a tab that is inside
|
||||
// a navbarMenu, our item will also be inside
|
||||
const $uncleTabset = $targetLiTag.parent("ul");
|
||||
@@ -1168,7 +1193,7 @@ class ShinyApp {
|
||||
);
|
||||
|
||||
// If the given tabset has no active tabs, select the first one
|
||||
function ensureTabsetHasVisibleTab($tabset) {
|
||||
function ensureTabsetHasVisibleTab($tabset: JQuery<HTMLElement>) {
|
||||
const inputBinding = $tabset.data("shiny-input-binding");
|
||||
|
||||
// Use the getValue() method to avoid duplicating the CSS selector
|
||||
@@ -1189,7 +1214,7 @@ class ShinyApp {
|
||||
|
||||
// Given a tabset ul jquery object, return the value of the first tab
|
||||
// (in document order) that's visible and able to be selected.
|
||||
function getFirstTab($ul) {
|
||||
function getFirstTab($ul: JQuery<HTMLElement>) {
|
||||
return (
|
||||
$ul
|
||||
.find("li:visible a[data-toggle='tab']")
|
||||
@@ -1198,21 +1223,31 @@ class ShinyApp {
|
||||
);
|
||||
}
|
||||
|
||||
function tabApplyFunction(target, func, liTags = false) {
|
||||
function tabApplyFunction(
|
||||
target: ReturnType<typeof getTargetTabs>,
|
||||
func: ($el: JQuery<HTMLElement>) => void,
|
||||
liTags = false
|
||||
) {
|
||||
$.each(target, function (key, el) {
|
||||
if (key === "$liTag") {
|
||||
// $liTag is always just one jQuery element
|
||||
func(el);
|
||||
func(el as ReturnType<typeof getTargetTabs>["$liTag"]);
|
||||
} else if (key === "$divTags") {
|
||||
// $divTags is always an array (even if length = 1)
|
||||
$.each(el, function (i, div) {
|
||||
func(div);
|
||||
});
|
||||
$.each(
|
||||
el as ReturnType<typeof getTargetTabs>["$divTags"],
|
||||
function (i, div) {
|
||||
func(div);
|
||||
}
|
||||
);
|
||||
} else if (liTags && key === "$liTags") {
|
||||
// $liTags is always an array (even if length = 0)
|
||||
$.each(el, function (i, div) {
|
||||
func(div);
|
||||
});
|
||||
$.each(
|
||||
el as ReturnType<typeof getTargetTabs>["$liTags"],
|
||||
function (i, div) {
|
||||
func(div);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1228,7 +1263,7 @@ class ShinyApp {
|
||||
|
||||
ensureTabsetHasVisibleTab($tabset);
|
||||
|
||||
function removeEl($el) {
|
||||
function removeEl($el: JQuery<HTMLElement>) {
|
||||
shinyUnbindAll($el, true);
|
||||
$el.remove();
|
||||
}
|
||||
@@ -1250,7 +1285,7 @@ class ShinyApp {
|
||||
|
||||
ensureTabsetHasVisibleTab($tabset);
|
||||
|
||||
function changeVisibility($el) {
|
||||
function changeVisibility($el: JQuery<HTMLElement>) {
|
||||
if (message.type === "show") $el.css("display", "");
|
||||
else if (message.type === "hide") {
|
||||
$el.hide();
|
||||
@@ -1265,6 +1300,7 @@ class ShinyApp {
|
||||
(message: { mode: unknown | "replace"; queryString: string }) => {
|
||||
// leave the bookmarking code intact
|
||||
if (message.mode === "replace") {
|
||||
// @ts-expect-error; No title value being supplied
|
||||
window.history.replaceState(null, null, message.queryString);
|
||||
return;
|
||||
}
|
||||
@@ -1298,6 +1334,7 @@ class ShinyApp {
|
||||
|
||||
if (what === "query") relURL += message.queryString;
|
||||
else relURL += oldQS + message.queryString; // leave old QS if it exists
|
||||
// @ts-expect-error; No title value being supplied
|
||||
window.history.pushState(null, null, relURL);
|
||||
|
||||
// for the case when message.queryString has both a query string
|
||||
@@ -1327,7 +1364,7 @@ class ShinyApp {
|
||||
|
||||
progressHandlers = {
|
||||
// Progress for a particular object
|
||||
binding: function (message: { id: string }): void {
|
||||
binding: function (this: ShinyApp, message: { id: string }): void {
|
||||
const key = message.id;
|
||||
const binding = this.$bindings[key];
|
||||
|
||||
@@ -1395,17 +1432,24 @@ class ShinyApp {
|
||||
// Stack bars
|
||||
const $progressBar = $progress.find(".progress");
|
||||
|
||||
$progressBar.css("top", depth * $progressBar.height() + "px");
|
||||
if ($progressBar) {
|
||||
$progressBar.css(
|
||||
"top",
|
||||
depth * ($progressBar.height() as number) + "px"
|
||||
);
|
||||
|
||||
// Stack text objects
|
||||
const $progressText = $progress.find(".progress-text");
|
||||
// Stack text objects
|
||||
const $progressText = $progress.find(".progress-text");
|
||||
|
||||
$progressText.css(
|
||||
"top",
|
||||
3 * $progressBar.height() + depth * $progressText.outerHeight() + "px"
|
||||
);
|
||||
$progressText.css(
|
||||
"top",
|
||||
3 * ($progressBar.height() as number) +
|
||||
depth * ($progressText.outerHeight() as number) +
|
||||
"px"
|
||||
);
|
||||
|
||||
$progress.hide();
|
||||
$progress.hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1492,9 +1536,11 @@ class ShinyApp {
|
||||
}
|
||||
url +=
|
||||
"/session/" +
|
||||
encodeURIComponent(this.config.sessionId) +
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
encodeURIComponent(this.config!.sessionId) +
|
||||
"/dataobj/shinytest?w=" +
|
||||
encodeURIComponent(this.config.workerId) +
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
encodeURIComponent(this.config!.workerId) +
|
||||
"&nonce=" +
|
||||
randomId();
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ function renderHtml(
|
||||
}
|
||||
// Take an object where keys are names of singletons, and merges it into
|
||||
// knownSingletons
|
||||
function register(s) {
|
||||
function register(s: typeof knownSingletons) {
|
||||
$.extend(knownSingletons, s);
|
||||
}
|
||||
// Takes a string or array of strings and adds them to knownSingletons
|
||||
@@ -84,7 +84,12 @@ function processHtml(val: string): {
|
||||
const newSingletons: typeof knownSingletons = {};
|
||||
let newVal: string;
|
||||
|
||||
const findNewPayload = function (match, p1, sig, payload) {
|
||||
const findNewPayload = function (
|
||||
match: string,
|
||||
p1: string,
|
||||
sig: string,
|
||||
payload: string
|
||||
) {
|
||||
if (knownSingletons[sig] || newSingletons[sig]) return "";
|
||||
newSingletons[sig] = true;
|
||||
return payload;
|
||||
@@ -97,8 +102,8 @@ function processHtml(val: string): {
|
||||
val = newVal;
|
||||
}
|
||||
|
||||
const heads = [];
|
||||
const headAddPayload = function (match, payload) {
|
||||
const heads: string[] = [];
|
||||
const headAddPayload = function (match: string, payload: string) {
|
||||
heads.push(payload);
|
||||
return "";
|
||||
};
|
||||
|
||||
9
srcts/src/testing/extendJQuery.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// Used to avoid isolated module warning
|
||||
import "jquery";
|
||||
|
||||
declare global {
|
||||
interface JQuery {
|
||||
// used for testing only
|
||||
internalTest: () => void;
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,17 @@ import type { InputRatePolicy } from "../inputPolicies/inputRatePolicy";
|
||||
import type { AnyVoidFunction } from "../utils/extraTypes";
|
||||
|
||||
class Debouncer<X extends AnyVoidFunction> implements InputRatePolicy<X> {
|
||||
target: InputPolicy;
|
||||
target: InputPolicy | null;
|
||||
func: X;
|
||||
delayMs: number | undefined;
|
||||
timerId: ReturnType<typeof setTimeout> | null;
|
||||
args: Parameters<X> | null;
|
||||
|
||||
constructor(target: InputPolicy, func: X, delayMs: number | undefined) {
|
||||
constructor(
|
||||
target: InputPolicy | null,
|
||||
func: X,
|
||||
delayMs: number | undefined
|
||||
) {
|
||||
this.target = target;
|
||||
this.func = func;
|
||||
this.delayMs = delayMs;
|
||||
|
||||
@@ -3,10 +3,10 @@ import type { InputRatePolicy } from "../inputPolicies/inputRatePolicy";
|
||||
import type { AnyVoidFunction } from "../utils/extraTypes";
|
||||
|
||||
class Invoker<X extends AnyVoidFunction> implements InputRatePolicy<X> {
|
||||
target: InputPolicy;
|
||||
target: InputPolicy | null;
|
||||
func: X;
|
||||
|
||||
constructor(target: InputPolicy, func: X) {
|
||||
constructor(target: InputPolicy | null, func: X) {
|
||||
this.target = target;
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,17 @@ import type { InputRatePolicy } from "../inputPolicies/inputRatePolicy";
|
||||
import type { AnyVoidFunction } from "../utils/extraTypes";
|
||||
|
||||
class Throttler<X extends AnyVoidFunction> implements InputRatePolicy<X> {
|
||||
target: InputPolicy;
|
||||
target: InputPolicy | null;
|
||||
func: X;
|
||||
delayMs: number | undefined;
|
||||
timerId: ReturnType<typeof setTimeout> | null;
|
||||
args: Parameters<X> | null;
|
||||
|
||||
constructor(target: InputPolicy, func: X, delayMs: number | undefined) {
|
||||
constructor(
|
||||
target: InputPolicy | null,
|
||||
func: X,
|
||||
delayMs: number | undefined
|
||||
) {
|
||||
this.target = target;
|
||||
this.func = func;
|
||||
this.delayMs = delayMs;
|
||||
|
||||
@@ -5,7 +5,7 @@ import $ from "jquery";
|
||||
import { getQueriesForElement } from "@testing-library/dom";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
$.fn.internalTest = function countify() {
|
||||
$.fn.internalTest = function countify(this: JQuery<HTMLElement>) {
|
||||
this.html(`
|
||||
<div>
|
||||
<button>0</button>
|
||||
@@ -13,10 +13,13 @@ $.fn.internalTest = function countify() {
|
||||
`);
|
||||
const $button = this.find("button");
|
||||
|
||||
$button._count = 0;
|
||||
// JQuery<HTMLButtonElement>
|
||||
|
||||
let count = 0;
|
||||
|
||||
$button.click(() => {
|
||||
$button._count++;
|
||||
$button.text($button._count);
|
||||
count++;
|
||||
$button.text(count);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import $ from "jquery";
|
||||
|
||||
type BlobBuilderConstructor = typeof window.MSBlobBuilder;
|
||||
|
||||
let blobBuilderClass: BlobBuilderConstructor;
|
||||
|
||||
function setBlobBuilder(blobBuilderClass_: BlobBuilderConstructor): void {
|
||||
blobBuilderClass = blobBuilderClass_;
|
||||
return;
|
||||
}
|
||||
|
||||
function makeBlob(parts: BlobPart[]): Blob {
|
||||
// Browser compatibility is a mess right now. The code as written works in
|
||||
// a variety of modern browsers, but sadly gives a deprecation warning
|
||||
// message on the console in current versions (as of this writing) of
|
||||
// Chrome.
|
||||
|
||||
// Safari 6.0 (8536.25) on Mac OS X 10.8.1:
|
||||
// Has Blob constructor but it doesn't work with ArrayBufferView args
|
||||
|
||||
// Google Chrome 21.0.1180.81 on Xubuntu 12.04:
|
||||
// Has Blob constructor, accepts ArrayBufferView args, accepts ArrayBuffer
|
||||
// but with a deprecation warning message
|
||||
|
||||
// Firefox 15.0 on Xubuntu 12.04:
|
||||
// Has Blob constructor, accepts both ArrayBuffer and ArrayBufferView args
|
||||
|
||||
// Chromium 18.0.1025.168 (Developer Build 134367 Linux) on Xubuntu 12.04:
|
||||
// No Blob constructor. Has WebKitBlobBuilder.
|
||||
|
||||
try {
|
||||
return new Blob(parts);
|
||||
} catch (e) {
|
||||
const blobBuilder = new blobBuilderClass();
|
||||
|
||||
$.each(parts, function (i, part) {
|
||||
blobBuilder.append(part);
|
||||
});
|
||||
return blobBuilder.getBlob();
|
||||
}
|
||||
}
|
||||
|
||||
export { makeBlob, setBlobBuilder };
|
||||
export type { BlobBuilderConstructor };
|
||||
@@ -1,4 +1,19 @@
|
||||
type AnyFunction = (...args: any[]) => any;
|
||||
type AnyVoidFunction = (...args: any[]) => void;
|
||||
type MapValuesUnion<T> = T[keyof T];
|
||||
type MapWithResult<X, R> = {
|
||||
[Property in keyof X]: R;
|
||||
};
|
||||
|
||||
export type { AnyFunction, AnyVoidFunction };
|
||||
/**
|
||||
* Exclude undefined from T
|
||||
*/
|
||||
type NotUndefined<T> = T extends undefined ? never : T;
|
||||
|
||||
export type {
|
||||
AnyFunction,
|
||||
AnyVoidFunction,
|
||||
MapValuesUnion,
|
||||
MapWithResult,
|
||||
NotUndefined,
|
||||
};
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import $ from "jquery";
|
||||
import { windowDevicePixelRatio } from "../window/pixelRatio";
|
||||
import { makeBlob } from "./blob";
|
||||
import { hasOwnProperty } from "./object";
|
||||
import type { MapValuesUnion, MapWithResult } from "./extraTypes";
|
||||
import { hasOwnProperty, hasDefinedProperty } from "./object";
|
||||
|
||||
function escapeHTML(str: string): string {
|
||||
const escaped = {
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
const escaped: { [key: string]: string } = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
"\"": """,
|
||||
'"': """,
|
||||
"'": "'",
|
||||
"/": "/",
|
||||
};
|
||||
|
||||
return str.replace(/[&<>'"/]/g, function (m) {
|
||||
return escaped[m];
|
||||
return escaped[m] as string;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -41,13 +42,14 @@ function strToBool(str: string): boolean | undefined {
|
||||
function getStyle(el: Element, styleProp: string): string | undefined {
|
||||
let x = undefined;
|
||||
|
||||
// @ts-expect-error; Old, IE 5+ attribute only - https://developer.mozilla.org/en-US/docs/Web/API/Element/currentStyle
|
||||
if (el.currentStyle) x = el.currentStyle[styleProp];
|
||||
else if (window.getComputedStyle) {
|
||||
if ("currentStyle" in el) {
|
||||
// @ts-expect-error; Old, IE 5+ attribute only - https://developer.mozilla.org/en-US/docs/Web/API/Element/currentStyle
|
||||
x = el.currentStyle[styleProp];
|
||||
} else {
|
||||
// getComputedStyle can return null when we're inside a hidden iframe on
|
||||
// Firefox; don't attempt to retrieve style props in this case.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=548397
|
||||
const style = document.defaultView.getComputedStyle(el, null);
|
||||
const style = document?.defaultView?.getComputedStyle(el, null);
|
||||
|
||||
if (style) x = style.getPropertyValue(styleProp);
|
||||
}
|
||||
@@ -85,6 +87,7 @@ function parseDate(dateString: string): Date {
|
||||
|
||||
// Given a Date object, return a string in yyyy-mm-dd format, using the
|
||||
// UTC date. This may be a day off from the date in the local time zone.
|
||||
function formatDateUTC(x: Date): string;
|
||||
function formatDateUTC(date: Date | null): string | null {
|
||||
if (date instanceof Date) {
|
||||
return (
|
||||
@@ -186,11 +189,11 @@ function asArray<T>(value: T | T[] | null | undefined): T[] {
|
||||
|
||||
// We need a stable sorting algorithm for ordering
|
||||
// bindings by priority and insertion order.
|
||||
function mergeSort<T>(
|
||||
list: T[],
|
||||
sortfunc: (a: T, b: T) => boolean | number
|
||||
): T[] {
|
||||
function merge(sortfunc, a, b) {
|
||||
function mergeSort<Item>(
|
||||
list: Item[],
|
||||
sortfunc: (a: Item, b: Item) => boolean | number
|
||||
): Item[] {
|
||||
function merge(a: Item[], b: Item[]) {
|
||||
let ia = 0;
|
||||
let ib = 0;
|
||||
const sorted = [];
|
||||
@@ -214,8 +217,8 @@ function mergeSort<T>(
|
||||
for (let i = 0; i < list.length; i += chunkSize * 2) {
|
||||
const listA = list.slice(i, i + chunkSize);
|
||||
const listB = list.slice(i + chunkSize, i + chunkSize * 2);
|
||||
const merged = merge(sortfunc, listA, listB);
|
||||
const args = [i, merged.length];
|
||||
const merged = merge(listA, listB);
|
||||
const args = [i, merged.length] as [number, number];
|
||||
|
||||
Array.prototype.push.apply(args, merged);
|
||||
Array.prototype.splice.apply(list, args);
|
||||
@@ -226,21 +229,24 @@ function mergeSort<T>(
|
||||
}
|
||||
|
||||
// Escape jQuery selector metacharacters: !"#$%&'()*+,./:;<=>?@[\]^`{|}~
|
||||
const $escape = function (val: string): string {
|
||||
function $escape(val: undefined): undefined;
|
||||
function $escape(val: string): string;
|
||||
function $escape(val: string | undefined): string | undefined {
|
||||
if (typeof val === "undefined") return val;
|
||||
return val.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, "\\$1");
|
||||
};
|
||||
}
|
||||
|
||||
// Maps a function over an object, preserving keys. Like the mapValues
|
||||
// function from lodash.
|
||||
function mapValues<V, R>(
|
||||
obj: { [key: string]: V },
|
||||
f: (value: V, key: string, obj: { [key: string]: V }) => R
|
||||
): { [key: string]: R } {
|
||||
const newObj: { [key: string]: R } = {};
|
||||
function mapValues<T extends { [key: string]: any }, R>(
|
||||
obj: T,
|
||||
f: (value: MapValuesUnion<T>, key: string, object: typeof obj) => R
|
||||
): MapWithResult<T, R> {
|
||||
const newObj = {} as MapWithResult<T, R>;
|
||||
|
||||
for (const key in obj) {
|
||||
if (hasOwnProperty(obj, key)) newObj[key] = f(obj[key], key, obj);
|
||||
}
|
||||
Object.keys(obj).forEach((key: keyof typeof obj) => {
|
||||
newObj[key] = f(obj[key], key as string, obj);
|
||||
});
|
||||
return newObj;
|
||||
}
|
||||
|
||||
@@ -297,26 +303,26 @@ const compareVersion = function (
|
||||
op: "<" | "<=" | "==" | ">" | ">=",
|
||||
b: string
|
||||
): boolean {
|
||||
function versionParts(ver) {
|
||||
function versionParts(ver: string) {
|
||||
return (ver + "")
|
||||
.replace(/-/, ".")
|
||||
.replace(/(\.0)+[^.]*$/, "")
|
||||
.split(".");
|
||||
}
|
||||
|
||||
function cmpVersion(a, b) {
|
||||
a = versionParts(a);
|
||||
b = versionParts(b);
|
||||
const len = Math.min(a.length, b.length);
|
||||
function cmpVersion(a: string, b: string) {
|
||||
const aParts = versionParts(a);
|
||||
const bParts = versionParts(b);
|
||||
const len = Math.min(aParts.length, bParts.length);
|
||||
let cmp;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
cmp = parseInt(a[i], 10) - parseInt(b[i], 10);
|
||||
cmp = parseInt(aParts[i], 10) - parseInt(bParts[i], 10);
|
||||
if (cmp !== 0) {
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
return a.length - b.length;
|
||||
return aParts.length - bParts.length;
|
||||
}
|
||||
|
||||
const diff = cmpVersion(a, b);
|
||||
@@ -401,8 +407,8 @@ export {
|
||||
compareVersion,
|
||||
updateLabel,
|
||||
getComputedLinkColor,
|
||||
makeBlob,
|
||||
hasOwnProperty,
|
||||
hasDefinedProperty,
|
||||
isBS3,
|
||||
toLowerCase,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,42 @@
|
||||
function hasOwnProperty(x: { [key: string]: unknown }, y: string): boolean {
|
||||
return Object.prototype.hasOwnProperty.call(x, y);
|
||||
import type { NotUndefined } from "./extraTypes";
|
||||
|
||||
// Inspriation from https://fettblog.eu/typescript-hasownproperty/
|
||||
// But mixing with "NonNullable key of Obj" instead of "key to unknown values"
|
||||
function hasOwnProperty<Prop extends keyof X, X extends { [key: string]: any }>(
|
||||
obj: X,
|
||||
prop: Prop
|
||||
): obj is X & { [key in NonNullable<Prop>]: X[key] } {
|
||||
return Object.prototype.hasOwnProperty.call(obj, prop);
|
||||
}
|
||||
|
||||
export { hasOwnProperty };
|
||||
// Return true if the key exists on the object and the value is not undefined.
|
||||
//
|
||||
// This method is mainly used in input bindings' `receiveMessage` method.
|
||||
// Since we know that the values are sent by Shiny via `{jsonlite}`,
|
||||
// then we know that there are no `undefined` values. `null` is possible, but not `undefined`.
|
||||
function hasDefinedProperty<
|
||||
Prop extends keyof X,
|
||||
X extends { [key: string]: any }
|
||||
>(
|
||||
obj: X,
|
||||
prop: Prop
|
||||
): obj is X & { [key in NonNullable<Prop>]: NotUndefined<X[key]> } {
|
||||
return (
|
||||
Object.prototype.hasOwnProperty.call(obj, prop) && obj[prop] !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
// Return type for non-null value
|
||||
function ifUndefined<X extends NotUndefined<any>, Y>(
|
||||
value: X,
|
||||
alternate: Y
|
||||
): NotUndefined<X>;
|
||||
// Return type for null value
|
||||
function ifUndefined<X extends undefined, Y>(value: X, alternate: Y): Y;
|
||||
// Logic
|
||||
function ifUndefined<X, Y>(value: X, alternate: Y): X | Y {
|
||||
if (value === undefined) return alternate;
|
||||
return value;
|
||||
}
|
||||
|
||||
export { hasOwnProperty, hasDefinedProperty, ifUndefined };
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { BlobBuilderConstructor } from "../utils/blob";
|
||||
|
||||
function windowBlobBuilder(): BlobBuilderConstructor {
|
||||
const blob =
|
||||
// @ts-expect-error; Using legacy definitions of Blob builders
|
||||
window.BlobBuilder ||
|
||||
// @ts-expect-error; Using legacy definitions of Blob builders
|
||||
window.WebKitBlobBuilder ||
|
||||
// @ts-expect-error; Using legacy definitions of Blob builders
|
||||
window.MozBlobBuilder ||
|
||||
window.MSBlobBuilder;
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
export { windowBlobBuilder };
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { Shiny } from "../shiny";
|
||||
|
||||
function windowShiny(): Shiny | null {
|
||||
// Use `unknown` type as we know what we are doing is _dangerous_
|
||||
function windowShiny(): Shiny {
|
||||
// Use `any` type as we know what we are doing is _dangerous_
|
||||
// Immediately init shiny on the window
|
||||
if (!(window as unknown)["Shiny"]) {
|
||||
(window as unknown)["Shiny"] = {};
|
||||
if (!(window as any)["Shiny"]) {
|
||||
(window as any)["Shiny"] = {};
|
||||
}
|
||||
return (window as unknown)["Shiny"];
|
||||
return (window as any)["Shiny"];
|
||||
}
|
||||
|
||||
export { windowShiny };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { InputBinding } from "./inputBinding";
|
||||
declare type ActionButtonReceiveMessageData = {
|
||||
label?: string;
|
||||
icon?: string;
|
||||
icon?: string | [];
|
||||
};
|
||||
declare class ActionButtonInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement>;
|
||||
|
||||
@@ -14,7 +14,7 @@ declare type CheckboxGroupValue = CheckboxGroupHTMLElement["value"];
|
||||
declare class CheckboxGroupInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement>;
|
||||
getValue(el: CheckboxGroupHTMLElement): CheckboxGroupValue[];
|
||||
setValue(el: HTMLElement, value: string[] | string): void;
|
||||
setValue(el: HTMLElement, value: string[] | string | null): void;
|
||||
getState(el: CheckboxGroupHTMLElement): {
|
||||
label: string;
|
||||
value: ReturnType<CheckboxGroupInputBinding["getValue"]>;
|
||||
|
||||
6
srcts/types/src/bindings/input/date.d.ts
vendored
@@ -31,8 +31,8 @@ declare class DateInputBindingBase extends InputBinding {
|
||||
parts: string[];
|
||||
separators: string[];
|
||||
}): string;
|
||||
protected _setMin(el: HTMLElement, date: Date | null | undefined): void;
|
||||
protected _setMax(el: HTMLElement, date: Date): void;
|
||||
protected _setMin(el: HTMLElement, date: Date | null): void;
|
||||
protected _setMax(el: HTMLElement, date: Date | null): void;
|
||||
protected _newDate(date: Date | never | string): Date | null;
|
||||
protected _floorDateTime(date: Date): Date;
|
||||
protected _dateAsUTC(date: Date): Date;
|
||||
@@ -40,7 +40,7 @@ declare class DateInputBindingBase extends InputBinding {
|
||||
}
|
||||
declare class DateInputBinding extends DateInputBindingBase {
|
||||
getValue(el: HTMLElement): string;
|
||||
setValue(el: HTMLElement, value: Date): void;
|
||||
setValue(el: HTMLElement, value: Date | null): void;
|
||||
getState(el: HTMLElement): {
|
||||
label: string;
|
||||
value: string | null;
|
||||
|
||||
@@ -4,7 +4,7 @@ declare class InputBinding {
|
||||
name: string;
|
||||
find(scope: BindScope): JQuery<HTMLElement>;
|
||||
getId(el: HTMLElement): string;
|
||||
getType(el: HTMLElement): string | false;
|
||||
getType(el: HTMLElement): string | null;
|
||||
getValue(el: HTMLElement): any;
|
||||
subscribe(el: HTMLElement, callback: (value: boolean) => void): void;
|
||||
unsubscribe(el: HTMLElement): void;
|
||||
|
||||
2
srcts/types/src/bindings/input/number.d.ts
vendored
@@ -9,7 +9,7 @@ declare type NumberReceiveMessageData = {
|
||||
};
|
||||
declare class NumberInputBinding extends TextInputBindingBase {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement>;
|
||||
getValue(el: NumberHTMLElement): string[] | number | string;
|
||||
getValue(el: NumberHTMLElement): string[] | number | string | null | undefined;
|
||||
setValue(el: NumberHTMLElement, value: number): void;
|
||||
getType(el: NumberHTMLElement): string;
|
||||
receiveMessage(el: NumberHTMLElement, data: NumberReceiveMessageData): void;
|
||||
|
||||
8
srcts/types/src/bindings/input/radio.d.ts
vendored
@@ -5,17 +5,17 @@ declare type ValueLabelObject = {
|
||||
label: string;
|
||||
};
|
||||
declare type RadioReceiveMessageData = {
|
||||
value?: string;
|
||||
value?: string | [];
|
||||
options?: ValueLabelObject[];
|
||||
label: string;
|
||||
};
|
||||
declare class RadioInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement>;
|
||||
getValue(el: RadioHTMLElement): string[] | number | string | null;
|
||||
setValue(el: RadioHTMLElement, value: string): void;
|
||||
getValue(el: RadioHTMLElement): string[] | number | string | null | undefined;
|
||||
setValue(el: RadioHTMLElement, value: string | []): void;
|
||||
getState(el: RadioHTMLElement): {
|
||||
label: string;
|
||||
value: string[] | number | string;
|
||||
value: ReturnType<RadioInputBinding["getValue"]>;
|
||||
options: ValueLabelObject[];
|
||||
};
|
||||
receiveMessage(el: RadioHTMLElement, data: RadioReceiveMessageData): void;
|
||||
|
||||
10
srcts/types/src/bindings/input/selectInput.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
/// <reference types="selectize" />
|
||||
import { InputBinding } from "./inputBinding";
|
||||
import type { NotUndefined } from "../../utils/extraTypes";
|
||||
declare type SelectHTMLElement = HTMLSelectElement & {
|
||||
nonempty: boolean;
|
||||
};
|
||||
@@ -10,18 +11,19 @@ declare type SelectInputReceiveMessageData = {
|
||||
url?: string;
|
||||
value?: string;
|
||||
};
|
||||
declare type SelectizeOptions = Selectize.IOptions<string, unknown>;
|
||||
declare type SelectizeInfo = Selectize.IApi<string, unknown> & {
|
||||
settings: Selectize.IOptions<string, unknown>;
|
||||
settings: SelectizeOptions;
|
||||
};
|
||||
declare class SelectInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement>;
|
||||
getType(el: HTMLElement): string;
|
||||
getType(el: HTMLElement): string | null;
|
||||
getId(el: SelectHTMLElement): string;
|
||||
getValue(el: HTMLElement): string[] | number | string;
|
||||
getValue(el: HTMLElement): NotUndefined<ReturnType<JQuery<HTMLElement>["val"]>>;
|
||||
setValue(el: SelectHTMLElement, value: string): void;
|
||||
getState(el: SelectHTMLElement): {
|
||||
label: JQuery<HTMLElement>;
|
||||
value: string[] | number | string;
|
||||
value: ReturnType<SelectInputBinding["getValue"]>;
|
||||
options: Array<{
|
||||
value: string;
|
||||
label: string;
|
||||
|
||||
5
srcts/types/src/bindings/input/slider.d.ts
vendored
@@ -7,6 +7,9 @@ declare type SliderReceiveMessageData = {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
"data-type"?: string;
|
||||
"time-format"?: string;
|
||||
timezone?: string;
|
||||
};
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -18,7 +21,7 @@ declare global {
|
||||
}
|
||||
declare class SliderInputBinding extends TextInputBindingBase {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement>;
|
||||
getType(el: HTMLElement): string | false;
|
||||
getType(el: HTMLElement): string | null;
|
||||
getValue(el: TextHTMLElement): number | string | [number | string, number | string];
|
||||
setValue(el: HTMLElement, value: number | string | [number | string, number | string]): void;
|
||||
subscribe(el: HTMLElement, callback: (x: boolean) => void): void;
|
||||
|
||||
2
srcts/types/src/bindings/input/tabinput.d.ts
vendored
@@ -5,7 +5,7 @@ declare type TabInputReceiveMessageData = {
|
||||
declare class BootstrapTabInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement>;
|
||||
getValue(el: HTMLElement): string | null;
|
||||
setValue(el: HTMLElement, value: string): void;
|
||||
setValue(el: HTMLElement, value: string | undefined): void;
|
||||
getState(el: HTMLElement): {
|
||||
value: string | null;
|
||||
};
|
||||
|
||||
@@ -10,9 +10,10 @@ declare class DatatableOutputBinding extends OutputBinding {
|
||||
search?: {
|
||||
caseInsensitive?: boolean;
|
||||
};
|
||||
escape?: string;
|
||||
} | null;
|
||||
action?: string;
|
||||
escape?: string;
|
||||
action?: string;
|
||||
evalOptions?: string[];
|
||||
callback?: string;
|
||||
searchDelay?: number;
|
||||
|
||||
20
srcts/types/src/events/jQueryEvents.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { JQueryEventHandlerBase } from "bootstrap";
|
||||
import "jquery";
|
||||
declare type EvtPrefix<T extends string> = `${T}.${string}`;
|
||||
declare type EvtFn<T extends JQuery.Event> = ((evt: T) => void) | null | undefined;
|
||||
declare global {
|
||||
interface JQuery {
|
||||
on(events: EvtPrefix<"change">, handler: EvtFn<JQuery.DragEvent>): this;
|
||||
on(events: EvtPrefix<"mousdown">, handler: EvtFn<JQuery.MouseDownEvent>): this;
|
||||
on(events: EvtPrefix<"dblclick">, handler: EvtFn<JQuery.DoubleClickEvent>): this;
|
||||
on(events: EvtPrefix<"dblclick2">, handler: EvtFn<JQuery.MouseDownEvent>): this;
|
||||
on(events: EvtPrefix<"mousemove">, handler: EvtFn<JQuery.MouseMoveEvent>): this;
|
||||
on(events: EvtPrefix<"mouseout">, handler: EvtFn<JQuery.MouseOutEvent>): this;
|
||||
on(events: EvtPrefix<"mousedown">, handler: EvtFn<JQuery.MouseDownEvent>): this;
|
||||
on(events: EvtPrefix<"mousedown2">, handler: EvtFn<JQuery.MouseDownEvent>): this;
|
||||
on(events: EvtPrefix<"mouseup">, handler: EvtFn<JQuery.MouseUpEvent>): this;
|
||||
on(events: EvtPrefix<"resize">, handler: EvtFn<JQuery.ResizeEvent>): this;
|
||||
on(events: `shown.bs.${string}.sendImageSize`, selector: string, handler: (this: HTMLElement, e: JQueryEventHandlerBase<HTMLElement, any>) => void): this;
|
||||
}
|
||||
}
|
||||
export {};
|
||||
6
srcts/types/src/file/fileProcessor.d.ts
vendored
@@ -7,12 +7,12 @@ declare type UploadInitValue = {
|
||||
};
|
||||
declare type UploadEndValue = never;
|
||||
declare class FileProcessor {
|
||||
files: FileList;
|
||||
files: File[];
|
||||
fileIndex: number;
|
||||
aborted: boolean;
|
||||
completed: boolean;
|
||||
constructor(files: FileList, exec$run?: boolean);
|
||||
onBegin(files: FileList, cont: () => void): void;
|
||||
onBegin(files: File[], cont: () => void): void;
|
||||
onFile(file: File, cont: () => void): void;
|
||||
onComplete(): void;
|
||||
onAbort(): void;
|
||||
@@ -35,7 +35,7 @@ declare class FileUploader extends FileProcessor {
|
||||
type: string;
|
||||
}>>, onSuccess: (value: UploadInitValue) => void, onFailure: Parameters<ShinyApp["makeRequest"]>[3], blobs: Parameters<ShinyApp["makeRequest"]>[4]): void;
|
||||
makeRequest(method: "uploadEnd", args: [string, string], onSuccess: (value: unknown) => void, onFailure: Parameters<ShinyApp["makeRequest"]>[3], blobs: Parameters<ShinyApp["makeRequest"]>[4]): void;
|
||||
onBegin(files: FileList, cont: () => void): void;
|
||||
onBegin(files: File[], cont: () => void): void;
|
||||
onFile(file: File, cont: () => void): void;
|
||||
onComplete(): void;
|
||||
onError(message: string): void;
|
||||
|
||||
24
srcts/types/src/imageutils/createBrush.d.ts
vendored
@@ -10,21 +10,21 @@ declare type Bounds = {
|
||||
declare type BoundsCss = Bounds;
|
||||
declare type BoundsData = Bounds;
|
||||
declare type ImageState = {
|
||||
brushing?: boolean;
|
||||
dragging?: boolean;
|
||||
resizing?: boolean;
|
||||
down?: Offset;
|
||||
up?: Offset;
|
||||
resizeSides?: {
|
||||
brushing: boolean;
|
||||
dragging: boolean;
|
||||
resizing: boolean;
|
||||
down: Offset;
|
||||
up: Offset;
|
||||
resizeSides: {
|
||||
left: boolean;
|
||||
right: boolean;
|
||||
top: boolean;
|
||||
bottom: boolean;
|
||||
};
|
||||
boundsCss?: BoundsCss;
|
||||
boundsData?: BoundsData;
|
||||
panel?: Panel;
|
||||
changeStartBounds?: Bounds;
|
||||
boundsCss: BoundsCss;
|
||||
boundsData: BoundsData;
|
||||
panel: Panel | null;
|
||||
changeStartBounds: Bounds;
|
||||
};
|
||||
declare type BrushOpts = {
|
||||
brushDirection: "x" | "xy" | "y";
|
||||
@@ -54,11 +54,11 @@ declare type Brush = {
|
||||
getPanel: () => ImageState["panel"];
|
||||
down: {
|
||||
(): ImageState["down"];
|
||||
(offsetCss: any): void;
|
||||
(offsetCss: Offset): void;
|
||||
};
|
||||
up: {
|
||||
(): ImageState["up"];
|
||||
(offsetCss: any): void;
|
||||
(offsetCss: Offset): void;
|
||||
};
|
||||
isBrushing: () => ImageState["brushing"];
|
||||
startBrushing: () => void;
|
||||
|
||||
@@ -8,7 +8,7 @@ declare type CreateHandler = {
|
||||
mouseout?: (e: JQuery.MouseOutEvent) => void;
|
||||
mousedown?: (e: JQuery.MouseDownEvent) => void;
|
||||
onResetImg: () => void;
|
||||
onResize?: () => void;
|
||||
onResize: ((e: JQuery.ResizeEvent) => void) | null;
|
||||
};
|
||||
declare type BrushInfo = {
|
||||
xmin: number;
|
||||
|
||||