diff --git a/.dev/sass/layouts/_hero.scss b/.dev/sass/layouts/_hero.scss
index c27244bc..ec0444f4 100644
--- a/.dev/sass/layouts/_hero.scss
+++ b/.dev/sass/layouts/_hero.scss
@@ -36,6 +36,10 @@
list-style: none;
}
}
+
+ b, strong {
+ font-weight: 600;
+ }
}
label, input, select, textarea {
diff --git a/assets/js/admin/hero-text-widget.js b/assets/js/admin/hero-text-widget.js
new file mode 100755
index 00000000..42963709
--- /dev/null
+++ b/assets/js/admin/hero-text-widget.js
@@ -0,0 +1,169 @@
+/* global jQuery, primer_hero_text_widget */
+
+( function( $ ) {
+
+ var link = {
+
+ init: function( input ) {
+
+ if ( ! ( $ && $.ui && $.ui.autocomplete ) ) {
+
+ return;
+
+ }
+
+ var $input = $( input ),
+ cache, last;
+
+ $input.on( 'keydown', function() {
+
+ $input.removeAttr( 'aria-activedescendant' );
+
+ } )
+ .autocomplete( {
+
+ source: function( request, response ) {
+
+ if ( last === request.term ) {
+
+ response( cache );
+
+ return;
+
+ }
+
+ if ( /^https?:/.test( request.term ) || request.term.indexOf( '.' ) !== -1 ) {
+
+ return response();
+
+ }
+
+ $.post(
+ window.ajaxurl,
+ {
+ action: 'wp-link-ajax',
+ page: 1,
+ search: request.term,
+ _ajax_linking_nonce: primer_hero_text_widget._ajax_linking_nonce
+ },
+ function( data ) {
+
+ cache = data;
+ response( data );
+
+ },
+ 'json'
+ );
+
+ last = request.term;
+
+ },
+ focus: function( event, ui ) {
+
+ $input.attr( 'aria-activedescendant', 'mce-wp-autocomplete-' + ui.item.ID );
+
+ /*
+ * Don't empty the URL input field, when using the arrow keys to
+ * highlight items. See api.jqueryui.com/autocomplete/#event-focus
+ */
+ event.preventDefault();
+
+ },
+ select: function( event, ui ) {
+
+ $input.val( ui.item.permalink );
+
+ // This is for the customizer.
+ $input.trigger( 'change' );
+
+ return false;
+
+ },
+ open: function() {
+
+ $input.attr( 'aria-expanded', 'true' );
+
+ },
+ close: function() {
+
+ $input.attr( 'aria-expanded', 'false' );
+
+ },
+ minLength: 2,
+ position: {
+
+ my: 'left top+2'
+
+ }
+
+ } ).autocomplete( 'instance' )._renderItem = function( ul, item ) {
+
+ return $( '
' )
+ .append( '' + item.title + ' ' + item.info + '' )
+ .appendTo( ul );
+
+ };
+
+ $input.attr( {
+ 'role': 'combobox',
+ 'aria-autocomplete': 'list',
+ 'aria-expanded': 'false',
+ 'aria-owns': $input.autocomplete( 'widget' ).attr( 'id' )
+ } )
+ .on( 'focus', function() {
+
+ var inputValue = $input.val();
+
+ /*
+ * Don't trigger a search if the URL field already has a link or is empty.
+ * Also, avoids screen readers announce `No search results`.
+ */
+ if ( inputValue && ! /^https?:/.test( inputValue ) ) {
+
+ $input.autocomplete( 'search' );
+
+ }
+
+ } )
+ // Returns a jQuery object containing the menu element.
+ .autocomplete( 'widget' )
+ .attr( 'role', 'listbox' )
+ .removeAttr( 'tabindex' ) // Remove the `tabindex=0` attribute added by jQuery UI.
+ /*
+ * Looks like Safari and VoiceOver need an `aria-selected` attribute. See ticket #33301.
+ * The `menufocus` and `menublur` events are the same events used to add and remove
+ * the `ui-state-focus` CSS class on the menu items. See jQuery UI Menu Widget.
+ */
+ .on( 'menufocus', function( event, ui ) {
+
+ ui.item.attr( 'aria-selected', 'true' );
+
+ } )
+ .on( 'menublur', function() {
+
+ /*
+ * The `menublur` event returns an object where the item is `null`
+ * so we need to find the active item with other means.
+ */
+ $( this ).find( '[aria-selected="true"]' ).removeAttr( 'aria-selected' );
+
+ } );
+
+ } // end init.
+
+ };
+
+ function addAutocomplete() {
+
+ $( '.primer-hero-text-widget input.link' ).each( function() {
+
+ link.init( this );
+
+ } );
+
+ }
+
+ $( document ).ready( addAutocomplete );
+ $( document ).on( 'primer.widgets.change', addAutocomplete );
+
+} )( jQuery );
diff --git a/assets/js/admin/hero-text-widget.min.js b/assets/js/admin/hero-text-widget.min.js
new file mode 100644
index 00000000..781b345b
--- /dev/null
+++ b/assets/js/admin/hero-text-widget.min.js
@@ -0,0 +1 @@
+!function(a){function b(){a(".primer-hero-text-widget input.link").each(function(){c.init(this)})}var c={init:function(b){if(a&&a.ui&&a.ui.autocomplete){var c,d,e=a(b);e.on("keydown",function(){e.removeAttr("aria-activedescendant")}).autocomplete({source:function(b,e){return d===b.term?void e(c):/^https?:/.test(b.term)||b.term.indexOf(".")!==-1?e():(a.post(window.ajaxurl,{action:"wp-link-ajax",page:1,search:b.term,_ajax_linking_nonce:primer_hero_text_widget._ajax_linking_nonce},function(a){c=a,e(a)},"json"),void(d=b.term))},focus:function(a,b){e.attr("aria-activedescendant","mce-wp-autocomplete-"+b.item.ID),a.preventDefault()},select:function(a,b){return e.val(b.item.permalink),e.trigger("change"),!1},open:function(){e.attr("aria-expanded","true")},close:function(){e.attr("aria-expanded","false")},minLength:2,position:{my:"left top+2"}}).autocomplete("instance")._renderItem=function(b,c){return a('').append(""+c.title+' '+c.info+"").appendTo(b)},e.attr({role:"combobox","aria-autocomplete":"list","aria-expanded":"false","aria-owns":e.autocomplete("widget").attr("id")}).on("focus",function(){var a=e.val();a&&!/^https?:/.test(a)&&e.autocomplete("search")}).autocomplete("widget").attr("role","listbox").removeAttr("tabindex").on("menufocus",function(a,b){b.item.attr("aria-selected","true")}).on("menublur",function(){a(this).find('[aria-selected="true"]').removeAttr("aria-selected")})}}};a(document).ready(b),a(document).on("primer.widgets.change",b)}(jQuery);
\ No newline at end of file
diff --git a/functions.php b/functions.php
index 3cde142b..e4131a16 100755
--- a/functions.php
+++ b/functions.php
@@ -436,6 +436,21 @@ function primer_register_sidebars() {
}
add_action( 'widgets_init', 'primer_register_sidebars' );
+/**
+ * Register Primer widgets.
+ *
+ * @link http://codex.wordpress.org/Function_Reference/register_widget
+ * @since NEXT
+ */
+function primer_register_widgets() {
+
+ require_once get_template_directory() . '/inc/hero-text-widget.php';
+
+ register_widget( 'Primer_Hero_Text_Widget' );
+
+}
+add_action( 'widgets_init', 'primer_register_widgets' );
+
/**
* Enqueue theme scripts and styles.
*
diff --git a/inc/hero-text-widget.php b/inc/hero-text-widget.php
new file mode 100755
index 00000000..c36fb658
--- /dev/null
+++ b/inc/hero-text-widget.php
@@ -0,0 +1,263 @@
+ true,
+ 'classname' => 'widget_text primer-widgets primer-hero-text-widget',
+ 'description' => sprintf(
+ esc_html_x( "A %s theme widget designed for the Hero area on your site's front page.", 'theme name', 'primer' ),
+ esc_html( $this->get_current_theme_name() )
+ ),
+ );
+
+ parent::__construct( 'primer-hero-text', esc_html_x( 'Hero Text', 'the widget title', 'primer' ), $widget_options );
+
+ add_action( 'admin_init', array( $this, 'register_scripts' ) );
+
+ }
+
+ /**
+ * Display the widget on the front-end.
+ *
+ * @since NEXT
+ *
+ * @param array $args Display arguments including `before_title`, `after_title`, `before_widget`, and `after_widget`.
+ * @param array $instance The settings for the particular instance of the widget.
+ */
+ public function widget( $args, $instance ) {
+
+ /**
+ * Filter the widget title.
+ *
+ * @link https://developer.wordpress.org/reference/hooks/widget_title/
+ * @since NEXT
+ *
+ * @param array $instance An array of the widget's settings.
+ * @param string $id_base The widget ID.
+ *
+ * @var string
+ */
+ $title = ! empty( $instance['title'] ) ? (string) apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) : null;
+
+ /**
+ * Filter the widget text.
+ *
+ * @link https://developer.wordpress.org/reference/hooks/widget_text/
+ * @since NEXT
+ *
+ * @param array $instance Array of settings for the current widget.
+ * @param Primer_Hero_Text_Widget $this Current Hero Text widget instance.
+ *
+ * @var string
+ */
+ $text = ! empty( $instance['text'] ) ? (string) apply_filters( 'widget_text', $instance['text'], $instance, $this ) : null;
+
+ $button_text = ! empty( $instance['button_text'] ) ? $instance['button_text'] : null;
+ $button_link = ! empty( $instance['button_link'] ) ? $instance['button_link'] : null;
+
+ // The `button_link` can be empty.
+ if ( ! $title && ! $text && ! $button_text ) {
+
+ return;
+
+ }
+
+ echo $args['before_widget']; // xss ok.
+
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ wp_create_nonce( 'internal-linking' ),
+ )
+ );
+
+ }
+
+ /**
+ * Print widget admin scripts.
+ *
+ * @action admin_print_footer_scripts
+ * @action customize_controls_print_footer_scripts
+ * @since NEXT
+ */
+ public function print_scripts() {
+
+ wp_print_scripts( 'primer-admin-hero-text-widget' );
+
+ }
+
+ /**
+ * Return the current theme name.
+ *
+ * Looks for the `current_theme` option first, and if not
+ * present will fetch it using `wp_get_theme()`.
+ *
+ * @since NEXT
+ *
+ * @return string
+ */
+ protected function get_current_theme_name() {
+
+ if ( $current_theme = get_option( 'current_theme' ) ) {
+
+ return $current_theme;
+
+ }
+
+ $theme = wp_get_theme();
+
+ return $theme->get( 'Name' );
+
+ }
+
+}
diff --git a/style-rtl.css b/style-rtl.css
index d9047b9a..ed4d8aa6 100644
--- a/style-rtl.css
+++ b/style-rtl.css
@@ -1681,6 +1681,8 @@ body.no-max-width .page-title-container .page-header {
padding-right: 0; }
.hero .widget ul li, .hero .widget ol li {
list-style: none; }
+ .hero .widget b, .hero .widget strong {
+ font-weight: 600; }
.hero label, .hero input, .hero select, .hero textarea {
display: inline;
width: auto; }
diff --git a/style.css b/style.css
index 63e363aa..8989bd0c 100644
--- a/style.css
+++ b/style.css
@@ -1681,6 +1681,8 @@ body.no-max-width .page-title-container .page-header {
padding-left: 0; }
.hero .widget ul li, .hero .widget ol li {
list-style: none; }
+ .hero .widget b, .hero .widget strong {
+ font-weight: 600; }
.hero label, .hero input, .hero select, .hero textarea {
display: inline;
width: auto; }