diff --git a/docs/THIRDPARTY b/docs/THIRDPARTY index af54f3df15..f3189c64fd 100644 --- a/docs/THIRDPARTY +++ b/docs/THIRDPARTY @@ -283,6 +283,11 @@ Source: https://lucide.dev/icons/plus Copyright: 2013-2022 Cole Bemis License: ISC License +Files: web/shared/icons/expand-both-diagonals.svg +Source: https://lucide.dev/icons/expand +Copyright: 2013-2022 Cole Bemis +License: ISC License + Files: web/third/bootstrap/css/bootstrap.app.css Copyright: 2012 Twitter, Inc. License: Apache-2.0 diff --git a/web/shared/icons/expand-both-diagonals.svg b/web/shared/icons/expand-both-diagonals.svg new file mode 100644 index 0000000000..7829aa73f5 --- /dev/null +++ b/web/shared/icons/expand-both-diagonals.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/web/src/add_group_members_pill.ts b/web/src/add_group_members_pill.ts index 06a2440a6a..c29c6a45b6 100644 --- a/web/src/add_group_members_pill.ts +++ b/web/src/add_group_members_pill.ts @@ -3,6 +3,7 @@ import assert from "minimalistic-assert"; import * as add_subscribers_pill from "./add_subscribers_pill.ts"; import * as input_pill from "./input_pill.ts"; import * as keydown_util from "./keydown_util.ts"; +import * as people from "./people.ts"; import type {User} from "./people.ts"; import * as stream_pill from "./stream_pill.ts"; import type {CombinedPill, CombinedPillContainer} from "./typeahead_helper.ts"; @@ -24,6 +25,30 @@ function get_pill_group_ids(pill_widget: CombinedPillContainer): number[] { return group_user_ids; } +function expand_group_pill($pill_elem: JQuery, pill_widget: CombinedPillContainer): void { + const group_id = Number.parseInt($pill_elem.attr("data-user-group-id")!, 10); + const group = user_groups.get_user_group_from_id(group_id); + const direct_subgroup_ids = group.direct_subgroup_ids; + const direct_member_ids = group.members; + + const taken_group_ids = user_group_pill.get_group_ids(pill_widget); + const taken_user_ids = user_pill.get_user_ids(pill_widget); + + for (const member_id of direct_member_ids) { + if (!taken_user_ids.includes(member_id)) { + const user = people.get_by_user_id(member_id); + user_pill.append_user(user, pill_widget, false); + } + } + + for (const group_id of direct_subgroup_ids) { + if (!taken_group_ids.includes(group_id)) { + const subgroup = user_groups.get_user_group_from_id(group_id); + user_group_pill.append_user_group(subgroup, pill_widget, false, true); + } + } +} + export function create_item_from_text( text: string, current_items: CombinedPill[], @@ -42,6 +67,8 @@ export function create_item_from_text( const group_item = user_group_pill.create_item_from_group_name(text, current_items); if (group_item) { const subgroup = user_groups.get_user_group_from_id(group_item.group_id); + group_item.show_expand_button = + subgroup.members.size > 0 || subgroup.direct_subgroup_ids.size > 0; const current_group_id = user_group_components.active_group_id; assert(current_group_id !== undefined); const current_group = user_groups.get_user_group_from_id(current_group_id); @@ -88,6 +115,10 @@ export function create({ return user_group_pill.filter_taken_groups(potential_groups, pill_widget); } + pill_widget.onPillExpand((pill) => { + expand_group_pill(pill, pill_widget); + }); + add_subscribers_pill.set_up_pill_typeahead({ pill_widget, $pill_container, @@ -136,6 +167,10 @@ export function create_without_add_button({ onPillRemoveAction(get_pill_user_ids(pill_widget), get_pill_group_ids(pill_widget)); }); + pill_widget.onPillExpand((pill) => { + expand_group_pill(pill, pill_widget); + }); + add_subscribers_pill.set_up_pill_typeahead({ pill_widget, $pill_container, diff --git a/web/src/input_pill.ts b/web/src/input_pill.ts index a45d3691e4..b16fef69d9 100644 --- a/web/src/input_pill.ts +++ b/web/src/input_pill.ts @@ -58,6 +58,7 @@ type InputPillStore = { on_pill_exit: InputPillCreateOptions["on_pill_exit"]; onPillCreate?: () => void; onPillRemove?: (pill: InputPill, trigger: RemovePillTrigger) => void; + onPillExpand?: (pill: JQuery) => void; createPillonPaste?: () => void; split_text_on_comma: boolean; convert_to_pill_on_enter: boolean; @@ -78,6 +79,7 @@ export type InputPillContainer = { onPillRemove: ( callback: (pill: InputPill, trigger: RemovePillTrigger) => void, ) => void; + onPillExpand: (callback: (pill: JQuery) => void) => void; onTextInputHook: (callback: () => void) => void; createPillonPaste: (callback: () => void) => void; clear: (quiet?: boolean) => void; @@ -469,6 +471,15 @@ export function create( store.$input.trigger("focus"); }); + store.$parent.on("click", ".expand", function (this: HTMLElement, e) { + assert(store.onPillExpand !== undefined); + e.stopPropagation(); + store.onPillExpand($(this).closest(".pill")); + const pill = util.the($(this).closest(".pill")); + funcs.removePill(pill, "close"); + store.$input.trigger("focus"); + }); + store.$parent.on("click", function (e) { if ($(e.target).is(".pill-container")) { $(this).find(".input").trigger("focus"); @@ -501,6 +512,10 @@ export function create( store.onPillRemove = callback; }, + onPillExpand(callback) { + store.onPillExpand = callback; + }, + onTextInputHook(callback) { store.onTextInputHook = callback; }, diff --git a/web/src/pill_typeahead.ts b/web/src/pill_typeahead.ts index 7297f39735..5e79a37f16 100644 --- a/web/src/pill_typeahead.ts +++ b/web/src/pill_typeahead.ts @@ -394,7 +394,10 @@ export function set_up_combined( if (include_streams(query) && item.type === "stream") { stream_pill.append_stream(item, pills); } else if (include_user_groups && item.type === "user_group") { - user_group_pill.append_user_group(item, pills); + const show_expand_button = + !opts.for_stream_subscribers && + (item.members.size > 0 || item.direct_subgroup_ids.size > 0); + user_group_pill.append_user_group(item, pills, true, show_expand_button); } else if ( include_users && item.type === "user" && diff --git a/web/src/user_group_pill.ts b/web/src/user_group_pill.ts index ad926d2039..b660a2d53a 100644 --- a/web/src/user_group_pill.ts +++ b/web/src/user_group_pill.ts @@ -17,6 +17,7 @@ export type UserGroupPill = { type: "user_group"; group_id: number; group_name: string; + show_expand_button?: boolean; }; export type UserGroupPillWidget = InputPillContainer; @@ -34,6 +35,7 @@ export function generate_pill_html(item: UserGroupPill): string { group_id: item.group_id, show_group_members_count: true, group_members_count: group_members.length, + show_expand_button: item.show_expand_button ?? false, }); } @@ -87,12 +89,14 @@ export function append_user_group( group: UserGroup, pill_widget: CombinedPillContainer | GroupSettingPillContainer | UserGroupPillWidget, execute_oncreate_callback = true, + show_expand_button = false, ): void { pill_widget.appendValidatedData( { type: "user_group", group_id: group.id, group_name: group.name, + show_expand_button, }, false, !execute_oncreate_callback, diff --git a/web/styles/input_pill.css b/web/styles/input_pill.css index 7a42d5b568..8c5ed4c42a 100644 --- a/web/styles/input_pill.css +++ b/web/styles/input_pill.css @@ -70,7 +70,8 @@ line-height: 1.5; } - .pill-close-button { + .pill-close-button, + .pill-expand-button { font-size: 0.7142em; /* 10px at 14px em */ text-decoration: none; /* Let the close button's box stretch, @@ -92,20 +93,22 @@ opacity: 0.7; } - .exit { + .exit, + .expand { width: var(--length-input-pill-exit); height: var(--length-input-pill-exit); display: flex; justify-content: center; margin-right: 2px; border-radius: 2px; - } - .exit:hover { - background: var(--color-background-input-pill-exit-hover); + &:hover { + background: var(--color-background-input-pill-exit-hover); - .pill-close-button { - opacity: 1; + .pill-close-button, + .pill-expand-button { + opacity: 1; + } } } diff --git a/web/templates/input_pill.hbs b/web/templates/input_pill.hbs index 2652fdb60d..88ae25550c 100644 --- a/web/templates/input_pill.hbs +++ b/web/templates/input_pill.hbs @@ -33,6 +33,11 @@  ({{group_members_count}}) {{~/if~}} + {{#if show_expand_button}} +
+ +
+ {{/if}} {{#unless disabled}}
diff --git a/web/tests/pill_typeahead.test.cjs b/web/tests/pill_typeahead.test.cjs index b8b42b9e8f..7edf92cae6 100644 --- a/web/tests/pill_typeahead.test.cjs +++ b/web/tests/pill_typeahead.test.cjs @@ -94,14 +94,14 @@ const admins = { name: "Admins", description: "foo", id: 1, - members: [jill.user_id, mark.user_id, me.user_id], + members: new Set([jill.user_id, mark.user_id, me.user_id]), }; const admins_item = user_group_item(admins); const testers = { name: "Testers", description: "bar", id: 2, - members: [mark.user_id, fred.user_id, me.user_id], + members: new Set([mark.user_id, fred.user_id, me.user_id]), }; const testers_item = user_group_item(testers); diff --git a/web/tests/user_group_pill.test.cjs b/web/tests/user_group_pill.test.cjs index 2007c62352..15a0e1d139 100644 --- a/web/tests/user_group_pill.test.cjs +++ b/web/tests/user_group_pill.test.cjs @@ -77,6 +77,7 @@ const testers_pill = { group_id: testers.id, group_name: testers.name, type: "user_group", + show_expand_button: false, }; const everyone_pill = { group_id: everyone.id,