<div class="search-field ">
    <div class="search-field__field">
        <input type="search" class="search-field__input" />
        <button class="search-field__submit" type="submit"></button>
    </div>
</div>
<div class="search-field {{ getmodifiers modifiers "search-field" }}">
    <div class="search-field__field">
        <input type="search" class="search-field__input" {{{getattributes attributes}}} />
        <button class="search-field__submit" type="submit"></button>
    </div>
</div>
/* No context defined. */
  • Content:
    import React from 'react';
    
    const Geolocation = (props) => {
    
        const requestIpLocation = async (cb) => {
            try {
                const res = await fetch('https://ipinfo.io/geo');
                const data = await res.json();
                cb(data.loc.split(','));
            } catch (e) {
                console.warn(e);
            }
        };
    
        const requestGeolocation = async (e) => {
            e.preventDefault();
    
            if (!navigator.geolocation) {
                return requestIpLocation();
            }
            const cords = await new Promise((resolve) => {
                navigator.geolocation.getCurrentPosition((position) => {
                    resolve([position.coords.latitude, position.coords.longitude]);
                }, () => requestIpLocation(resolve));
            });
            props.onGeolocation(cords);
        };
    
        return (
            <button
                className="search-field__geolocation"
                onClick={requestGeolocation}
            />
        );
    };
    
    export default Geolocation;
    
  • URL: /components/raw/search-field/Geolocation.jsx
  • Filesystem Path: src/components/search-field/Geolocation.jsx
  • Size: 976 Bytes
  • Content:
    import React from 'react';
    import Downshift from 'downshift';
    import PropTypes from 'prop-types';
    import Radio from '../form/radio/Radio';
    
    const SearchField = (props) => {
        const {
            preSelectedFirstItem,
            onInputChange,
            onSubmit,
            onChange,
            itemStyle,
            items,
            itemComponent,
            className,
            inputPlaceholder,
            categories,
            suggestions,
            inputValue: initialInputValue,
            ...downshiftProps
        } = props;
    
        const [selected, setSelected] = React.useState(null);
        const hasCategories =
            categories &&
            Array.isArray(categories.items) &&
            categories.items.length > 0;
        const hasSuggestions =
            suggestions &&
            Array.isArray(suggestions.items) &&
            suggestions.items.length > 0;
    
        function stateReducer(state, changes) {
            // this prevents the menu from being closed when the user
            // selects an item with a keyboard or mouse
            switch (changes.type) {
                case Downshift.stateChangeTypes.mouseUp:
                    // Preserve input value on mouseup (when clicking outside)
                    return {
                        ...changes,
                        inputValue: state.inputValue
                    };
                case Downshift.stateChangeTypes.blurInput:
                    return {
                        ...changes,
                        ...(items[state.highlightedIndex] && {
                            inputValue: items[state.highlightedIndex].value
                        }),
                        ...(typeof onSubmit !== 'undefined' && {
                            inputValue: state.inputValue
                        }),
                        ...(onSubmit === 'undefined' && {
                            highlightedIndex: state.highlightedIndex
                        })
                    };
                default:
                    return changes;
            }
        }
    
        return (
            <form onSubmit={(e) => onSubmit && onSubmit(e)}>
                <Downshift
                    itemToString={(item) => (item ? item.value : ``)}
                    defaultHighlightedIndex={preSelectedFirstItem ? 0 : null}
                    initialInputValue={initialInputValue}
                    stateReducer={stateReducer}
                    {...downshiftProps}
                >
                    {({
                        getInputProps,
                        getItemProps,
                        getMenuProps,
                        isOpen,
                        inputValue,
                        highlightedIndex,
                        reset,
                        clearSelection
                    }) => (
                        <div className={`search-field ${className}`}>
                            {/* Search field */}
                            <div className="search-field__field">
                                <input
                                    name="q"
                                    className="search-field__input"
                                    placeholder={inputPlaceholder}
                                    type="search"
                                    {...getInputProps({
                                        onChange: (e) => onInputChange(e),
                                        // Ensure value is always a string to prevent controlled/uncontrolled warning
                                        value: inputValue || ''
                                    })}
                                />
    
                                {/* Clear button */}
                                {inputValue && inputValue.length > 0 && (
                                    <button
                                        type="button"
                                        className="search-field__clear"
                                        aria-label="Rensa sökfältet"
                                        onClick={(e) => {
                                            onInputChange({
                                                target: { value: '' }
                                            });
    
                                            clearSelection();
                                            reset();
                                            e.preventDefault();
                                        }}
                                    />
                                )}
    
                                <button
                                    className="search-field__submit"
                                    type="submit"
                                    onClick={(e) => {
                                        if (
                                            typeof items[highlightedIndex] !==
                                            'undefined'
                                        ) {
                                            onChange(items[highlightedIndex]);
                                            e.preventDefault();
                                        } else if (
                                            typeof onSubmit === 'undefined'
                                        ) {
                                            e.preventDefault();
                                        }
                                    }}
                                    aria-label={inputPlaceholder}
                                />
                                {props.children}
                            </div>
                            {/* Autocomplete */}
                            {isOpen && inputValue.length > 0 && items.length > 0 ? (
                                <React.Fragment>
                                    <ul
                                        className={`search-field__autocomplete ${
                                            isOpen &&
                                            `search-field__autocomplete--is-open`
                                        }`}
                                        {...getMenuProps()}
                                    >
                                        {items.map((item, index) => (
                                            <li
                                                className={`search-field__item ${
                                                    highlightedIndex === index &&
                                                    `search-field__item--selected`
                                                } ${itemStyle}`}
                                                {...getItemProps({
                                                    key: index,
                                                    index,
                                                    item
                                                })}
                                            >
                                                {itemComponent(item)}
                                            </li>
                                        ))}
                                    </ul>
                                </React.Fragment>
                            ) : null}
    
                            {(hasCategories || hasSuggestions) && (
                                <div className="row">
                                    {/* Categories */}
                                    {hasCategories && (
                                        <div className="column-block col col--span-12 col--span-s-8">
                                            <div className="search-field__categories">
                                                <Radio
                                                    id="search-category"
                                                    name="category"
                                                    label={categories.label}
                                                    selectedValue={selected || categories.items[0].key}
                                                    onChange={(val) =>
                                                        setSelected(val)
                                                    }
                                                    modifier="radio-pill"
                                                    options={categories.items}
                                                />
                                            </div>
                                        </div>
                                    )}
                                    <div className="column-block col col--span-12 col--span-s-4">
                                        {/* Popular links */}
                                        {hasSuggestions && (
                                            <div className="search-field__suggestions">
                                                {suggestions.label && (
                                                    <div className="search-field__suggestions-label">
                                                        {suggestions.label}
                                                    </div>
                                                )}
                                                <ul>
                                                    {suggestions &&
                                                        suggestions.items &&
                                                        suggestions.items.map(
                                                            (
                                                                popularLink,
                                                                index
                                                            ) => {
                                                                return (
                                                                    <li key={index}>
                                                                        <a
                                                                            href={
                                                                                popularLink.url
                                                                            }
                                                                        >
                                                                            {
                                                                                popularLink.label
                                                                            }
                                                                        </a>
                                                                    </li>
                                                                );
                                                            }
                                                        )}
                                                </ul>
                                            </div>
                                        )}
                                    </div>
                                </div>
                            )}
                        </div>
                    )}
                </Downshift>
            </form>
        );
    };
    
    SearchField.propTypes = {
        onInputChange: PropTypes.func,
        onSubmit: PropTypes.func,
        itemStyle: PropTypes.string,
        items: PropTypes.array.isRequired,
        itemComponent: PropTypes.func,
        className: PropTypes.string,
        inputPlaceholder: PropTypes.string,
        preSelectedFirstItem: PropTypes.bool,
        seeMoreLink: PropTypes.string,
        inputValue: PropTypes.string,
        categories: PropTypes.shape({
            label: PropTypes.string,
            items: PropTypes.arrayOf(
                PropTypes.shape({
                    value: PropTypes.string,
                    label: PropTypes.string
                })
            )
        }),
        suggestions: PropTypes.shape({
            label: PropTypes.string,
            items: PropTypes.arrayOf(
                PropTypes.shape({
                    label: PropTypes.string,
                    url: PropTypes.string
                })
            )
        })
    };
    
    SearchField.defaultProps = {
        onInputChange: () => {},
        itemStyle: '',
        className: '',
        preSelectedFirstItem: true,
        seeMoreLink: ''
    };
    
    export default SearchField;
    
  • URL: /components/raw/search-field/SearchField.jsx
  • Filesystem Path: src/components/search-field/SearchField.jsx
  • Size: 11.7 KB
  • Content:
    import SearchField from './SearchField';
    export { default as Geolocation } from './Geolocation';
    
    export default SearchField;
    
  • URL: /components/raw/search-field/index.js
  • Filesystem Path: src/components/search-field/index.js
  • Size: 126 Bytes
  • Content:
    $height-big: size(8);
    $height-small: size(6);
    
    .search-field {
        width: 100%;
        position: relative;
        margin-bottom: size(4);
    
        &--big {
            @include breakpoint-classes {
                @include breakpoint($s) {
                    .search-field__input,
                    .search-field__submit,
                    .search-field__geolocation {
                        height: $height-big;
                    }
                    .search-field__submit,
                    .search-field__geolocation {
                        width: $height-big;
                        background-size: calc(100% - #{size(3)});
                    }
                }
            }
        }
    }
    
    .search-field__field {
        display: flex;
    }
    
    .search-field__input {
        border: $color-gray-2 1px solid;
        padding: size(2) size(3);
        line-height: size(3);
        height: $height-small;
        display: flex;
        flex: 1;
        font-size: size(2.5);
        border-radius: size(0.5) 0 0 size(0.5);
        border-right: 0;
        background: $color-white;
        width: 100%;
    
        @include placeholder {
            color: $color-gray-3;
            font-size: size(2.5);
            opacity: 1;
        }
    
        &::-webkit-input-placeholder {
            transform: translateY(0);
            -webkit-transform: translateY(2px);
        }
    
        &::-webkit-search-decoration,
        &::-webkit-search-cancel-button,
        &::-webkit-search-results-button,
        &::-webkit-search-results-decoration {
            display: none;
        }
    }
    
    // buttons
    .search-field__geolocation,
    .search-field__submit {
        width: $height-small;
        height: $height-small;
        min-width: auto;
    }
    
    .search-field__geolocation {
        background: url(./img/icon-crosshair.svg) $color-gray-1 no-repeat center;
        border-radius: size(0.5);
        background-size: calc(100% - #{size(2)});
        margin-left: size(1);
    
        @include breakpoint($m) {
            margin-left: size(2);
        }
    }
    
    .search-field__clear {
        position: absolute;
        background: url(./img/icon-close.svg) no-repeat center;
        right: 48px;
        top: 24px;
        transform: translateY(-50%);
        width: size(5);
        height: size(5);
        z-index: 1;
    
        @include breakpoint($s) {
            right: 64px;
            top: 32px;
        }
    }
    
    .search-field__submit {
        background: url(./img/icon-search.svg) no-repeat center;
        border-radius: 0 size(0.5) size(0.5) 0;
        background-size: calc(100% - #{size(2)});
        background-color: $color-green-dark;
    
        &:hover {
            cursor: pointer;
        }
    
        @include color-theme {
            background-color: $arg-theme-color;
        }
    }
    
    .search-field__autocomplete {
        position: absolute;
        width: 100%;
        border-radius: size(0.5);
        border: $color-gray-2 1px solid;
        visibility: hidden;
        list-style: none;
        padding: size(2);
        margin: 0;
        text-align: left;
        z-index: 1;
        background-color: $color-white;
    
        &--is-open {
            visibility: visible;
        }
    }
    
    .search-field__item {
        display: flex;
        align-items: center;
        margin-top: size(1);
    
        &:first-child {
            margin-top: 0;
        }
    
        &--selected {
            a {
                background-color: $color-gray-1;
            }
        }
    
        &--link-holder {
            justify-content: center;
            padding: size(2) size(3);
        }
    
        &--link {
            color: $color-green;
            display: inline-block;
            vertical-align: top;
            position: relative;
            padding-right: size(2);
        }
    
        &--icon {
            position: absolute;
            top: 50%;
            right: 0;
            width: 16px;
            height: 16px;
            vertical-align: middle;
            fill: currentColor;
            transform: translateY(-50%);
        }
    }
    
    .search-widget__spinner {
        position: absolute;
        top: 32px;
        transform: translateY(-50%);
        right: 70px;
    
        .search-field--with-clear-button & {
            top: 24px;
            right: 80px;
    
            @include breakpoint($s) {
                top: 32px;
                right: 100px;
            }
        }
    }
    
    .search-field {
        .header__search & {
            display: none;
        }
    
        &.is-open {
            .header__search & {
                display: block;
            }
        }
    }
    .search-field__categories,
    .search-field__suggestions {
        margin-top: size(4);
    }
    
    .search-field__categories {
        .form-item__legend {
            margin-bottom: size(3);
        }
    }
    
    .search-field__suggestions {
        ul {
            list-style: none;
            padding: 0;
        }
    
        li {
            padding-top: size(1);
    
            &:first-child {
                padding-top: 0;
            }
        }
    
        a {
            display: block;
            color: $color-black;
            font-size: size(2);
            line-height: size(3);
            font-weight: 700;
            padding: 0 size(0.5);
    
            &:hover {
                background-color: $color-gray-1;
                text-decoration: none;
            }
        }
    
        &-label {
            margin-bottom: size(2);
            font-size: size(2);
            color: $color-gray-5;
        }
    }
    
  • URL: /components/raw/search-field/search-field.scss
  • Filesystem Path: src/components/search-field/search-field.scss
  • Size: 4.8 KB

No notes defined.