chore: import upstream snapshot with attribution
CI / compile_and_lint (push) Failing after 0s
CI / docker_build (linux/amd64, -linux-amd64-duckdb, duckdb) (push) Failing after 0s
CI / docker_build (linux/arm64, -linux-arm64, minimal) (push) Failing after 2s
CI / docker_build (linux/arm64, -linux-arm64-duckdb, duckdb) (push) Failing after 1s
CI / docker_build (linux/amd64, minimal) (push) Failing after 1s
CI / test (, sqlite, sqlite::memory:) (push) Has been skipped
CI / test (mssql, mssql, mssql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (mysql, mysql, mysql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (oracle, oracle, Driver=Oracle 21 ODBC driver;Dbq=//127.0.0.1:1521/FREEPDB1;Uid=root;Pwd=Password123!) (push) Has been skipped
CI / test (postgres, odbc, Driver=PostgreSQL Unicode;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!, true) (push) Has been skipped
CI / test (postgres, postgres, postgres://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / playwright (push) Has been skipped
CI / docker_build (linux/arm/v7, -linux-arm-v7, minimal) (push) Failing after 0s
CI / hurl_examples (push) Failing after 8s
deploy website / deploy_official_site (push) Failing after 1s
CI / hurl (${{ matrix.example }}) (push) Has been skipped
CI / docker_push (duckdb) (push) Has been cancelled
CI / docker_push (minimal) (push) Has been cancelled
CI / windows_test (push) Has been cancelled
CI / compile_and_lint (push) Failing after 0s
CI / docker_build (linux/amd64, -linux-amd64-duckdb, duckdb) (push) Failing after 0s
CI / docker_build (linux/arm64, -linux-arm64, minimal) (push) Failing after 2s
CI / docker_build (linux/arm64, -linux-arm64-duckdb, duckdb) (push) Failing after 1s
CI / docker_build (linux/amd64, minimal) (push) Failing after 1s
CI / test (, sqlite, sqlite::memory:) (push) Has been skipped
CI / test (mssql, mssql, mssql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (mysql, mysql, mysql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (oracle, oracle, Driver=Oracle 21 ODBC driver;Dbq=//127.0.0.1:1521/FREEPDB1;Uid=root;Pwd=Password123!) (push) Has been skipped
CI / test (postgres, odbc, Driver=PostgreSQL Unicode;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!, true) (push) Has been skipped
CI / test (postgres, postgres, postgres://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / playwright (push) Has been skipped
CI / docker_build (linux/arm/v7, -linux-arm-v7, minimal) (push) Failing after 0s
CI / hurl_examples (push) Failing after 8s
deploy website / deploy_official_site (push) Failing after 1s
CI / hurl (${{ matrix.example }}) (push) Has been skipped
CI / docker_push (duckdb) (push) Has been cancelled
CI / docker_push (minimal) (push) Has been cancelled
CI / windows_test (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
create table users (
|
||||
id int primary key auto_increment,
|
||||
name varchar(255) not null,
|
||||
email varchar(255) not null
|
||||
);
|
||||
|
||||
create table `groups` (
|
||||
id int primary key auto_increment,
|
||||
name varchar(255) not null
|
||||
);
|
||||
|
||||
create table group_members (
|
||||
group_id int not null,
|
||||
user_id int not null,
|
||||
primary key (group_id, user_id),
|
||||
foreign key (group_id) references `groups` (id),
|
||||
foreign key (user_id) references users (id)
|
||||
);
|
||||
|
||||
INSERT INTO users (id, name, email) VALUES
|
||||
(1, 'John Smith', 'john@email.com'),
|
||||
(2, 'Jane Doe', 'jane@email.com'),
|
||||
(3, 'Bob Wilson', 'bob@email.com'),
|
||||
(4, 'Mary Johnson', 'mary@email.com'),
|
||||
(5, 'James Brown', 'james@email.com'),
|
||||
(6, 'Sarah Davis', 'sarah@email.com'),
|
||||
(7, 'Michael Lee', 'michael@email.com'),
|
||||
(8, 'Lisa Anderson', 'lisa@email.com'),
|
||||
(9, 'David Miller', 'david@email.com'),
|
||||
(10, 'Emma Wilson', 'emma@email.com');
|
||||
|
||||
INSERT INTO `groups` (id, name) VALUES
|
||||
(1, 'Team Alpha');
|
||||
|
||||
INSERT INTO group_members (group_id, user_id) VALUES
|
||||
(1, 1),
|
||||
(1, 2),
|
||||
(1, 3),
|
||||
(1, 4),
|
||||
(1, 5);
|
||||
@@ -0,0 +1,126 @@
|
||||
{{!-- This is a form that will post data to the URL specified in the top-level 'action' property coming from the SQL query --}}
|
||||
<form method="POST" action="{{action}}">
|
||||
{{!-- Create a row with centered content and spacing between items --}}
|
||||
<div class="row justify-content-center align-items-center g-4">
|
||||
{{!-- Left List Box: 5 columns wide (out of the 12 made available by bootstrap) --}}
|
||||
<div class="col-5">
|
||||
{{!-- Card with no border and subtle shadow --}}
|
||||
<div class="card border-0 shadow-sm">
|
||||
{{!-- Card header with white background, no border, semibold font, and secondary text color --}}
|
||||
<div class="card-header bg-white border-bottom fw-semibold text-secondary py-3">
|
||||
Available Items
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
{{!-- Multiple-select dropdown list, 300px tall --}}
|
||||
<select class="form-select" id="leftList" multiple style="height: 300px">
|
||||
{{!-- Loop through each row of data returned by the second SQL query (row-level properties are available as variables) --}}
|
||||
{{#each_row}}
|
||||
{{!-- Create an option for each item, marking it selected if the 'selected' property is true --}}
|
||||
<option class="py-2 px-3 rounded-1 my-1" value="{{id}}" {{#if selected}}selected{{/if}}>{{label}}</option>
|
||||
{{/each_row}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Middle section with transfer buttons (auto-sized column) --}}
|
||||
<div class="col-auto d-flex flex-column gap-2">
|
||||
{{!-- Right arrow button (→) to move items to selected list --}}
|
||||
<button type="button" class="btn btn-outline-primary rounded-circle p-0 d-flex align-items-center justify-content-center"
|
||||
id="moveRight"
|
||||
title="Move to selected"
|
||||
style="width: 40px; height: 40px">
|
||||
{{!-- icon_img is a helper that renders an icon image from tabler.io/icons --}}
|
||||
{{~icon_img 'arrow-narrow-right' 20~}}
|
||||
</button>
|
||||
{{!-- Left arrow button (←) to remove items from selected list --}}
|
||||
<button type="button" class="btn btn-outline-primary rounded-circle p-0 d-flex align-items-center justify-content-center"
|
||||
id="moveLeft"
|
||||
title="Remove from selected"
|
||||
style="width: 40px; height: 40px">
|
||||
{{~icon_img 'arrow-narrow-left' 20~}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{!-- Right List Box (5 columns wide) --}}
|
||||
<div class="col-5">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white border-bottom fw-semibold text-secondary py-3">
|
||||
Selected Items
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
{{!-- Multiple-select dropdown that will contain selected items. The name attribute makes it submit as an array --}}
|
||||
<select class="form-select" id="rightList" name="selected_items[]" multiple style="height: 300px"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Submit Button Section (full width) --}}
|
||||
<div class="col-12 text-center mt-4">
|
||||
<input type="submit" class="btn btn-primary px-4 py-2 fw-semibold shadow-sm"
|
||||
id="submitBtn"
|
||||
disabled
|
||||
value="Submit Selection">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{!-- JavaScript code with CSP (Content Security Policy) nonce for security --}}
|
||||
<script nonce="{{@csp_nonce}}">
|
||||
// Get references to both list elements
|
||||
// If we wanted to be able to include this component multiple times on the same page,
|
||||
// we would need to generate IDs like "rightList1", "leftList1", etc. using a template string.like "rightList{{@component_index}}"
|
||||
const rightList = document.getElementById('rightList');
|
||||
const leftList = document.getElementById('leftList');
|
||||
|
||||
/**
|
||||
* Moves selected items from one list to another while maintaining alphabetical order
|
||||
* @param {HTMLSelectElement} fromList - The list to take items from
|
||||
* @param {HTMLSelectElement} toList - The list to add items to
|
||||
*/
|
||||
function transferItems(fromList, toList) {
|
||||
// Deselect all items in the destination list
|
||||
for (const option of toList.options) option.selected = false;
|
||||
|
||||
// Combine existing options with newly selected ones
|
||||
const newOptions = [...toList.options, ...fromList.selectedOptions];
|
||||
|
||||
// Sort the combined options alphabetically
|
||||
newOptions.sort((a, b) => a.text.localeCompare(b.text));
|
||||
|
||||
// Add all options to the destination list
|
||||
// Since an element can only exist once in the page,
|
||||
// this will automatically remove the options from the source list
|
||||
toList.append(...newOptions);
|
||||
|
||||
// Focus the destination list and update submit button state
|
||||
toList.focus();
|
||||
updateSubmitButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable submit button based on whether there are items in the right list
|
||||
*/
|
||||
function updateSubmitButton() {
|
||||
submitBtn.disabled = rightList.options.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure all items in right list are selected before form submission
|
||||
* This is necessary because unselected options aren't included in form data
|
||||
*/
|
||||
function handleSubmit() {
|
||||
for (const option of rightList.options) option.selected = true;
|
||||
}
|
||||
|
||||
// Set initial state of submit button
|
||||
updateSubmitButton();
|
||||
|
||||
// Move any pre-selected items to the right list when page loads
|
||||
transferItems(leftList, rightList);
|
||||
|
||||
// Set up click handlers for the transfer buttons and form submission
|
||||
document.getElementById('moveRight').addEventListener('click', transferItems.bind(null, leftList, rightList));
|
||||
document.getElementById('moveLeft').addEventListener('click', transferItems.bind(null, rightList, leftList));
|
||||
document.querySelector('form').addEventListener('submit', handleSubmit);
|
||||
</script>
|
||||
Reference in New Issue
Block a user