Skip to content
29 changes: 29 additions & 0 deletions inst/www/shared/shiny.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions inst/www/shared/shiny.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion inst/www/shared/shiny.min.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions inst/www/shared/shiny.min.js.map

Large diffs are not rendered by default.

66 changes: 57 additions & 9 deletions srcts/src/bindings/input/selectInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,23 @@ type SelectInputReceiveMessageData = {
value?: string;
};

type SelectizeOptions = Selectize.IOptions<string, unknown>;
type SelectizeInfo = Selectize.IApi<string, unknown> & {
settings: SelectizeOptions;
settings: Selectize.IOptions<string, unknown>;
};

type SelectizeOptions = Selectize.IOptions<string, unknown> & {
// Provide some stronger typing for the Selectize options
labelField: "label";
valueField: "value";
searchField: ["label"];
onItemRemove?: (value: string) => void;
onDropdownClose?: () => void;
};

// Adds a py-shiny specific "option" that makes the
// input_selectize(remove_button) parameter possible
type SelectizeShinyOptions = SelectizeOptions & {
shinyRemoveButton?: "none" | "true" | "false" | "both";
};

function getLabelNode(el: SelectHTMLElement): JQuery<HTMLElement> {
Expand Down Expand Up @@ -244,13 +258,7 @@ class SelectInputBinding extends InputBinding {

if (config.length === 0) return undefined;

let options: SelectizeOptions & {
labelField: "label";
valueField: "value";
searchField: ["label"];
onItemRemove?: (value: string) => void;
onDropdownClose?: () => void;
} = $.extend(
let options: SelectizeShinyOptions = $.extend(
{
labelField: "label",
valueField: "value",
Expand All @@ -259,6 +267,8 @@ class SelectInputBinding extends InputBinding {
JSON.parse(config.html()),
);

options = this._addShinyRemoveButton(options, el.hasAttribute("multiple"));

// selectize created from selectInput()
if (typeof config.data("nonempty") !== "undefined") {
el.nonempty = true;
Expand Down Expand Up @@ -305,6 +315,44 @@ class SelectInputBinding extends InputBinding {

return control;
}

// Translate shinyRemoveButton option into selectize plugins
private _addShinyRemoveButton(
options: SelectizeShinyOptions,
multiple: boolean,
): SelectizeOptions {
let removeButton = options.shinyRemoveButton;
if (removeButton === undefined) {
return options;
}

// None really means 'smart default'
if (removeButton === "none") {
removeButton = multiple ? "true" : "false";
}

if (removeButton === "false") {
return options;
}

const plugins = [];
if (removeButton === "both") {
plugins.push("remove_button", "clear_button");
} else if (removeButton === "true") {
plugins.push(multiple ? "remove_button" : "clear_button");
}

// Add plugins to existing plugins if not already present
return {
...options,
plugins: Array.from(
new Set([
...(Array.isArray(options.plugins) ? options.plugins : []),
...plugins,
]),
),
};
}
}

export { SelectInputBinding };
Expand Down
4 changes: 2 additions & 2 deletions srcts/types/src/bindings/input/selectInput.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ type SelectInputReceiveMessageData = {
url?: string;
value?: string;
};
type SelectizeOptions = Selectize.IOptions<string, unknown>;
type SelectizeInfo = Selectize.IApi<string, unknown> & {
settings: SelectizeOptions;
settings: Selectize.IOptions<string, unknown>;
};
declare class SelectInputBinding extends InputBinding {
find(scope: HTMLElement): JQuery<HTMLElement>;
Expand All @@ -32,6 +31,7 @@ declare class SelectInputBinding extends InputBinding {
unsubscribe(el: HTMLElement): void;
initialize(el: SelectHTMLElement): void;
protected _selectize(el: SelectHTMLElement, update?: boolean): SelectizeInfo | undefined;
private _addShinyRemoveButton;
}
export { SelectInputBinding };
export type { SelectInputReceiveMessageData };