1/* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5import { actionCreators as ac } from "common/Actions.jsm"; 6import { ContextMenu } from "content-src/components/ContextMenu/ContextMenu"; 7import React from "react"; 8import { connect } from "react-redux"; 9import { SectionMenuOptions } from "content-src/lib/section-menu-options"; 10 11const DEFAULT_SECTION_MENU_OPTIONS = [ 12 "MoveUp", 13 "MoveDown", 14 "Separator", 15 "RemoveSection", 16 "CheckCollapsed", 17 "Separator", 18 "ManageSection", 19]; 20const WEBEXT_SECTION_MENU_OPTIONS = [ 21 "MoveUp", 22 "MoveDown", 23 "Separator", 24 "CheckCollapsed", 25 "Separator", 26 "ManageWebExtension", 27]; 28 29export class _SectionMenu extends React.PureComponent { 30 handleAddWhileCollapsed() { 31 const { action, userEvent } = SectionMenuOptions.ExpandSection(this.props); 32 this.props.dispatch(action); 33 if (userEvent) { 34 this.props.dispatch( 35 ac.UserEvent({ 36 event: userEvent, 37 source: this.props.source, 38 }) 39 ); 40 } 41 } 42 43 getOptions() { 44 const { props } = this; 45 46 const propOptions = props.isWebExtension 47 ? [...WEBEXT_SECTION_MENU_OPTIONS] 48 : [...DEFAULT_SECTION_MENU_OPTIONS]; 49 50 // Remove Collapse/Expand related option if the `newNewtabExperience.enabled` 51 // pref is set to true. 52 if (props.Prefs.values.featureConfig.newNewtabExperienceEnabled) { 53 if (props.isWebExtension) { 54 propOptions.splice(2, 2); 55 } else { 56 propOptions.splice(4, 1); 57 } 58 } 59 60 // Remove the move related options if the section is fixed 61 if (props.isFixed) { 62 propOptions.splice(propOptions.indexOf("MoveUp"), 3); 63 } 64 // Prepend custom options and a separator 65 if (props.extraOptions) { 66 propOptions.splice(0, 0, ...props.extraOptions, "Separator"); 67 } 68 // Insert privacy notice before the last option ("ManageSection") 69 if (props.privacyNoticeURL) { 70 propOptions.splice(-1, 0, "PrivacyNotice"); 71 } 72 73 const options = propOptions 74 .map(o => SectionMenuOptions[o](props)) 75 .map(option => { 76 const { action, id, type, userEvent } = option; 77 if (!type && id) { 78 option.onClick = () => { 79 const hasAddEvent = 80 userEvent === "MENU_ADD_TOPSITE" || 81 userEvent === "MENU_ADD_SEARCH"; 82 83 if (props.collapsed && hasAddEvent) { 84 this.handleAddWhileCollapsed(); 85 } 86 87 props.dispatch(action); 88 if (userEvent) { 89 props.dispatch( 90 ac.UserEvent({ 91 event: userEvent, 92 source: props.source, 93 }) 94 ); 95 } 96 }; 97 } 98 return option; 99 }); 100 101 // This is for accessibility to support making each item tabbable. 102 // We want to know which item is the first and which item 103 // is the last, so we can close the context menu accordingly. 104 options[0].first = true; 105 options[options.length - 1].last = true; 106 return options; 107 } 108 109 render() { 110 return ( 111 <ContextMenu 112 onUpdate={this.props.onUpdate} 113 options={this.getOptions()} 114 keyboardAccess={this.props.keyboardAccess} 115 /> 116 ); 117 } 118} 119 120export const SectionMenu = connect(state => ({ 121 Prefs: state.Prefs, 122}))(_SectionMenu); 123