Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                #loc-8-component

    #loc-8-view.row(v-if='uiState==="view"')
        //-
          This is the View state
          This will show the current location either from WP
          or values be saved to WP upon a form submit
        .one-half.first
          h2(v-if='hasLocation') Current Location
          h2(v-else) Address Only

          tempate(v-if='hasLocation || fullAddress(loc) !== ""')
              p.l8-address {{ fullAddress(loc) }}
              p(v-if="hasLocation").l8-latlong ({{ loc.lat + ', ' + loc.long }})
          template(v-else)
              p.l8-address No Location
          template(v-if='canEdit')
              template(v-if='hasLocation')
                  button(v-on:click.prevent="currentEdit()") Edit
                  button(v-on:click.prevent="currentRemove()") Remove
              template(v-else-if='!hasLocation && fullAddress(loc) === ""')
                  button(v-on:click.prevent="currentEdit()") Add
              template(v-else)
                  button(v-on:click.prevent="currentEdit()") Edit & Geocode
        .one-half
            template(v-if='hasLocation')
              map-component(v-bind:loc="loc")

    #loc-8-edit.row(v-if='uiState==="edit"')
        //-
          This is the Edit state
          User can input address or Lat/Long and do a geocode search
          When the Geocode completes - goes to Choice state
        .one-half    
          h2 Edit Location
          label(for="l8address") Address
          input(type="text" v-model="editLoc.address" id="l8address")
          label(for="l8address2") Address2
          input(type="text" v-model="editLoc.address2" id="l8address2")
          label(for="l8city") City/Town
          input(type="text" v-model="editLoc.city" id="l8city")
          label(for="l8state") State/Province/Region
          input(type="text" v-model="editLoc.state" id="l8state")
          label(for="l8zip") Zip/Postal Code
          input(type="text" v-model="editLoc.zip" id="l8zip")
          label(for="l8country") Country
          input(type="text" v-model="editLoc.country" id="l8country")
          label(for="l8lat") Latitude
          input(type="text" v-model="editLoc.lat" id="l8lat")
          label(for="l8long") Longitutde
          input(type="text" v-model="editLoc.long" id="l8long")
          button(v-on:click.prevent="editOk()") OK
          button(v-on:click.prevent="geocode()") Geocode
          button(v-on:click.prevent="editCancel()") Cancel
          button(v-on:click.prevent="choiceUseMyAddress()") Use this address without Geocoding without Geocoding
          #loc-8-spinner(v-show="request") Requesting....

        .one-half
          //-
            Had to add key attribute so Vue wouldn't try to reuse map-component.  See - https://vuejs.org/v2/guide/conditional.html
          map-component(v-bind:loc="editLoc" key="loc-8-edit")
          
    #loc-8-choice.row(v-if='uiState === "choice"')
        //-
          This is the Choice state
          Allows the user to select the address that was
          returned by Geocode
          Or to use the address inputted if not found
          v-if is used so this markup is recreated each time (new map instances)

        .one-half
          ul 
            li(v-for='(item, index) in results' v-bind:class="{'selected': selected == index}" v-on:click.prevent='choiceSelect(index)')
              p.loc-8-address {{ fullAddress(item) }}
              p.loc-8-latlong {{ item.lat + ', ' + item.long }}
          button(v-show="selected != null" v-on:click.prevent="choiceUseSelected()") Use Selected
          button(v-show="selected != null" v-on:click.prevent="choiceUseSelectedLocation()") Use Selected for Location but keep my address info
          button(v-on:click.prevent='uiState="edit"') None of these, Search again
          //- button(v-on:click.prevent="choiceUseMyAddress()") None but use my address info anyway
        .one-half
          //-
            Had to add key attribute so Vue wouldn't try to reuse map-component.  See - https://vuejs.org/v2/guide/conditional.html
          map-component(v-bind:loc="results" key="loc-8-choice")            

    #loc-8-post-inputs
        //-
          If this component is in WP admin page it will be in a form
          These contain the values to be returned via $_POST
          if there is a submit.
          Remember to add the WP nonce
        input(type="hidden" v-model="loc.lat" name="loc-8-lat")
        input(type="hidden" v-model="loc.long" name="loc-8-long")
        input(type="hidden" v-model="loc.address" name="loc-8-address")
        input(type="hidden" v-model="loc.address2" name="loc-8-address2")
        input(type="hidden" v-model="loc.city" name="loc-8-city")
        input(type="hidden" v-model="loc.state" name="loc-8-state")
        input(type="hidden" v-model="loc.zip" name="loc-8-zip")
        input(type="hidden" v-model="loc.country" name="loc-8-country")
        input(type="hidden" v-model="addressChanged" name="loc-8-changed")
        input(type="hidden" v-model="addressDeleted" name="loc-8-deleted")

              
            
!

CSS

              
                @use postcss-simple-vars;

.row {
  display: flex;
  padding: 3rem;
  flex-wrap: wrap;
  justify-content: center;
    align-items: center;
}
.one-half {
  width: 45%;
  min-width: 35rem;
  
}
.aMap {
  min-height: 280px;
  width: 100%;
}

.loc-8-edit-tabs li {
  cursor: pointer;
  display: inline-block;
  padding: 5px;
  font-weight :bold;

}
ul, li {
    list-style-type: none;
  padding: 0;
  margin: 0;
}

#loc-8-choice li {
  cursor: pointer;

}

#loc-8-choice .selected {
  border: 1px solid #000;
}

.double-input {
  margin-top: 1rem;
}

.double-input span {
  font-size: 1rem;
}
.double-input span:before {
  content: "(Prev:";
  font-weight: 800;
}
.double-input span:after {
  content: ")";
}
.double-input label {
  display: block;
}
#loc-8-choice ul {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}
#loc-8-choice li {
  line-height: 1;
  padding: 1rem;
  font-size: 1.4rem;
  width: 90%;
}
#loc-8-choice li:nth-child(2n+1) {
  background: #eee;
}
#loc-8-choice li:hover {
  background: #ccc;
}
              
            
!

JS

              
                // Simulate the data WP will populate on page creation
let loc_8_fwp = {
    result: {
        lat: '32.3336368',
        long: '-95.2930722',
        address: '1329 S Beckham Ave',
        address2: '',
        city: 'Tyler',
        state: 'TX',
        zip: '75701',
        country: 'USA'
    },
    canEdit: true,
    mapTileLayer: 'https://api.mapbox.com/styles/v1/mapbox/streets-v10/tiles/256/{z}/{x}/{y}?access_token={accessToken}',
    mapAttribution: 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://mapbox.com">Mapbox</a>',
    mapAccessId: 'CodePen',
    mapMaxZoom: 18,
         mapAccessToken: 'pk.eyJ1IjoiamVyMGRoIiwiYSI6ImNpeGo3MGRjaTAwNGIyd280ODJ0dzA1bm4ifQ.tFc-Mw0uY6Zf5056W_R5qw',
         action: 'loc_8_geocode',
         ajax_url: 'http://staging3.adv.jhtechservices.com/wp-admin/admin-ajax.php'
}

// Simulate a sample result of an Ajax or REST geocode request
let fwpResults = {
    results: [{
        lat: '30.0075278',
        long: '-95.7369283',
        address: '16125 Country Fair Ln',
        city: 'Cypress',
        state: 'TX',
        zip: '77433',
        country: 'USA'
    }, {
        lat: '29.9316274',
        long: '-95.6702606',
        address: '11655 Green Canyon Dr',
        city: 'Houston',
        state: 'TX',
        zip: '77095',
        country: 'USA'
    }, {
        lat: '39.612032',
        long: '-82.904623',
        address: '1476 Lancaster Pike',
        city: 'Circleville',
        state: 'OH',
        zip: '43113',
        country: 'USA'
    }]
}
// Vue component as a simple bus to pass events between components
let bus = new Vue();


// Vue component to hold the Leaflet Map
// Pass in the Location object containing the lat and long in props
// Added ability to add an array for multiple markers.  It will emit the
// index of the location array that was clicked. Using a simple bus
// Currently does not update the map if lat/long change in the array
Vue.component('map-component', {
    template: '<div class="aMap"></div>',
    props: ['loc'],
    data: function() {
        return {
            map: {},
            markers: []
        }
    },
    // Initialize map
    mounted: function() {
        this.map = L.map(this.$el);
        L.tileLayer(loc_8_fwp.mapTileLayer, {
            attribution: loc_8_fwp.mapAttribution,
            maxZoom: loc_8_fwp.mapMaxZoom,
            id: loc_8_fwp.mapAccessId,
            accessToken: loc_8_fwp.mapAccessToken
        }).addTo(this.map);

        if (Array.isArray(this.loc)) {
            console.log(this.loc);
            for(let i = 0; i < this.loc.length; i++){
                let marker = L.marker([this.loc[i].lat, this.loc[i].long])
                    .addTo(this.map)
                    .on('click', this.markerClick);
                marker.loc_8_id = i;
                this.markers.push( marker );
            }
            let group = new L.featureGroup(this.markers);
            this.map.fitBounds(group.getBounds().pad(0.5));
        } else {
            let marker = L.marker([this.loc.lat, this.loc.long]).addTo(this.map);
            this.markers[0] = marker;
            this.map.setView([this.loc.lat, this.loc.long], 13);
        }
    },
    destroyed: function() { console.log ('destroyed'); },
    methods: {
        markerClick: function(e) {
            console.log("emitting" + e.target.loc_8_id);
            bus.$emit('marker-click', e.target.loc_8_id);
        }
    },
    // if Location changes from parent,
    // update map
    watch: {
        'loc': {
            deep: true,
            handler: function() {
                console.log("a map given new coords");
                if(Array.isArray(this.loc)) {
                    //To implement if needed.
                } else {
                    this.map.setView([this.loc.lat, this.loc.long], 13)
                        .removeLayer(this.markers[0]);
                    this.markers[0] = L.marker([this.loc.lat, this.loc.long]).addTo(this.map);
                }
            }
        }
    },

});

// Start and bind Vue framework
let vm = new Vue({
    el: '#loc-8-component',
    data: {
        loc: {
                    lat: '',
                    long: '',
                    address: '',
                    address2: '',
                    city: '',
                    state: '',
                    zip: '',
                    country: '',
                    geo_date: ''
        },
        editLoc: {},
                canEdit: false,
        uiState: 'view', //'view, 'edit', 'choice'
        request: false,
        results: [],
        selected: null,
        action: null,
        ajax_url: null,
        changed: false,
        deleted: false,
         originalLocation: ''

    },

            created: function() {
                if(typeof(loc_8_fwp) !== 'undefined') {
                    this.canEdit = loc_8_fwp.canEdit;
                    if (typeof(loc_8_fwp.result) !== 'undefined') {
                        this.copyLocation(loc_8_fwp.result, this.loc)
                    }
                    this.ajax_url = loc_8_fwp.ajax_url;
                    this.action = loc_8_fwp.action;
                    this.ajax_nonce = loc_8_fwp._ajax_nonce;
                }
                this.originalLocation = this.fullAddressHash(this.loc);
    },
    mounted: function() {
                console.log('setting up $on');
        let that = this;
        bus.$on('marker-click', function(s) {
            console.log('in bus.$on:masterClick with ' + s)
            that.selected = s;
        });
    },
    computed: {
        hasLocation: function() {
            return this.loc.long && this.loc.lat
        },
        editAddressChange : function() {
            return this.fullAddress(this.loc).toLowercase() !== this.fullAddress(this.editLoc).toLowercase();
        },
        editLocationChange: function() {
            return (this.loc.lat + this.loc.long) !== (this.editLoc.lat + this.editLoc.long);
                },
                addressChanged: function() {
                    return (this.originalLocation !== this.fullAddressHash(this.loc));
                },
                addressDeleted: function() {
                    return ( (( this.originalLocation !== '') && this.fullAddressHash(this.loc) === ''));
        }

    },
    methods: {
        // View State functions
        //---------------------------------------------
        // copies current location to editLoc for user manipulation
        // and changes to Edit state
        currentEdit: function() {
            this.editLoc = JSON.parse(JSON.stringify(this.loc));
            this.uiState = 'edit';
        },

        // Edit State functions
        //---------------------------------------------

        // Remove pressed so blank out location
        currentRemove: function() {
            this.loc = this.blankLocation();
        },

        // OK pressed so go back to View Stat
        // Move editLoc to loc
        // Check to see if address or loc has changed
        editOk: function() {
            this.copyLocation(this.editLoc, this.loc);
            this.selected=null;
            this.uiState = "view";
        },
        // Cancel pressed so go back to View State
        editCancel: function() {
            this.uiState = "view";
        },

        // Will send request to WP to geocode editLoc object
        // If multiple add a label: A-Z for markers
        geocode: function() {
            this.request = true;
            let that = this;
                /*    //Get the inputs for this component
                    let data = {};
                    $.each($('input[name^=geo_loc_8_]'),function(i,v){data[$(v).attr('name')] = $(v).val()});
                    data['action'] = this.action;
                    data['_ajax_nonce'] = this.ajax_nonce;
                    console.log(data);
                    $.ajax(this.ajax_url,{
                        method: 'POST',
                        data: data ,

                    } )
                        .done( (results) => {
                            console.log(results);
                            if(results.data.results.length > 0) {
                                that.results = results.data.results;
                                that.uiState = "choice";
                            }

                        })
                        .fail( (error) => {
                            console.log('failed: ' + error);
                        })
                        .always( () => {
                            that.request = false;
                        });

                   */
            setTimeout(
                function() {
                    that.request = false;
                    that.results = fwpResults.results;
                    that.uiState = 'choice';
                    that.selected = null;
                }, 500);
        },

        // The Choice State
        //-----------------------------------------------

        // Use the selected returned location so copy to loc
        // and change to View state
        choiceUseSelected: function() {
            this.copyLocation(this.results[this.selected], this.loc);
            this.selected = null;
            this.uiState = 'view';
        },

        // Use the selected results Lat/Long but use any inputted
        // field value from editLoc
        choiceUseSelectedLocation: function() {
            let result = this.results[this.selected];
            let editLoc = this.editLoc;
            this.loc.lat = result.lat;
            this.loc.long = result.long;
            // if editLoc prop has a value use it, otherwise use result
            let address = editLoc.address !== '' ? editLoc.address : result.address,
                address2 = editLoc.address2 !== '' ? editLoc.address2 : result.address2,
                city = editLoc.city !== '' ? editLoc.city : result.city,
                zip = editLoc.zip !== '' ? editLoc.zip : result.zip,
                state = editLoc.state !== '' ? editLoc.state : result.state,
                country = editLoc.country !== '' ? editLoc.country : result.country;
            this.loc.address = address;
            this.loc.address2 = address2;
            this.loc.city = city;
            this.loc.zip = zip;
            this.loc.country = country;
            this.copyLocation(this.loc, this.editLoc)
            this.uiState = "edit";
        },

        // If user clicks on address, assign selected to index
        choiceSelect: function(index) {
            this.selected = index;
        },

        // Just use the address inputted and not result
        // Also used in Edit state
        choiceUseMyAddress: function() {
            this.copyLocation(this.editLoc, this.loc);
            this.uiState = "view";
        },

        // Utility Functions
        //-----------------------------------------

        blankLocation: function() {
            return {
                lat: '',
                long: '',
                address: '',
                address2: '',
                city: '',
                state: '',
                zip: '',
                country: '',
            }
        },
        copyLocation: function(from, to) {
            to.lat = from.lat;
            to.long = from.long;
            to.address = from.address;
            to.address2 = from.address2;
            to.city = from.city;
            to.state = from.state;
            to.zip = from.zip;
            to.country = from.country;
        },
        fullAddress: function(loc = {}) {
            let result = '';
            result += loc.address ? loc.address : '';
            result += loc.address2 ? ', ' + loc.address2 : '';
            result += loc.city ? ', ' + loc.city : '';
            result += loc.state ? ', ' + loc.state : '';
            result += loc.zip ? ', ' + loc.zip : '';
            result += loc.country ? ', ' + loc.country : '';
            // return result after removing any beginning comma
                    return result.replace(/^,/g, '').trim();
                },

                fullAddressHash: function(loc = {}) {
                    let result = '';
                    result = this.fullAddress(loc);
                    result += loc.lat ? ',' + loc.lat : '';
                    result += loc.long ? ',' + loc.long : '';
                    return result.trim();
        }
    },

});
              
            
!
999px

Console